From: Kenneth Kasilag Date: Wed, 3 Jun 2026 00:25:28 +0000 (+0000) Subject: kernel/airoha: restore files for v6.12 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=b0687062bdc8f67fd0a48bb128fd9fca03b7103e;p=thirdparty%2Fopenwrt.git kernel/airoha: restore files for v6.12 This is an automatically generated commit which aids following Kernel patch history, as git will see the move and copy as a rename thus defeating the purpose. For the original discussion see: https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html Signed-off-by: Kenneth Kasilag Link: https://github.com/openwrt/openwrt/pull/21019 Signed-off-by: Jonas Jelonek --- diff --git a/target/linux/airoha/an7581/config-6.12 b/target/linux/airoha/an7581/config-6.12 new file mode 100644 index 00000000000..3acef03fb45 --- /dev/null +++ b/target/linux/airoha/an7581/config-6.12 @@ -0,0 +1,387 @@ +CONFIG_64BIT=y +CONFIG_AIROHA_CPU_PM_DOMAIN=y +CONFIG_AIROHA_SCU_SSR=y +CONFIG_AIROHA_THERMAL=y +CONFIG_AIROHA_WATCHDOG=y +CONFIG_AMPERE_ERRATUM_AC03_CPU_38=y +CONFIG_ARCH_AIROHA=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_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_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_843419=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_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +CONFIG_ARM_AIROHA_SOC_CPUFREQ=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_PMU=y +CONFIG_ARM_PMUV3=y +CONFIG_ARM_PSCI_FW=y +CONFIG_ARM_SMCCC_SOC_ID=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BLOCK_NOTIFIERS=y +CONFIG_BUFFER_HEAD=y +CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y +CONFIG_CC_HAVE_SHADOW_CALL_STACK=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_EN7523=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +# CONFIG_COMPAT_32BIT_TIME is not set +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MITIGATIONS=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +CONFIG_CRC_CCITT=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MISC=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_DTC=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_EXT4_FS=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FRAME_POINTER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FUNCTION_ALIGNMENT=4 +CONFIG_FUNCTION_ALIGNMENT_4B=y +CONFIG_FWNODE_MDIO=y +# CONFIG_FW_LOADER_USER_HELPER is not set +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_MIGRATION=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_GPIO_EN7523=y +CONFIG_GPIO_GENERIC=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HOTPLUG_CORE_SYNC=y +CONFIG_HOTPLUG_CORE_SYNC_DEAD=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_AIROHA=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IO_URING=y +CONFIG_IPV6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_COMMON=y +# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MSI_LIB=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=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_MDIO_AIROHA is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MEDIATEK_GE_SOC_PHY=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_CQHCI=y +CONFIG_MMC_MTK=y +CONFIG_MMU_LAZY_TLB_REFCOUNT=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_MTK_BMT=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_LIMIT=20 +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI_NVMEM=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTK_NET_PHYLIB=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_NET_AIROHA=y +# CONFIG_NET_AIROHA_FLOW_STATS is not set +CONFIG_NET_AIROHA_NPU=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_MT7530=y +# CONFIG_NET_DSA_MT7530_MDIO is not set +CONFIG_NET_DSA_MT7530_MMIO=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_EGRESS=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_INGRESS=y +CONFIG_NET_SELFTESTS=y +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_NET_VENDOR_AIROHA=y +# CONFIG_NET_VENDOR_MEDIATEK is not set +CONFIG_NET_XGRESS=y +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVMEM=y +CONFIG_NVMEM_BLOCK=y +CONFIG_NVMEM_LAYOUTS=y +CONFIG_NVMEM_LAYOUT_ASCII_ENV=y +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 is not set +CONFIG_PCIEASPM_PERFORMANCE=y +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_MEDIATEK=y +CONFIG_PCIE_MEDIATEK_GEN3=y +CONFIG_PCIE_PME=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCS_AIROHA=y +CONFIG_PCS_AIROHA_AN7581=y +# CONFIG_PCS_AIROHA_AN7583 is not set +CONFIG_PERF_EVENTS=y +CONFIG_PER_VMA_LOCK=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLIB_LEDS=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_AIROHA_PCIE=y +CONFIG_PHY_AIROHA_USB=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_AIROHA=y +# CONFIG_PINCTRL_MT2712 is not set +# CONFIG_PINCTRL_MT6765 is not set +# CONFIG_PINCTRL_MT6795 is not set +# CONFIG_PINCTRL_MT6797 is not set +# CONFIG_PINCTRL_MT7622 is not set +# CONFIG_PINCTRL_MT7981 is not set +# CONFIG_PINCTRL_MT7986 is not set +# CONFIG_PINCTRL_MT8173 is not set +# CONFIG_PINCTRL_MT8183 is not set +# CONFIG_PINCTRL_MT8186 is not set +# CONFIG_PINCTRL_MT8188 is not set +# CONFIG_PINCTRL_MT8516 is not set +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_RELOCATABLE=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SERIAL_8250_AIROHA=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=5 +CONFIG_SERIAL_8250_RUNTIME_UARTS=5 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_AN7581=y +CONFIG_SND_SOC_AN7581_WM8960=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOC_BUS=y +CONFIG_SOFTIRQ_ON_OWN_STACK=y +CONFIG_SOUND=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_AIROHA_EN7523 is not set +CONFIG_SPI_AIROHA_SNFI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SPLIT_PMD_PTLOCKS=y +CONFIG_SPLIT_PTE_PTLOCKS=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +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_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TOOLS_SUPPORT_RELR=y +CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UIMAGE_FIT_BLK=y +# CONFIG_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_USB_SUPPORT=y +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_VDSO_GETRANDOM=y +CONFIG_VMAP_STACK=y +CONFIG_WANT_DEV_COREDUMP=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WLAN is not set +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +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/airoha/an7583/config-6.12 b/target/linux/airoha/an7583/config-6.12 new file mode 100644 index 00000000000..392603a41f9 --- /dev/null +++ b/target/linux/airoha/an7583/config-6.12 @@ -0,0 +1,375 @@ +CONFIG_64BIT=y +CONFIG_AIROHA_CPU_PM_DOMAIN=y +CONFIG_AIROHA_SCU_SSR=y +CONFIG_AIROHA_THERMAL=y +CONFIG_AIROHA_WATCHDOG=y +CONFIG_AMPERE_ERRATUM_AC03_CPU_38=y +CONFIG_ARCH_AIROHA=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_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_PROC_KCORE_TEXT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_NO_INSTR=y +CONFIG_ARCH_WANTS_THP_SWAP=y +CONFIG_ARM64=y +CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_ERRATUM_843419=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_TAGGED_ADDR_ABI=y +CONFIG_ARM64_VA_BITS=39 +CONFIG_ARM64_VA_BITS_39=y +# CONFIG_ARM64_VA_BITS_48 is not set +# CONFIG_ARM64_VA_BITS_52 is not set +CONFIG_ARM_AIROHA_SOC_CPUFREQ=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +# CONFIG_ARM_DEBUG_WX is not set +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V2M=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_PMU=y +CONFIG_ARM_PMUV3=y +CONFIG_ARM_PSCI_FW=y +CONFIG_ARM_SMCCC_SOC_ID=y +# CONFIG_ARM_SMMU is not set +# CONFIG_ARM_SMMU_V3 is not set +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BUFFER_HEAD=y +CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y +CONFIG_CC_HAVE_SHADOW_CALL_STACK=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_EN7523=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +# CONFIG_COMPAT_32BIT_TIME is not set +# CONFIG_COMPRESSED_INSTALL is not set +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_RMAP=y +CONFIG_CRC16=y +CONFIG_CRC_CCITT=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DEV_EIP93=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_MISC=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_OPS_HELPERS=y +CONFIG_DTC=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EXT4_FS=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FRAME_POINTER=y +CONFIG_FS_IOMAP=y +CONFIG_FS_MBCACHE=y +CONFIG_FUNCTION_ALIGNMENT=4 +CONFIG_FUNCTION_ALIGNMENT_4B=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_CACHE=y +# CONFIG_FW_LOADER_USER_HELPER is not set +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_GPIO_EN7523=y +CONFIG_GPIO_GENERIC=y +CONFIG_GRO_CELLS=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_IOPORT_MAP=y +# CONFIG_HISILICON_ERRATUM_162100801 is not set +CONFIG_HOTPLUG_CORE_SYNC=y +CONFIG_HOTPLUG_CORE_SYNC_DEAD=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_AIROHA=y +# CONFIG_IDPF is not set +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_IO_URING=y +CONFIG_IPC_NS=y +CONFIG_IPV6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_COMMON=y +# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MSI_LIB=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=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_MDIO_AIROHA=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +# CONFIG_MEDIATEK_GE_SOC_PHY is not set +# CONFIG_MEMCG is not set +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_CQHCI=y +CONFIG_MMC_MTK=y +CONFIG_MMU_LAZY_TLB_REFCOUNT=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_RELA=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_MTK_BMT=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=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_AIROHA=y +# CONFIG_NET_AIROHA_FLOW_STATS is not set +CONFIG_NET_AIROHA_NPU=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_MT7530=y +CONFIG_NET_DSA_MT7530_MDIO=y +CONFIG_NET_DSA_MT7530_MMIO=y +CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_FLOW_LIMIT=y +# CONFIG_NET_MEDIATEK_SOC is not set +CONFIG_NET_SELFTESTS=y +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_NET_VENDOR_AIROHA=y +# CONFIG_NET_VENDOR_MEDIATEK is not set +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVMEM=y +CONFIG_NVMEM_BLOCK=y +CONFIG_NVMEM_LAYOUTS=y +CONFIG_NVMEM_LAYOUT_ASCII_ENV=y +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_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 is not set +CONFIG_PCIEASPM_PERFORMANCE=y +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIE_MEDIATEK=y +CONFIG_PCIE_MEDIATEK_GEN3=y +CONFIG_PCIE_PME=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +# CONFIG_PCS_AIROHA_AN7581 is not set +CONFIG_PCS_AIROHA_AN7583=y +CONFIG_PERF_EVENTS=y +CONFIG_PER_VMA_LOCK=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=y +CONFIG_PHYLIB_LEDS=y +CONFIG_PHYLINK=y +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_PHY_AIROHA_PCIE=y +# CONFIG_PHY_AIROHA_USB is not set +CONFIG_PINCTRL=y +CONFIG_PINCTRL_AIROHA=y +# CONFIG_PINCTRL_MT2712 is not set +# CONFIG_PINCTRL_MT6765 is not set +# CONFIG_PINCTRL_MT6795 is not set +# CONFIG_PINCTRL_MT6797 is not set +# CONFIG_PINCTRL_MT7622 is not set +# CONFIG_PINCTRL_MT7981 is not set +# CONFIG_PINCTRL_MT7986 is not set +# CONFIG_PINCTRL_MT8173 is not set +# CONFIG_PINCTRL_MT8183 is not set +# CONFIG_PINCTRL_MT8186 is not set +# CONFIG_PINCTRL_MT8188 is not set +# CONFIG_PINCTRL_MT8516 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_SYSCON=y +CONFIG_POWER_SUPPLY=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_RELOCATABLE=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RODATA_FULL_DEFAULT_ENABLED=y +CONFIG_RPS=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SERIAL_8250_AIROHA=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=5 +CONFIG_SERIAL_8250_RUNTIME_UARTS=5 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SGL_ALLOC=y +CONFIG_SMP=y +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_AIROHA_EN7523 is not set +CONFIG_SPI_AIROHA_SNFI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=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_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_UNMAP_KERNEL_AT_EL0 is not set +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_VDSO_GETRANDOM=y +CONFIG_VMAP_STACK=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WLAN is not set +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +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/airoha/en7523/config-6.12 b/target/linux/airoha/en7523/config-6.12 new file mode 100644 index 00000000000..825b2cc5362 --- /dev/null +++ b/target/linux/airoha/en7523/config-6.12 @@ -0,0 +1,313 @@ +CONFIG_AIROHA_CPU_PM_DOMAIN=y +CONFIG_AIROHA_WATCHDOG=y +CONFIG_ALIGNMENT_TRAP=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_AIROHA=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MULTIPLATFORM=y +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_STACKWALK=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_V3=y +CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V3_ITS_PCI=y +CONFIG_ARM_HAS_GROUP_RELOCS=y +CONFIG_ARM_HEAVY_MB=y +# CONFIG_ARM_HIGHBANK_CPUIDLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_PATCH_IDIV=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_PSCI=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ARM_SMMU is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_UNWIND=y +CONFIG_ARM_VIRT_EXT=y +CONFIG_ATAGS=y +CONFIG_AUTO_ZRELADDR=y +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_MQ_PCI=y +CONFIG_BLK_PM=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_CACHE_L2X0=y +CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_EN7523=y +CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTEXT_TRACKING=y +CONFIG_CONTEXT_TRACKING_IDLE=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MITIGATIONS=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_SPECTRE=y +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRC16=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_CURRENT_POINTER_IN_TPIDRURO=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_DEBUG_MISC=y +CONFIG_DMA_OPS=y +CONFIG_DTC=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_EXCLUSIVE_SYSTEM_RAM=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FS_IOMAP=y +CONFIG_FUNCTION_ALIGNMENT=0 +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ARCH_TOPOLOGY=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_MIGRATION=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=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_GENERIC_VDSO_32=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_EN7523=y +CONFIG_GPIO_GENERIC=y +# CONFIG_HARDEN_BRANCH_HISTORY is not set +# CONFIG_HARDEN_BRANCH_PREDICTOR is not set +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAVE_SMP=y +CONFIG_HOTPLUG_CORE_SYNC=y +CONFIG_HOTPLUG_CORE_SYNC_DEAD=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_AIROHA=y +CONFIG_HZ_FIXED=0 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_IOMMUFD is not set +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IRQCHIP=y +CONFIG_IRQSTACKS=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_IRQ_WORK=y +# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_MDIO_AIROHA is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_MIGRATION=y +CONFIG_MMU_LAZY_TLB_REFCOUNT=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_NAND_CORE=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_ECC_SW_HAMMING=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_FIT_FW=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_SRCU_NMI_SAFE=y +CONFIG_NET_AIROHA=y +# CONFIG_NET_AIROHA_FLOW_STATS is not set +CONFIG_NET_AIROHA_NPU=y +CONFIG_NET_EGRESS=y +CONFIG_NET_FLOW_LIMIT=y +CONFIG_NET_INGRESS=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_VENDOR_AIROHA=y +# CONFIG_NET_VENDOR_MEDIATEK is not set +CONFIG_NET_XGRESS=y +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=2 +CONFIG_NVMEM=y +CONFIG_NVMEM_LAYOUTS=y +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_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_PADATA=y +CONFIG_PAGE_OFFSET=0xC0000000 +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_PCIEPORTBUS=y +CONFIG_PCIE_MEDIATEK=y +CONFIG_PCIE_PME=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +# CONFIG_PCS_AIROHA_AN7581 is not set +# CONFIG_PCS_AIROHA_AN7583 is not set +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLIB_LEDS=y +CONFIG_PHY_AIROHA_PCIE=y +CONFIG_PINCTRL=y +CONFIG_PM=y +CONFIG_PM_CLK=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_PWM=y +# CONFIG_PWM_AIROHA is not set +CONFIG_PWM_SYSFS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_SCSI=y +CONFIG_SCSI_COMMON=y +CONFIG_SERIAL_8250_AIROHA=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_FSL=y +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_SOFTIRQ_ON_OWN_STACK=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPI=y +CONFIG_SPI_AIROHA_EN7523=y +CONFIG_SPI_AIROHA_SNFI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_STACKTRACE=y +# CONFIG_SWAP is not set +CONFIG_SWPHY=y +CONFIG_SWP_EMULATE=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_UBIFS_FS=y +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNWINDER_ARM=y +CONFIG_USB=y +CONFIG_USB_COMMON=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_XHCI_HCD=y +# CONFIG_USB_XHCI_PLATFORM is not set +CONFIG_USE_OF=y +# CONFIG_VFP is not set +CONFIG_WATCHDOG_CORE=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_XPS=y +CONFIG_XXHASH=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZSTD_COMMON=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/airoha/patches-6.12/016-v6.13-net-airoha-Fix-EGRESS_RATE_METER_EN_MASK-definition.patch b/target/linux/airoha/patches-6.12/016-v6.13-net-airoha-Fix-EGRESS_RATE_METER_EN_MASK-definition.patch new file mode 100644 index 00000000000..d70cadf9d99 --- /dev/null +++ b/target/linux/airoha/patches-6.12/016-v6.13-net-airoha-Fix-EGRESS_RATE_METER_EN_MASK-definition.patch @@ -0,0 +1,33 @@ +From 2518b119639162251b6cc7195aec394930c1d867 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 9 Oct 2024 00:21:47 +0200 +Subject: [PATCH] net: airoha: Fix EGRESS_RATE_METER_EN_MASK definition + +Fix typo in EGRESS_RATE_METER_EN_MASK mask definition. This bus in not +introducing any user visible problem since, even if we are setting +EGRESS_RATE_METER_EN_MASK bit in REG_EGRESS_RATE_METER_CFG register, +egress QoS metering is not supported yet since we are missing some other +hw configurations (e.g token bucket rate, token bucket size). + +Introduced by commit 23020f049327 ("net: airoha: Introduce ethernet support +for EN7581 SoC") + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20241009-airoha-fixes-v2-1-18af63ec19bf@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -554,7 +554,7 @@ + #define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) + + #define REG_EGRESS_RATE_METER_CFG 0x100c +-#define EGRESS_RATE_METER_EN_MASK BIT(29) ++#define EGRESS_RATE_METER_EN_MASK BIT(31) + #define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) + #define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) + #define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) diff --git a/target/linux/airoha/patches-6.12/029-05-v6.19-spi-airoha-remove-unnecessary-restriction-length.patch b/target/linux/airoha/patches-6.12/029-05-v6.19-spi-airoha-remove-unnecessary-restriction-length.patch new file mode 100644 index 00000000000..c99c25921c7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-05-v6.19-spi-airoha-remove-unnecessary-restriction-length.patch @@ -0,0 +1,33 @@ +From 661856ca131c8bf6724905966e02149805660abe Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:16:53 +0300 +Subject: [PATCH 05/14] spi: airoha: remove unnecessary restriction length + +The "length < 160" restriction is not needed because airoha_snand_write_data() +and airoha_snand_read_data() will properly handle data transfers above +SPI_MAX_TRANSFER_SIZE. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251012121707.2296160-3-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 7 ------- + 1 file changed, 7 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -619,13 +619,6 @@ static int airoha_snand_adjust_op_size(s + + if (op->data.nbytes > max_len) + op->data.nbytes = max_len; +- } else { +- max_len = 1 + op->addr.nbytes + op->dummy.nbytes; +- if (max_len >= 160) +- return -EOPNOTSUPP; +- +- if (op->data.nbytes > 160 - max_len) +- op->data.nbytes = 160 - max_len; + } + + return 0; diff --git a/target/linux/airoha/patches-6.12/029-06-v6.19-spi-airoha-remove-unnecessary-switch-to-non-dma-mode.patch b/target/linux/airoha/patches-6.12/029-06-v6.19-spi-airoha-remove-unnecessary-switch-to-non-dma-mode.patch new file mode 100644 index 00000000000..afe496c968f --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-06-v6.19-spi-airoha-remove-unnecessary-switch-to-non-dma-mode.patch @@ -0,0 +1,30 @@ +From 7350f8dc15bfbb7abf1ce4babea6fcace1c574c5 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:16:55 +0300 +Subject: [PATCH 06/14] spi: airoha: remove unnecessary switch to non-dma mode + +The code switches to dma at the start of dirmap operation and returns +to non-dma at the end of dirmap operation, so an additional switch to +non-dma at the start of dirmap write is not required. + +Signed-off-by: Mikhail Kshevetskiy +Acked-by: Lorenzo Bianconi +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251012121707.2296160-5-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -815,9 +815,6 @@ static ssize_t airoha_snand_dirmap_write + int err; + + as_ctrl = spi_controller_get_devdata(spi->controller); +- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); +- if (err < 0) +- return err; + + memcpy(txrx_buf + offs, buf, len); + dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, diff --git a/target/linux/airoha/patches-6.12/029-07-v6.19-spi-airoha-unify-dirmap-read-write-code.patch b/target/linux/airoha/patches-6.12/029-07-v6.19-spi-airoha-unify-dirmap-read-write-code.patch new file mode 100644 index 00000000000..c85c6861fc2 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-07-v6.19-spi-airoha-unify-dirmap-read-write-code.patch @@ -0,0 +1,137 @@ +From 233a22687411ea053a4b169c07324ee6aa33bf38 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:16:58 +0300 +Subject: [PATCH 07/14] spi: airoha: unify dirmap read/write code + +Makes dirmap writing looks similar to dirmap reading. Just a minor +refactoring, no behavior change is expected. + +Signed-off-by: Mikhail Kshevetskiy +Link: https://patch.msgid.link/20251012121707.2296160-8-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 50 ++++++++++++++++++++++------------- + 1 file changed, 32 insertions(+), 18 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -672,6 +672,8 @@ static ssize_t airoha_snand_dirmap_read( + u32 val, rd_mode; + int err; + ++ as_ctrl = spi_controller_get_devdata(spi->controller); ++ + switch (op->cmd.opcode) { + case SPI_NAND_OP_READ_FROM_CACHE_DUAL: + rd_mode = 1; +@@ -684,7 +686,6 @@ static ssize_t airoha_snand_dirmap_read( + break; + } + +- as_ctrl = spi_controller_get_devdata(spi->controller); + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); + if (err < 0) + return err; +@@ -748,7 +749,7 @@ static ssize_t airoha_snand_dirmap_read( + if (err) + goto error_dma_unmap; + +- /* trigger dma start read */ ++ /* trigger dma reading */ + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_RD_TRIG); + if (err) +@@ -806,37 +807,47 @@ error_dma_mode_off: + static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) + { +- struct spi_mem_op *op = &desc->info.op_tmpl; + struct spi_device *spi = desc->mem->spi; + u8 *txrx_buf = spi_get_ctldata(spi); + struct airoha_snand_ctrl *as_ctrl; + dma_addr_t dma_addr; +- u32 wr_mode, val; ++ u32 wr_mode, val, opcode; + int err; + + as_ctrl = spi_controller_get_devdata(spi->controller); + ++ opcode = desc->info.op_tmpl.cmd.opcode; ++ switch (opcode) { ++ case SPI_NAND_OP_PROGRAM_LOAD_SINGLE: ++ case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE: ++ wr_mode = 0; ++ break; ++ case SPI_NAND_OP_PROGRAM_LOAD_QUAD: ++ case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD: ++ wr_mode = 2; ++ break; ++ default: ++ /* unknown opcode */ ++ return -EOPNOTSUPP; ++ } ++ + memcpy(txrx_buf + offs, buf, len); +- dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, +- DMA_TO_DEVICE); +- err = dma_mapping_error(as_ctrl->dev, dma_addr); +- if (err) +- return err; + + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); + if (err < 0) +- goto error_dma_unmap; ++ return err; + + err = airoha_snand_nfi_config(as_ctrl); + if (err) +- goto error_dma_unmap; ++ goto error_dma_mode_off; + +- if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || +- op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) +- wr_mode = BIT(1); +- else +- wr_mode = 0; ++ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, ++ DMA_TO_DEVICE); ++ err = dma_mapping_error(as_ctrl->dev, dma_addr); ++ if (err) ++ goto error_dma_mode_off; + ++ /* set dma addr */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, + dma_addr); + if (err) +@@ -850,12 +861,13 @@ static ssize_t airoha_snand_dirmap_write + if (err) + goto error_dma_unmap; + ++ /* set write command */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, +- FIELD_PREP(SPI_NFI_PG_LOAD_CMD, +- op->cmd.opcode)); ++ FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode)); + if (err) + goto error_dma_unmap; + ++ /* set write mode */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); + if (err) +@@ -887,6 +899,7 @@ static ssize_t airoha_snand_dirmap_write + if (err) + goto error_dma_unmap; + ++ /* trigger dma writing */ + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_WR_TRIG); + if (err) +@@ -931,6 +944,7 @@ static ssize_t airoha_snand_dirmap_write + error_dma_unmap: + dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE, + DMA_TO_DEVICE); ++error_dma_mode_off: + airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + return err; + } diff --git a/target/linux/airoha/patches-6.12/029-08-v6.19-spi-airoha-support-of-dualio-quadio-flash-reading-co.patch b/target/linux/airoha/patches-6.12/029-08-v6.19-spi-airoha-support-of-dualio-quadio-flash-reading-co.patch new file mode 100644 index 00000000000..7e3fcb47647 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-08-v6.19-spi-airoha-support-of-dualio-quadio-flash-reading-co.patch @@ -0,0 +1,94 @@ +From 80b09137aeab27e59004383058f8cc696a9ee048 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:16:59 +0300 +Subject: [PATCH 08/14] spi: airoha: support of dualio/quadio flash reading + commands + +Airoha snfi spi controller supports acceleration of DUAL/QUAD +operations, but does not supports DUAL_IO/QUAD_IO operations. +Luckily DUAL/QUAD operations do the same as DUAL_IO/QUAD_IO ones, +so we can issue corresponding DUAL/QUAD operation instead of +DUAL_IO/QUAD_IO one. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251012121707.2296160-9-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 28 ++++++++++++++++++++++------ + 1 file changed, 22 insertions(+), 6 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -147,6 +147,8 @@ + #define SPI_NFI_CUS_SEC_SIZE_EN BIT(16) + + #define REG_SPI_NFI_RD_CTL2 0x0510 ++#define SPI_NFI_DATA_READ_CMD GENMASK(7, 0) ++ + #define REG_SPI_NFI_RD_CTL3 0x0514 + + #define REG_SPI_NFI_PG_CTL1 0x0524 +@@ -179,7 +181,9 @@ + #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 + #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b + #define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b ++#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO 0xbb + #define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b ++#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO 0xeb + #define SPI_NAND_OP_WRITE_ENABLE 0x06 + #define SPI_NAND_OP_WRITE_DISABLE 0x04 + #define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 +@@ -664,26 +668,38 @@ static int airoha_snand_dirmap_create(st + static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) + { +- struct spi_mem_op *op = &desc->info.op_tmpl; + struct spi_device *spi = desc->mem->spi; + struct airoha_snand_ctrl *as_ctrl; + u8 *txrx_buf = spi_get_ctldata(spi); + dma_addr_t dma_addr; +- u32 val, rd_mode; ++ u32 val, rd_mode, opcode; + int err; + + as_ctrl = spi_controller_get_devdata(spi->controller); + +- switch (op->cmd.opcode) { ++ /* ++ * DUALIO and QUADIO opcodes are not supported by the spi controller, ++ * replace them with supported opcodes. ++ */ ++ opcode = desc->info.op_tmpl.cmd.opcode; ++ switch (opcode) { ++ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE: ++ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST: ++ rd_mode = 0; ++ break; + case SPI_NAND_OP_READ_FROM_CACHE_DUAL: ++ case SPI_NAND_OP_READ_FROM_CACHE_DUALIO: ++ opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL; + rd_mode = 1; + break; + case SPI_NAND_OP_READ_FROM_CACHE_QUAD: ++ case SPI_NAND_OP_READ_FROM_CACHE_QUADIO: ++ opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD; + rd_mode = 2; + break; + default: +- rd_mode = 0; +- break; ++ /* unknown opcode */ ++ return -EOPNOTSUPP; + } + + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); +@@ -717,7 +733,7 @@ static ssize_t airoha_snand_dirmap_read( + + /* set read command */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2, +- op->cmd.opcode); ++ FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode)); + if (err) + goto error_dma_unmap; + diff --git a/target/linux/airoha/patches-6.12/029-09-v6.19-spi-airoha-avoid-setting-of-page-oob-sizes-in-REG_SP.patch b/target/linux/airoha/patches-6.12/029-09-v6.19-spi-airoha-avoid-setting-of-page-oob-sizes-in-REG_SP.patch new file mode 100644 index 00000000000..f8902f7e575 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-09-v6.19-spi-airoha-avoid-setting-of-page-oob-sizes-in-REG_SP.patch @@ -0,0 +1,64 @@ +From 70eec454f2d6cdfab547c262781acd38328e11a1 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:17:00 +0300 +Subject: [PATCH 09/14] spi: airoha: avoid setting of page/oob sizes in + REG_SPI_NFI_PAGEFMT + +spi-airoha-snfi uses custom sector size in REG_SPI_NFI_SECCUS_SIZE +register, so setting of page/oob sizes in REG_SPI_NFI_PAGEFMT is not +required. + +Signed-off-by: Mikhail Kshevetskiy +Link: https://patch.msgid.link/20251012121707.2296160-10-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 38 ----------------------------------- + 1 file changed, 38 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -518,44 +518,6 @@ static int airoha_snand_nfi_config(struc + if (err) + return err; + +- /* page format */ +- switch (as_ctrl->nfi_cfg.spare_size) { +- case 26: +- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1); +- break; +- case 27: +- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2); +- break; +- case 28: +- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3); +- break; +- default: +- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0); +- break; +- } +- +- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, +- SPI_NFI_SPARE_SIZE, val); +- if (err) +- return err; +- +- switch (as_ctrl->nfi_cfg.page_size) { +- case 2048: +- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1); +- break; +- case 4096: +- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2); +- break; +- default: +- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0); +- break; +- } +- +- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, +- SPI_NFI_PAGE_SIZE, val); +- if (err) +- return err; +- + /* sec num */ + val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, diff --git a/target/linux/airoha/patches-6.12/029-10-v6.19-spi-airoha-reduce-the-number-of-modification-of-REG_.patch b/target/linux/airoha/patches-6.12/029-10-v6.19-spi-airoha-reduce-the-number-of-modification-of-REG_.patch new file mode 100644 index 00000000000..40e2f6ea218 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-10-v6.19-spi-airoha-reduce-the-number-of-modification-of-REG_.patch @@ -0,0 +1,199 @@ +From d1ff30df1d9a4eb4c067795abb5e2a66910fd108 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:17:01 +0300 +Subject: [PATCH 10/14] spi: airoha: reduce the number of modification of + REG_SPI_NFI_CNFG and REG_SPI_NFI_SECCUS_SIZE registers + +This just reduce the number of modification of REG_SPI_NFI_CNFG and +REG_SPI_NFI_SECCUS_SIZE registers during dirmap operation. + +This patch is a necessary step to avoid reading flash page settings +from SNFI registers during driver startup. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251012121707.2296160-11-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 135 +++++++++++++++++++++++++--------- + 1 file changed, 102 insertions(+), 33 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -668,7 +668,48 @@ static ssize_t airoha_snand_dirmap_read( + if (err < 0) + return err; + +- err = airoha_snand_nfi_config(as_ctrl); ++ /* NFI reset */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); ++ if (err) ++ goto error_dma_mode_off; ++ ++ /* NFI configure: ++ * - No AutoFDM (custom sector size (SECCUS) register will be used) ++ * - No SoC's hardware ECC (flash internal ECC will be used) ++ * - Use burst mode (faster, but requires 16 byte alignment for addresses) ++ * - Setup for reading (SPI_NFI_READ_MODE) ++ * - Setup reading command: FIELD_PREP(SPI_NFI_OPMODE, 6) ++ * - Use DMA instead of PIO for data reading ++ */ ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_DMA_MODE | ++ SPI_NFI_READ_MODE | ++ SPI_NFI_DMA_BURST_EN | ++ SPI_NFI_HW_ECC_EN | ++ SPI_NFI_AUTO_FDM_EN | ++ SPI_NFI_OPMODE, ++ SPI_NFI_DMA_MODE | ++ SPI_NFI_READ_MODE | ++ SPI_NFI_DMA_BURST_EN | ++ FIELD_PREP(SPI_NFI_OPMODE, 6)); ++ if (err) ++ goto error_dma_mode_off; ++ ++ /* Set number of sector will be read */ ++ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_SEC_NUM, val); ++ if (err) ++ goto error_dma_mode_off; ++ ++ /* Set custom sector size */ ++ val = as_ctrl->nfi_cfg.sec_size; ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, ++ SPI_NFI_CUS_SEC_SIZE | ++ SPI_NFI_CUS_SEC_SIZE_EN, ++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) | ++ SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + goto error_dma_mode_off; + +@@ -684,7 +725,14 @@ static ssize_t airoha_snand_dirmap_read( + if (err) + goto error_dma_unmap; + +- /* set cust sec size */ ++ /* ++ * Setup transfer length ++ * --------------------- ++ * The following rule MUST be met: ++ * transfer_length = ++ * = NFI_SNF_MISC_CTL2.read_data_byte_number = ++ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size ++ */ + val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; + val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val); + err = regmap_update_bits(as_ctrl->regmap_nfi, +@@ -711,18 +759,6 @@ static ssize_t airoha_snand_dirmap_read( + if (err) + goto error_dma_unmap; + +- /* set nfi read */ +- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_OPMODE, +- FIELD_PREP(SPI_NFI_OPMODE, 6)); +- if (err) +- goto error_dma_unmap; +- +- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE); +- if (err) +- goto error_dma_unmap; +- + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0); + if (err) + goto error_dma_unmap; +@@ -815,7 +851,48 @@ static ssize_t airoha_snand_dirmap_write + if (err < 0) + return err; + +- err = airoha_snand_nfi_config(as_ctrl); ++ /* NFI reset */ ++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); ++ if (err) ++ goto error_dma_mode_off; ++ ++ /* ++ * NFI configure: ++ * - No AutoFDM (custom sector size (SECCUS) register will be used) ++ * - No SoC's hardware ECC (flash internal ECC will be used) ++ * - Use burst mode (faster, but requires 16 byte alignment for addresses) ++ * - Setup for writing (SPI_NFI_READ_MODE bit is cleared) ++ * - Setup writing command: FIELD_PREP(SPI_NFI_OPMODE, 3) ++ * - Use DMA instead of PIO for data writing ++ */ ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, ++ SPI_NFI_DMA_MODE | ++ SPI_NFI_READ_MODE | ++ SPI_NFI_DMA_BURST_EN | ++ SPI_NFI_HW_ECC_EN | ++ SPI_NFI_AUTO_FDM_EN | ++ SPI_NFI_OPMODE, ++ SPI_NFI_DMA_MODE | ++ SPI_NFI_DMA_BURST_EN | ++ FIELD_PREP(SPI_NFI_OPMODE, 3)); ++ if (err) ++ goto error_dma_mode_off; ++ ++ /* Set number of sector will be written */ ++ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, ++ SPI_NFI_SEC_NUM, val); ++ if (err) ++ goto error_dma_mode_off; ++ ++ /* Set custom sector size */ ++ val = as_ctrl->nfi_cfg.sec_size; ++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, ++ SPI_NFI_CUS_SEC_SIZE | ++ SPI_NFI_CUS_SEC_SIZE_EN, ++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) | ++ SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + goto error_dma_mode_off; + +@@ -831,8 +908,16 @@ static ssize_t airoha_snand_dirmap_write + if (err) + goto error_dma_unmap; + +- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, +- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); ++ /* ++ * Setup transfer length ++ * --------------------- ++ * The following rule MUST be met: ++ * transfer_length = ++ * = NFI_SNF_MISC_CTL2.write_data_byte_number = ++ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size ++ */ ++ val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; ++ val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val); + err = regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_MISC_CTL2, + SPI_NFI_PROG_LOAD_BYTE_NUM, val); +@@ -857,22 +942,6 @@ static ssize_t airoha_snand_dirmap_write + if (err) + goto error_dma_unmap; + +- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_READ_MODE); +- if (err) +- goto error_dma_unmap; +- +- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_OPMODE, +- FIELD_PREP(SPI_NFI_OPMODE, 3)); +- if (err) +- goto error_dma_unmap; +- +- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_DMA_MODE); +- if (err) +- goto error_dma_unmap; +- + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80); + if (err) + goto error_dma_unmap; diff --git a/target/linux/airoha/patches-6.12/029-11-v6.19-spi-airoha-set-custom-sector-size-equal-to-flash-pag.patch b/target/linux/airoha/patches-6.12/029-11-v6.19-spi-airoha-set-custom-sector-size-equal-to-flash-pag.patch new file mode 100644 index 00000000000..514212835ce --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-11-v6.19-spi-airoha-set-custom-sector-size-equal-to-flash-pag.patch @@ -0,0 +1,142 @@ +From fb81b5cecb8553e3ca2b45288cf340d43c9c2991 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:17:02 +0300 +Subject: [PATCH 11/14] spi: airoha: set custom sector size equal to flash page + size + +Set custom sector size equal to flash page size including oob. Thus we +will always read a single sector. The maximum custom sector size is +8187, so all possible flash sector sizes are supported. + +This patch is a necessary step to avoid reading flash page settings +from SNFI registers during driver startup. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251012121707.2296160-12-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 35 +++++++++++++++++++---------------- + 1 file changed, 19 insertions(+), 16 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -519,7 +519,7 @@ static int airoha_snand_nfi_config(struc + return err; + + /* sec num */ +- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); ++ val = FIELD_PREP(SPI_NFI_SEC_NUM, 1); + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_SEC_NUM, val); + if (err) +@@ -532,7 +532,8 @@ static int airoha_snand_nfi_config(struc + return err; + + /* set cust sec size */ +- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size); ++ val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, ++ as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); + return regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE, val); +@@ -635,10 +636,13 @@ static ssize_t airoha_snand_dirmap_read( + u8 *txrx_buf = spi_get_ctldata(spi); + dma_addr_t dma_addr; + u32 val, rd_mode, opcode; ++ size_t bytes; + int err; + + as_ctrl = spi_controller_get_devdata(spi->controller); + ++ bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size; ++ + /* + * DUALIO and QUADIO opcodes are not supported by the spi controller, + * replace them with supported opcodes. +@@ -697,18 +701,17 @@ static ssize_t airoha_snand_dirmap_read( + goto error_dma_mode_off; + + /* Set number of sector will be read */ +- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, +- SPI_NFI_SEC_NUM, val); ++ SPI_NFI_SEC_NUM, ++ FIELD_PREP(SPI_NFI_SEC_NUM, 1)); + if (err) + goto error_dma_mode_off; + + /* Set custom sector size */ +- val = as_ctrl->nfi_cfg.sec_size; + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE | + SPI_NFI_CUS_SEC_SIZE_EN, +- FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) | ++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) | + SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + goto error_dma_mode_off; +@@ -733,11 +736,10 @@ static ssize_t airoha_snand_dirmap_read( + * = NFI_SNF_MISC_CTL2.read_data_byte_number = + * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size + */ +- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; +- val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val); + err = regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_MISC_CTL2, +- SPI_NFI_READ_DATA_BYTE_NUM, val); ++ SPI_NFI_READ_DATA_BYTE_NUM, ++ FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, bytes)); + if (err) + goto error_dma_unmap; + +@@ -826,10 +828,13 @@ static ssize_t airoha_snand_dirmap_write + struct airoha_snand_ctrl *as_ctrl; + dma_addr_t dma_addr; + u32 wr_mode, val, opcode; ++ size_t bytes; + int err; + + as_ctrl = spi_controller_get_devdata(spi->controller); + ++ bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size; ++ + opcode = desc->info.op_tmpl.cmd.opcode; + switch (opcode) { + case SPI_NAND_OP_PROGRAM_LOAD_SINGLE: +@@ -880,18 +885,17 @@ static ssize_t airoha_snand_dirmap_write + goto error_dma_mode_off; + + /* Set number of sector will be written */ +- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, +- SPI_NFI_SEC_NUM, val); ++ SPI_NFI_SEC_NUM, ++ FIELD_PREP(SPI_NFI_SEC_NUM, 1)); + if (err) + goto error_dma_mode_off; + + /* Set custom sector size */ +- val = as_ctrl->nfi_cfg.sec_size; + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE | + SPI_NFI_CUS_SEC_SIZE_EN, +- FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) | ++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) | + SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + goto error_dma_mode_off; +@@ -916,11 +920,10 @@ static ssize_t airoha_snand_dirmap_write + * = NFI_SNF_MISC_CTL2.write_data_byte_number = + * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size + */ +- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; +- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val); + err = regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_MISC_CTL2, +- SPI_NFI_PROG_LOAD_BYTE_NUM, val); ++ SPI_NFI_PROG_LOAD_BYTE_NUM, ++ FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, bytes)); + if (err) + goto error_dma_unmap; + diff --git a/target/linux/airoha/patches-6.12/029-12-v6.19-spi-airoha-avoid-reading-flash-page-settings-from-SN.patch b/target/linux/airoha/patches-6.12/029-12-v6.19-spi-airoha-avoid-reading-flash-page-settings-from-SN.patch new file mode 100644 index 00000000000..00fd100bc36 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-12-v6.19-spi-airoha-avoid-reading-flash-page-settings-from-SN.patch @@ -0,0 +1,206 @@ +From 902c0ea18a97b1a6eeee5799cb1fd9a79ef9208e Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:17:03 +0300 +Subject: [PATCH 12/14] spi: airoha: avoid reading flash page settings from + SNFI registers during driver startup + +The spinand driver do 3 type of dirmap requests: + * read/write whole flash page without oob + (offs = 0, len = page_size) + * read/write whole flash page including oob + (offs = 0, len = page_size + oob_size) + * read/write oob area only + (offs = page_size, len = oob_size) + +The trick is: + * read/write a single "sector" + * set a custom sector size equal to offs + len. It's a bit safer to + rounded up "sector size" value 64. + * set the transfer length equal to custom sector size + +And it works! + +Thus we can remove a dirty hack that reads flash page settings from +SNFI registers during driver startup. Also airoha_snand_adjust_op_size() +function becomes unnecessary. + +Signed-off-by: Mikhail Kshevetskiy +Link: https://patch.msgid.link/20251012121707.2296160-13-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 115 ++-------------------------------- + 1 file changed, 5 insertions(+), 110 deletions(-) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -223,13 +223,6 @@ struct airoha_snand_ctrl { + struct regmap *regmap_ctrl; + struct regmap *regmap_nfi; + struct clk *spi_clk; +- +- struct { +- size_t page_size; +- size_t sec_size; +- u8 sec_num; +- u8 spare_size; +- } nfi_cfg; + }; + + static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl, +@@ -490,55 +483,6 @@ static int airoha_snand_nfi_init(struct + SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN); + } + +-static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl) +-{ +- int err; +- u32 val; +- +- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, +- SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); +- if (err) +- return err; +- +- /* auto FDM */ +- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_AUTO_FDM_EN); +- if (err) +- return err; +- +- /* HW ECC */ +- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_HW_ECC_EN); +- if (err) +- return err; +- +- /* DMA Burst */ +- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, +- SPI_NFI_DMA_BURST_EN); +- if (err) +- return err; +- +- /* sec num */ +- val = FIELD_PREP(SPI_NFI_SEC_NUM, 1); +- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, +- SPI_NFI_SEC_NUM, val); +- if (err) +- return err; +- +- /* enable cust sec size */ +- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, +- SPI_NFI_CUS_SEC_SIZE_EN); +- if (err) +- return err; +- +- /* set cust sec size */ +- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, +- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); +- return regmap_update_bits(as_ctrl->regmap_nfi, +- REG_SPI_NFI_SECCUS_SIZE, +- SPI_NFI_CUS_SEC_SIZE, val); +-} +- + static bool airoha_snand_is_page_ops(const struct spi_mem_op *op) + { + if (op->addr.nbytes != 2) +@@ -571,26 +515,6 @@ static bool airoha_snand_is_page_ops(con + } + } + +-static int airoha_snand_adjust_op_size(struct spi_mem *mem, +- struct spi_mem_op *op) +-{ +- size_t max_len; +- +- if (airoha_snand_is_page_ops(op)) { +- struct airoha_snand_ctrl *as_ctrl; +- +- as_ctrl = spi_controller_get_devdata(mem->spi->controller); +- max_len = as_ctrl->nfi_cfg.sec_size; +- max_len += as_ctrl->nfi_cfg.spare_size; +- max_len *= as_ctrl->nfi_cfg.sec_num; +- +- if (op->data.nbytes > max_len) +- op->data.nbytes = max_len; +- } +- +- return 0; +-} +- + static bool airoha_snand_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) + { +@@ -641,7 +565,8 @@ static ssize_t airoha_snand_dirmap_read( + + as_ctrl = spi_controller_get_devdata(spi->controller); + +- bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size; ++ /* minimum oob size is 64 */ ++ bytes = round_up(offs + len, 64); + + /* + * DUALIO and QUADIO opcodes are not supported by the spi controller, +@@ -833,7 +758,8 @@ static ssize_t airoha_snand_dirmap_write + + as_ctrl = spi_controller_get_devdata(spi->controller); + +- bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size; ++ /* minimum oob size is 64 */ ++ bytes = round_up(offs + len, 64); + + opcode = desc->info.op_tmpl.cmd.opcode; + switch (opcode) { +@@ -1076,7 +1002,6 @@ static int airoha_snand_exec_op(struct s + } + + static const struct spi_controller_mem_ops airoha_snand_mem_ops = { +- .adjust_op_size = airoha_snand_adjust_op_size, + .supports_op = airoha_snand_supports_op, + .exec_op = airoha_snand_exec_op, + .dirmap_create = airoha_snand_dirmap_create, +@@ -1106,36 +1031,6 @@ static int airoha_snand_setup(struct spi + return 0; + } + +-static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) +-{ +- u32 val, sec_size, sec_num; +- int err; +- +- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val); +- if (err) +- return err; +- +- sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val); +- +- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val); +- if (err) +- return err; +- +- sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val); +- +- /* init default value */ +- as_ctrl->nfi_cfg.sec_size = sec_size; +- as_ctrl->nfi_cfg.sec_num = sec_num; +- as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024); +- as_ctrl->nfi_cfg.spare_size = 16; +- +- err = airoha_snand_nfi_init(as_ctrl); +- if (err) +- return err; +- +- return airoha_snand_nfi_config(as_ctrl); +-} +- + static const struct regmap_config spi_ctrl_regmap_config = { + .name = "ctrl", + .reg_bits = 32, +@@ -1227,7 +1122,7 @@ static int airoha_snand_probe(struct pla + ctrl->setup = airoha_snand_setup; + device_set_node(&ctrl->dev, dev_fwnode(dev)); + +- err = airoha_snand_nfi_setup(as_ctrl); ++ err = airoha_snand_nfi_init(as_ctrl); + if (err) + return err; + diff --git a/target/linux/airoha/patches-6.12/029-13-v6.19-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch b/target/linux/airoha/patches-6.12/029-13-v6.19-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch new file mode 100644 index 00000000000..669f9b2dd43 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-13-v6.19-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch @@ -0,0 +1,32 @@ +From 0743acf746a81e0460a56fd5ff847d97fa7eb370 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sun, 12 Oct 2025 15:17:04 +0300 +Subject: [PATCH 13/14] spi: airoha: buffer must be 0xff-ed before writing + +During writing, the entire flash page (including OOB) will be updated +with the values from the temporary buffer, so we need to fill the +untouched areas of the buffer with 0xff value to prevent accidental +data overwriting. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251012121707.2296160-14-mikhail.kshevetskiy@iopsys.eu +Signed-off-by: Mark Brown +--- + drivers/spi/spi-airoha-snfi.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/spi/spi-airoha-snfi.c ++++ b/drivers/spi/spi-airoha-snfi.c +@@ -776,7 +776,11 @@ static ssize_t airoha_snand_dirmap_write + return -EOPNOTSUPP; + } + ++ if (offs > 0) ++ memset(txrx_buf, 0xff, offs); + memcpy(txrx_buf + offs, buf, len); ++ if (bytes > offs + len) ++ memset(txrx_buf + offs + len, 0xff, bytes - offs - len); + + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); + if (err < 0) diff --git a/target/linux/airoha/patches-6.12/029-15-arm-dts-airoha-en7523-add-SNAND-node.patch b/target/linux/airoha/patches-6.12/029-15-arm-dts-airoha-en7523-add-SNAND-node.patch new file mode 100644 index 00000000000..bd68440b309 --- /dev/null +++ b/target/linux/airoha/patches-6.12/029-15-arm-dts-airoha-en7523-add-SNAND-node.patch @@ -0,0 +1,40 @@ +From bb2f9b3d71717c7df942deb1488c56e544d4a32c Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Fri, 10 Oct 2025 06:01:33 +0300 +Subject: [PATCH v6 3/3] arm: dts: airoha: en7523: add SNAND node + +Add SNAND node to enable support of attached SPI-NAND on the EN7523 SoC. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +--- + arch/arm/boot/dts/airoha/en7523.dtsi | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/arch/arm/boot/dts/airoha/en7523.dtsi ++++ b/arch/arm/boot/dts/airoha/en7523.dtsi +@@ -203,4 +203,24 @@ + #interrupt-cells = <1>; + }; + }; ++ ++ spi_ctrl: spi@1fa10000 { ++ compatible = "airoha,en7523-snand", "airoha,en7581-snand"; ++ reg = <0x1fa10000 0x140>, ++ <0x1fa11000 0x160>; ++ ++ clocks = <&scu EN7523_CLK_SPI>; ++ clock-names = "spi"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ nand: nand@0 { ++ compatible = "spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ spi-tx-bus-width = <1>; ++ spi-rx-bus-width = <2>; ++ }; ++ }; + }; diff --git a/target/linux/airoha/patches-6.12/030-v6.13-hwrng-airoha-add-support-for-Airoha-EN7581-TRNG.patch b/target/linux/airoha/patches-6.12/030-v6.13-hwrng-airoha-add-support-for-Airoha-EN7581-TRNG.patch new file mode 100644 index 00000000000..e21fb5649e2 --- /dev/null +++ b/target/linux/airoha/patches-6.12/030-v6.13-hwrng-airoha-add-support-for-Airoha-EN7581-TRNG.patch @@ -0,0 +1,306 @@ +From 5c5db81bff81a0fcd9ad998543d4241cbfe4742f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 17 Oct 2024 14:44:38 +0200 +Subject: [PATCH 2/2] hwrng: airoha - add support for Airoha EN7581 TRNG + +Add support for Airoha TRNG. The Airoha SoC provide a True RNG module +that can output 4 bytes of raw data at times. + +The module makes use of various noise source to provide True Random +Number Generation. + +On probe the module is reset to operate Health Test and verify correct +execution of it. + +The module can also provide DRBG function but the execution mode is +mutually exclusive, running as TRNG doesn't permit to also run it as +DRBG. + +Signed-off-by: Christian Marangi +Reviewed-by: Martin Kaiser +Signed-off-by: Herbert Xu +--- + drivers/char/hw_random/Kconfig | 13 ++ + drivers/char/hw_random/Makefile | 1 + + drivers/char/hw_random/airoha-trng.c | 243 +++++++++++++++++++++++++++ + 3 files changed, 257 insertions(+) + create mode 100644 drivers/char/hw_random/airoha-trng.c + +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -62,6 +62,19 @@ config HW_RANDOM_AMD + + If unsure, say Y. + ++config HW_RANDOM_AIROHA ++ tristate "Airoha True HW Random Number Generator support" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ default HW_RANDOM ++ help ++ This driver provides kernel-side support for the True Random Number ++ Generator hardware found on Airoha SoC. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called airoha-rng. ++ ++ If unsure, say Y. ++ + config HW_RANDOM_ATMEL + tristate "Atmel Random Number Generator support" + depends on (ARCH_AT91 || COMPILE_TEST) +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -8,6 +8,7 @@ rng-core-y := core.o + obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o + obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o + obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o ++obj-$(CONFIG_HW_RANDOM_AIROHA) += airoha-trng.o + obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o + obj-$(CONFIG_HW_RANDOM_BA431) += ba431-rng.o + obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o +--- /dev/null ++++ b/drivers/char/hw_random/airoha-trng.c +@@ -0,0 +1,243 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2024 Christian Marangi */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TRNG_IP_RDY 0x800 ++#define CNT_TRANS GENMASK(15, 8) ++#define SAMPLE_RDY BIT(0) ++#define TRNG_NS_SEK_AND_DAT_EN 0x804 ++#define RNG_EN BIT(31) /* referenced as ring_en */ ++#define RAW_DATA_EN BIT(16) ++#define TRNG_HEALTH_TEST_SW_RST 0x808 ++#define SW_RST BIT(0) /* Active High */ ++#define TRNG_INTR_EN 0x818 ++#define INTR_MASK BIT(16) ++#define CONTINUOUS_HEALTH_INITR_EN BIT(2) ++#define SW_STARTUP_INITR_EN BIT(1) ++#define RST_STARTUP_INITR_EN BIT(0) ++/* Notice that Health Test are done only out of Reset and with RNG_EN */ ++#define TRNG_HEALTH_TEST_STATUS 0x824 ++#define CONTINUOUS_HEALTH_AP_TEST_FAIL BIT(23) ++#define CONTINUOUS_HEALTH_RC_TEST_FAIL BIT(22) ++#define SW_STARTUP_TEST_DONE BIT(21) ++#define SW_STARTUP_AP_TEST_FAIL BIT(20) ++#define SW_STARTUP_RC_TEST_FAIL BIT(19) ++#define RST_STARTUP_TEST_DONE BIT(18) ++#define RST_STARTUP_AP_TEST_FAIL BIT(17) ++#define RST_STARTUP_RC_TEST_FAIL BIT(16) ++#define RAW_DATA_VALID BIT(7) ++ ++#define TRNG_RAW_DATA_OUT 0x828 ++ ++#define TRNG_CNT_TRANS_VALID 0x80 ++#define BUSY_LOOP_SLEEP 10 ++#define BUSY_LOOP_TIMEOUT (BUSY_LOOP_SLEEP * 10000) ++ ++struct airoha_trng { ++ void __iomem *base; ++ struct hwrng rng; ++ struct device *dev; ++ ++ struct completion rng_op_done; ++}; ++ ++static int airoha_trng_irq_mask(struct airoha_trng *trng) ++{ ++ u32 val; ++ ++ val = readl(trng->base + TRNG_INTR_EN); ++ val |= INTR_MASK; ++ writel(val, trng->base + TRNG_INTR_EN); ++ ++ return 0; ++} ++ ++static int airoha_trng_irq_unmask(struct airoha_trng *trng) ++{ ++ u32 val; ++ ++ val = readl(trng->base + TRNG_INTR_EN); ++ val &= ~INTR_MASK; ++ writel(val, trng->base + TRNG_INTR_EN); ++ ++ return 0; ++} ++ ++static int airoha_trng_init(struct hwrng *rng) ++{ ++ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); ++ int ret; ++ u32 val; ++ ++ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ val |= RNG_EN; ++ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ ++ /* Set out of SW Reset */ ++ airoha_trng_irq_unmask(trng); ++ writel(0, trng->base + TRNG_HEALTH_TEST_SW_RST); ++ ++ ret = wait_for_completion_timeout(&trng->rng_op_done, BUSY_LOOP_TIMEOUT); ++ if (ret <= 0) { ++ dev_err(trng->dev, "Timeout waiting for Health Check\n"); ++ airoha_trng_irq_mask(trng); ++ return -ENODEV; ++ } ++ ++ /* Check if Health Test Failed */ ++ val = readl(trng->base + TRNG_HEALTH_TEST_STATUS); ++ if (val & (RST_STARTUP_AP_TEST_FAIL | RST_STARTUP_RC_TEST_FAIL)) { ++ dev_err(trng->dev, "Health Check fail: %s test fail\n", ++ val & RST_STARTUP_AP_TEST_FAIL ? "AP" : "RC"); ++ return -ENODEV; ++ } ++ ++ /* Check if IP is ready */ ++ ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val, ++ val & SAMPLE_RDY, 10, 1000); ++ if (ret < 0) { ++ dev_err(trng->dev, "Timeout waiting for IP ready"); ++ return -ENODEV; ++ } ++ ++ /* CNT_TRANS must be 0x80 for IP to be considered ready */ ++ ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val, ++ FIELD_GET(CNT_TRANS, val) == TRNG_CNT_TRANS_VALID, ++ 10, 1000); ++ if (ret < 0) { ++ dev_err(trng->dev, "Timeout waiting for IP ready"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void airoha_trng_cleanup(struct hwrng *rng) ++{ ++ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); ++ u32 val; ++ ++ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ val &= ~RNG_EN; ++ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ ++ /* Put it in SW Reset */ ++ writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST); ++} ++ ++static int airoha_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) ++{ ++ struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); ++ u32 *data = buf; ++ u32 status; ++ int ret; ++ ++ ret = readl_poll_timeout(trng->base + TRNG_HEALTH_TEST_STATUS, status, ++ status & RAW_DATA_VALID, 10, 1000); ++ if (ret < 0) { ++ dev_err(trng->dev, "Timeout waiting for TRNG RAW Data valid\n"); ++ return ret; ++ } ++ ++ *data = readl(trng->base + TRNG_RAW_DATA_OUT); ++ ++ return 4; ++} ++ ++static irqreturn_t airoha_trng_irq(int irq, void *priv) ++{ ++ struct airoha_trng *trng = (struct airoha_trng *)priv; ++ ++ airoha_trng_irq_mask(trng); ++ /* Just complete the task, we will read the value later */ ++ complete(&trng->rng_op_done); ++ ++ return IRQ_HANDLED; ++} ++ ++static int airoha_trng_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_trng *trng; ++ int irq, ret; ++ u32 val; ++ ++ trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL); ++ if (!trng) ++ return -ENOMEM; ++ ++ trng->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(trng->base)) ++ return PTR_ERR(trng->base); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ airoha_trng_irq_mask(trng); ++ ret = devm_request_irq(&pdev->dev, irq, airoha_trng_irq, 0, ++ pdev->name, (void *)trng); ++ if (ret) { ++ dev_err(dev, "Can't get interrupt working.\n"); ++ return ret; ++ } ++ ++ init_completion(&trng->rng_op_done); ++ ++ /* Enable interrupt for SW reset Health Check */ ++ val = readl(trng->base + TRNG_INTR_EN); ++ val |= RST_STARTUP_INITR_EN; ++ writel(val, trng->base + TRNG_INTR_EN); ++ ++ /* Set output to raw data */ ++ val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ val |= RAW_DATA_EN; ++ writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); ++ ++ /* Put it in SW Reset */ ++ writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST); ++ ++ trng->dev = dev; ++ trng->rng.name = pdev->name; ++ trng->rng.init = airoha_trng_init; ++ trng->rng.cleanup = airoha_trng_cleanup; ++ trng->rng.read = airoha_trng_read; ++ ++ ret = devm_hwrng_register(dev, &trng->rng); ++ if (ret) { ++ dev_err(dev, "failed to register rng device: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_trng_of_match[] = { ++ { .compatible = "airoha,en7581-trng", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, airoha_trng_of_match); ++ ++static struct platform_driver airoha_trng_driver = { ++ .driver = { ++ .name = "airoha-trng", ++ .of_match_table = airoha_trng_of_match, ++ }, ++ .probe = airoha_trng_probe, ++}; ++ ++module_platform_driver(airoha_trng_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha True Random Number Generator driver"); diff --git a/target/linux/airoha/patches-6.12/031-02-v6.13-net-airoha-Simplify-Tx-napi-logic.patch b/target/linux/airoha/patches-6.12/031-02-v6.13-net-airoha-Simplify-Tx-napi-logic.patch new file mode 100644 index 00000000000..1e907c273d5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/031-02-v6.13-net-airoha-Simplify-Tx-napi-logic.patch @@ -0,0 +1,130 @@ +From 0c729f53b8c33b9e5eadc2d5e673759e3510501e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 29 Oct 2024 13:17:10 +0100 +Subject: [PATCH 2/2] net: airoha: Simplify Tx napi logic + +Simplify Tx napi logic relying just on the packet index provided by +completion queue indicating the completed packet that can be removed +from the Tx DMA ring. +This is a preliminary patch to add Qdisc offload for airoha_eth driver. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20241029-airoha-en7581-tx-napi-work-v1-2-96ad1686b946@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 73 ++++++++++++---------- + 1 file changed, 41 insertions(+), 32 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1690,8 +1690,12 @@ static int airoha_qdma_tx_napi_poll(stru + irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); + + while (irq_queued > 0 && done < budget) { +- u32 qid, last, val = irq_q->q[head]; ++ u32 qid, val = irq_q->q[head]; ++ struct airoha_qdma_desc *desc; ++ struct airoha_queue_entry *e; + struct airoha_queue *q; ++ u32 index, desc_ctrl; ++ struct sk_buff *skb; + + if (val == 0xff) + break; +@@ -1701,9 +1705,7 @@ static int airoha_qdma_tx_napi_poll(stru + irq_queued--; + done++; + +- last = FIELD_GET(IRQ_DESC_IDX_MASK, val); + qid = FIELD_GET(IRQ_RING_IDX_MASK, val); +- + if (qid >= ARRAY_SIZE(qdma->q_tx)) + continue; + +@@ -1711,46 +1713,53 @@ static int airoha_qdma_tx_napi_poll(stru + if (!q->ndesc) + continue; + ++ index = FIELD_GET(IRQ_DESC_IDX_MASK, val); ++ if (index >= q->ndesc) ++ continue; ++ + spin_lock_bh(&q->lock); + +- while (q->queued > 0) { +- struct airoha_qdma_desc *desc = &q->desc[q->tail]; +- struct airoha_queue_entry *e = &q->entry[q->tail]; +- u32 desc_ctrl = le32_to_cpu(desc->ctrl); +- struct sk_buff *skb = e->skb; +- u16 index = q->tail; +- +- if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && +- !(desc_ctrl & QDMA_DESC_DROP_MASK)) +- break; ++ if (!q->queued) ++ goto unlock; + +- q->tail = (q->tail + 1) % q->ndesc; +- q->queued--; ++ desc = &q->desc[index]; ++ desc_ctrl = le32_to_cpu(desc->ctrl); + +- dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, +- DMA_TO_DEVICE); +- +- WRITE_ONCE(desc->msg0, 0); +- WRITE_ONCE(desc->msg1, 0); ++ if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && ++ !(desc_ctrl & QDMA_DESC_DROP_MASK)) ++ goto unlock; ++ ++ e = &q->entry[index]; ++ skb = e->skb; ++ ++ dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, ++ DMA_TO_DEVICE); ++ memset(e, 0, sizeof(*e)); ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ q->queued--; ++ ++ /* completion ring can report out-of-order indexes if hw QoS ++ * is enabled and packets with different priority are queued ++ * to same DMA ring. Take into account possible out-of-order ++ * reports incrementing DMA ring tail pointer ++ */ ++ while (q->tail != q->head && !q->entry[q->tail].dma_addr) ++ q->tail = (q->tail + 1) % q->ndesc; + +- if (skb) { +- u16 queue = skb_get_queue_mapping(skb); +- struct netdev_queue *txq; +- +- txq = netdev_get_tx_queue(skb->dev, queue); +- netdev_tx_completed_queue(txq, 1, skb->len); +- if (netif_tx_queue_stopped(txq) && +- q->ndesc - q->queued >= q->free_thr) +- netif_tx_wake_queue(txq); +- +- dev_kfree_skb_any(skb); +- e->skb = NULL; +- } ++ if (skb) { ++ u16 queue = skb_get_queue_mapping(skb); ++ struct netdev_queue *txq; ++ ++ txq = netdev_get_tx_queue(skb->dev, queue); ++ netdev_tx_completed_queue(txq, 1, skb->len); ++ if (netif_tx_queue_stopped(txq) && ++ q->ndesc - q->queued >= q->free_thr) ++ netif_tx_wake_queue(txq); + +- if (index == last) +- break; ++ dev_kfree_skb_any(skb); + } +- ++unlock: + spin_unlock_bh(&q->lock); + } + diff --git a/target/linux/airoha/patches-6.12/032-v6.13-watchdog-Add-support-for-Airoha-EN7851-watchdog.patch b/target/linux/airoha/patches-6.12/032-v6.13-watchdog-Add-support-for-Airoha-EN7851-watchdog.patch new file mode 100644 index 00000000000..ac65bec2a14 --- /dev/null +++ b/target/linux/airoha/patches-6.12/032-v6.13-watchdog-Add-support-for-Airoha-EN7851-watchdog.patch @@ -0,0 +1,267 @@ +From 3cf67f3769b8227ca75ca7102180a2e270ee01aa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 11 Oct 2024 12:43:53 +0200 +Subject: [PATCH] watchdog: Add support for Airoha EN7851 watchdog + +Add support for Airoha EN7851 watchdog. This is a very basic watchdog +with no pretimeout support, max timeout is 28 seconds and it ticks based +on half the SoC BUS clock. + +Signed-off-by: Christian Marangi +Reviewed-by: Guenter Roeck +Link: https://lore.kernel.org/r/20241011104411.28659-2-ansuelsmth@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + drivers/watchdog/Kconfig | 8 ++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/airoha_wdt.c | 216 ++++++++++++++++++++++++++++++++++ + 3 files changed, 225 insertions(+) + create mode 100644 drivers/watchdog/airoha_wdt.c + +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -408,6 +408,14 @@ config SL28CPLD_WATCHDOG + + # ARM Architecture + ++config AIROHA_WATCHDOG ++ tristate "Airoha EN7581 Watchdog" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ select WATCHDOG_CORE ++ help ++ Watchdog timer embedded into Airoha SoC. This will reboot your ++ system when the timeout is reached. ++ + config ARM_SP805_WATCHDOG + tristate "ARM SP805 Watchdog" + depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb. + obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o + obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o + obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o ++obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o + obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o + obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o + obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o +--- /dev/null ++++ b/drivers/watchdog/airoha_wdt.c +@@ -0,0 +1,216 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Airoha Watchdog Driver ++ * ++ * Copyright (c) 2024, AIROHA All rights reserved. ++ * ++ * Mayur Kumar ++ * Christian Marangi ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Base address of timer and watchdog registers */ ++#define TIMER_CTRL 0x0 ++#define WDT_ENABLE BIT(25) ++#define WDT_TIMER_INTERRUPT BIT(21) ++/* Timer3 is used as Watchdog Timer */ ++#define WDT_TIMER_ENABLE BIT(5) ++#define WDT_TIMER_LOAD_VALUE 0x2c ++#define WDT_TIMER_CUR_VALUE 0x30 ++#define WDT_TIMER_VAL GENMASK(31, 0) ++#define WDT_RELOAD 0x38 ++#define WDT_RLD BIT(0) ++ ++/* Airoha watchdog structure description */ ++struct airoha_wdt_desc { ++ struct watchdog_device wdog_dev; ++ unsigned int wdt_freq; ++ void __iomem *base; ++}; ++ ++#define WDT_HEARTBEAT 24 ++static int heartbeat = WDT_HEARTBEAT; ++module_param(heartbeat, int, 0); ++MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default=" ++ __MODULE_STRING(WDT_HEARTBEAT) ")"); ++ ++static bool nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, bool, 0); ++MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++static int airoha_wdt_start(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + TIMER_CTRL); ++ val |= (WDT_TIMER_ENABLE | WDT_ENABLE | WDT_TIMER_INTERRUPT); ++ writel(val, airoha_wdt->base + TIMER_CTRL); ++ val = wdog_dev->timeout * airoha_wdt->wdt_freq; ++ writel(val, airoha_wdt->base + WDT_TIMER_LOAD_VALUE); ++ ++ return 0; ++} ++ ++static int airoha_wdt_stop(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + TIMER_CTRL); ++ val &= (~WDT_ENABLE & ~WDT_TIMER_ENABLE); ++ writel(val, airoha_wdt->base + TIMER_CTRL); ++ ++ return 0; ++} ++ ++static int airoha_wdt_ping(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + WDT_RELOAD); ++ val |= WDT_RLD; ++ writel(val, airoha_wdt->base + WDT_RELOAD); ++ ++ return 0; ++} ++ ++static int airoha_wdt_set_timeout(struct watchdog_device *wdog_dev, unsigned int timeout) ++{ ++ wdog_dev->timeout = timeout; ++ ++ if (watchdog_active(wdog_dev)) { ++ airoha_wdt_stop(wdog_dev); ++ return airoha_wdt_start(wdog_dev); ++ } ++ ++ return 0; ++} ++ ++static unsigned int airoha_wdt_get_timeleft(struct watchdog_device *wdog_dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); ++ u32 val; ++ ++ val = readl(airoha_wdt->base + WDT_TIMER_CUR_VALUE); ++ return DIV_ROUND_UP(val, airoha_wdt->wdt_freq); ++} ++ ++static const struct watchdog_info airoha_wdt_info = { ++ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, ++ .identity = "Airoha Watchdog", ++}; ++ ++static const struct watchdog_ops airoha_wdt_ops = { ++ .owner = THIS_MODULE, ++ .start = airoha_wdt_start, ++ .stop = airoha_wdt_stop, ++ .ping = airoha_wdt_ping, ++ .set_timeout = airoha_wdt_set_timeout, ++ .get_timeleft = airoha_wdt_get_timeleft, ++}; ++ ++static int airoha_wdt_probe(struct platform_device *pdev) ++{ ++ struct airoha_wdt_desc *airoha_wdt; ++ struct watchdog_device *wdog_dev; ++ struct device *dev = &pdev->dev; ++ struct clk *bus_clk; ++ int ret; ++ ++ airoha_wdt = devm_kzalloc(dev, sizeof(*airoha_wdt), GFP_KERNEL); ++ if (!airoha_wdt) ++ return -ENOMEM; ++ ++ airoha_wdt->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(airoha_wdt->base)) ++ return PTR_ERR(airoha_wdt->base); ++ ++ bus_clk = devm_clk_get_enabled(dev, "bus"); ++ if (IS_ERR(bus_clk)) ++ return dev_err_probe(dev, PTR_ERR(bus_clk), ++ "failed to enable bus clock\n"); ++ ++ /* Watchdog ticks at half the bus rate */ ++ airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2; ++ ++ /* Initialize struct watchdog device */ ++ wdog_dev = &airoha_wdt->wdog_dev; ++ wdog_dev->timeout = heartbeat; ++ wdog_dev->info = &airoha_wdt_info; ++ wdog_dev->ops = &airoha_wdt_ops; ++ /* Bus 300MHz, watchdog 150MHz, 28 seconds */ ++ wdog_dev->max_timeout = FIELD_MAX(WDT_TIMER_VAL) / airoha_wdt->wdt_freq; ++ wdog_dev->parent = dev; ++ ++ watchdog_set_drvdata(wdog_dev, airoha_wdt); ++ watchdog_set_nowayout(wdog_dev, nowayout); ++ watchdog_stop_on_unregister(wdog_dev); ++ ++ ret = devm_watchdog_register_device(dev, wdog_dev); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, airoha_wdt); ++ return 0; ++} ++ ++static int airoha_wdt_suspend(struct device *dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev); ++ ++ if (watchdog_active(&airoha_wdt->wdog_dev)) ++ airoha_wdt_stop(&airoha_wdt->wdog_dev); ++ ++ return 0; ++} ++ ++static int airoha_wdt_resume(struct device *dev) ++{ ++ struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev); ++ ++ if (watchdog_active(&airoha_wdt->wdog_dev)) { ++ airoha_wdt_start(&airoha_wdt->wdog_dev); ++ airoha_wdt_ping(&airoha_wdt->wdog_dev); ++ } ++ return 0; ++} ++ ++static const struct of_device_id airoha_wdt_of_match[] = { ++ { .compatible = "airoha,en7581-wdt", }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, airoha_wdt_of_match); ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(airoha_wdt_pm_ops, airoha_wdt_suspend, airoha_wdt_resume); ++ ++static struct platform_driver airoha_wdt_driver = { ++ .probe = airoha_wdt_probe, ++ .driver = { ++ .name = "airoha-wdt", ++ .pm = pm_sleep_ptr(&airoha_wdt_pm_ops), ++ .of_match_table = airoha_wdt_of_match, ++ }, ++}; ++ ++module_platform_driver(airoha_wdt_driver); ++ ++MODULE_AUTHOR("Mayur Kumar "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha EN7581 Watchdog Driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/033-05-v6.13-clk-en7523-move-en7581_reset_register-in-en7581_clk_.patch b/target/linux/airoha/patches-6.12/033-05-v6.13-clk-en7523-move-en7581_reset_register-in-en7581_clk_.patch new file mode 100644 index 00000000000..6d6868f75a6 --- /dev/null +++ b/target/linux/airoha/patches-6.12/033-05-v6.13-clk-en7523-move-en7581_reset_register-in-en7581_clk_.patch @@ -0,0 +1,174 @@ +From 82e6bf912d5846646892becea659b39d178d79e3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 12 Nov 2024 01:08:53 +0100 +Subject: [PATCH 5/6] clk: en7523: move en7581_reset_register() in + en7581_clk_hw_init() + +Move en7581_reset_register routine in en7581_clk_hw_init() since reset +feature is supported just by EN7581 SoC. +Get rid of reset struct in en_clk_soc_data data struct. + +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/20241112-clk-en7581-syscon-v2-6-8ada5e394ae4@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 93 ++++++++++++++-------------------------- + 1 file changed, 33 insertions(+), 60 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -76,11 +76,6 @@ struct en_rst_data { + + struct en_clk_soc_data { + const struct clk_ops pcie_ops; +- struct { +- const u16 *bank_ofs; +- const u16 *idx_map; +- u16 idx_map_nr; +- } reset; + int (*hw_init)(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data); + }; +@@ -596,32 +591,6 @@ static void en7581_register_clocks(struc + clk_data->num = EN7523_NUM_CLOCKS; + } + +-static int en7581_clk_hw_init(struct platform_device *pdev, +- struct clk_hw_onecell_data *clk_data) +-{ +- void __iomem *np_base; +- struct regmap *map; +- u32 val; +- +- map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); +- if (IS_ERR(map)) +- return PTR_ERR(map); +- +- np_base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(np_base)) +- return PTR_ERR(np_base); +- +- en7581_register_clocks(&pdev->dev, clk_data, map, np_base); +- +- val = readl(np_base + REG_NP_SCU_SSTR); +- val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +- writel(val, np_base + REG_NP_SCU_SSTR); +- val = readl(np_base + REG_NP_SCU_PCIC); +- writel(val | 3, np_base + REG_NP_SCU_PCIC); +- +- return 0; +-} +- + static int en7523_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) + { +@@ -671,23 +640,18 @@ static int en7523_reset_xlate(struct res + return rst_data->idx_map[reset_spec->args[0]]; + } + +-static const struct reset_control_ops en7523_reset_ops = { ++static const struct reset_control_ops en7581_reset_ops = { + .assert = en7523_reset_assert, + .deassert = en7523_reset_deassert, + .status = en7523_reset_status, + }; + +-static int en7523_reset_register(struct platform_device *pdev, +- const struct en_clk_soc_data *soc_data) ++static int en7581_reset_register(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct en_rst_data *rst_data; + void __iomem *base; + +- /* no reset lines available */ +- if (!soc_data->reset.idx_map_nr) +- return 0; +- + base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(base)) + return PTR_ERR(base); +@@ -696,13 +660,13 @@ static int en7523_reset_register(struct + if (!rst_data) + return -ENOMEM; + +- rst_data->bank_ofs = soc_data->reset.bank_ofs; +- rst_data->idx_map = soc_data->reset.idx_map; ++ rst_data->bank_ofs = en7581_rst_ofs; ++ rst_data->idx_map = en7581_rst_map; + rst_data->base = base; + +- rst_data->rcdev.nr_resets = soc_data->reset.idx_map_nr; ++ rst_data->rcdev.nr_resets = ARRAY_SIZE(en7581_rst_map); + rst_data->rcdev.of_xlate = en7523_reset_xlate; +- rst_data->rcdev.ops = &en7523_reset_ops; ++ rst_data->rcdev.ops = &en7581_reset_ops; + rst_data->rcdev.of_node = dev->of_node; + rst_data->rcdev.of_reset_n_cells = 1; + rst_data->rcdev.owner = THIS_MODULE; +@@ -711,6 +675,32 @@ static int en7523_reset_register(struct + return devm_reset_controller_register(dev, &rst_data->rcdev); + } + ++static int en7581_clk_hw_init(struct platform_device *pdev, ++ struct clk_hw_onecell_data *clk_data) ++{ ++ void __iomem *np_base; ++ struct regmap *map; ++ u32 val; ++ ++ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); ++ ++ np_base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(np_base)) ++ return PTR_ERR(np_base); ++ ++ en7581_register_clocks(&pdev->dev, clk_data, map, np_base); ++ ++ val = readl(np_base + REG_NP_SCU_SSTR); ++ val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); ++ writel(val, np_base + REG_NP_SCU_SSTR); ++ val = readl(np_base + REG_NP_SCU_PCIC); ++ writel(val | 3, np_base + REG_NP_SCU_PCIC); ++ ++ return en7581_reset_register(pdev); ++} ++ + static int en7523_clk_probe(struct platform_device *pdev) + { + struct device_node *node = pdev->dev.of_node; +@@ -729,19 +719,7 @@ static int en7523_clk_probe(struct platf + if (r) + return r; + +- r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); +- if (r) +- return dev_err_probe(&pdev->dev, r, "Could not register clock provider: %s\n", +- pdev->name); +- +- r = en7523_reset_register(pdev, soc_data); +- if (r) { +- of_clk_del_provider(node); +- return dev_err_probe(&pdev->dev, r, "Could not register reset controller: %s\n", +- pdev->name); +- } +- +- return 0; ++ return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + } + + static const struct en_clk_soc_data en7523_data = { +@@ -759,11 +737,6 @@ static const struct en_clk_soc_data en75 + .enable = en7581_pci_enable, + .disable = en7581_pci_disable, + }, +- .reset = { +- .bank_ofs = en7581_rst_ofs, +- .idx_map = en7581_rst_map, +- .idx_map_nr = ARRAY_SIZE(en7581_rst_map), +- }, + .hw_init = en7581_clk_hw_init, + }; + diff --git a/target/linux/airoha/patches-6.12/033-06-v6.13-clk-en7523-map-io-region-in-a-single-block.patch b/target/linux/airoha/patches-6.12/033-06-v6.13-clk-en7523-map-io-region-in-a-single-block.patch new file mode 100644 index 00000000000..51945a94927 --- /dev/null +++ b/target/linux/airoha/patches-6.12/033-06-v6.13-clk-en7523-map-io-region-in-a-single-block.patch @@ -0,0 +1,84 @@ +From a9eaf305017a5ebe73ab34e85bd5414055a88f29 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 12 Nov 2024 01:08:54 +0100 +Subject: [PATCH 6/6] clk: en7523: map io region in a single block + +Map all clock-controller memory region in a single block. +This patch does not introduce any backward incompatibility since the dts +for EN7581 SoC is not upstream yet. + +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/20241112-clk-en7581-syscon-v2-7-8ada5e394ae4@kernel.org +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 32 +++++++++++++------------------- + 1 file changed, 13 insertions(+), 19 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -39,8 +39,8 @@ + #define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) + #define REG_CRYPTO_CLKSRC2 0x20c + +-#define REG_RST_CTRL2 0x00 +-#define REG_RST_CTRL1 0x04 ++#define REG_RST_CTRL2 0x830 ++#define REG_RST_CTRL1 0x834 + + struct en_clk_desc { + int id; +@@ -646,15 +646,9 @@ static const struct reset_control_ops en + .status = en7523_reset_status, + }; + +-static int en7581_reset_register(struct platform_device *pdev) ++static int en7581_reset_register(struct device *dev, void __iomem *base) + { +- struct device *dev = &pdev->dev; + struct en_rst_data *rst_data; +- void __iomem *base; +- +- base = devm_platform_ioremap_resource(pdev, 1); +- if (IS_ERR(base)) +- return PTR_ERR(base); + + rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL); + if (!rst_data) +@@ -678,27 +672,27 @@ static int en7581_reset_register(struct + static int en7581_clk_hw_init(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data) + { +- void __iomem *np_base; + struct regmap *map; ++ void __iomem *base; + u32 val; + + map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) + return PTR_ERR(map); + +- np_base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(np_base)) +- return PTR_ERR(np_base); ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); + +- en7581_register_clocks(&pdev->dev, clk_data, map, np_base); ++ en7581_register_clocks(&pdev->dev, clk_data, map, base); + +- val = readl(np_base + REG_NP_SCU_SSTR); ++ val = readl(base + REG_NP_SCU_SSTR); + val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +- writel(val, np_base + REG_NP_SCU_SSTR); +- val = readl(np_base + REG_NP_SCU_PCIC); +- writel(val | 3, np_base + REG_NP_SCU_PCIC); ++ writel(val, base + REG_NP_SCU_SSTR); ++ val = readl(base + REG_NP_SCU_PCIC); ++ writel(val | 3, base + REG_NP_SCU_PCIC); + +- return en7581_reset_register(pdev); ++ return en7581_reset_register(&pdev->dev, base); + } + + static int en7523_clk_probe(struct platform_device *pdev) diff --git a/target/linux/airoha/patches-6.12/034-01-v6.13-pinctrl-airoha-Add-support-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.12/034-01-v6.13-pinctrl-airoha-Add-support-for-EN7581-SoC.patch new file mode 100644 index 00000000000..ceb58a1b00d --- /dev/null +++ b/target/linux/airoha/patches-6.12/034-01-v6.13-pinctrl-airoha-Add-support-for-EN7581-SoC.patch @@ -0,0 +1,3060 @@ +From 1c8ace2d0725c1c8d5012f8a56c5fb31805aad27 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 23 Oct 2024 01:20:05 +0200 +Subject: [PATCH] pinctrl: airoha: Add support for EN7581 SoC + +Introduce pinctrl driver for EN7581 SoC. Current EN7581 pinctrl driver +supports the following functionalities: +- pin multiplexing +- pin pull-up, pull-down, open-drain, current strength, + {input,output}_enable, output_{low,high} +- gpio controller +- irq controller + +Tested-by: Benjamin Larsson +Co-developed-by: Benjamin Larsson +Signed-off-by: Benjamin Larsson +Reviewed-by: Linus Walleij +Reviewed-by: AngeloGioacchino Del Regno +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/20241023-en7581-pinctrl-v9-5-afb0cbcab0ec@kernel.org +Signed-off-by: Linus Walleij +--- + MAINTAINERS | 7 + + drivers/pinctrl/mediatek/Kconfig | 17 +- + drivers/pinctrl/mediatek/Makefile | 1 + + drivers/pinctrl/mediatek/pinctrl-airoha.c | 2970 +++++++++++++++++++++ + 4 files changed, 2994 insertions(+), 1 deletion(-) + create mode 100644 drivers/pinctrl/mediatek/pinctrl-airoha.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -18204,6 +18204,13 @@ F: drivers/pinctrl/ + F: include/dt-bindings/pinctrl/ + F: include/linux/pinctrl/ + ++PIN CONTROLLER - AIROHA ++M: Lorenzo Bianconi ++L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) ++S: Maintained ++F: Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml ++F: drivers/pinctrl/mediatek/pinctrl-airoha.c ++ + PIN CONTROLLER - AMD + M: Basavaraj Natikar + M: Shyam Sundar S K +--- a/drivers/pinctrl/mediatek/Kconfig ++++ b/drivers/pinctrl/mediatek/Kconfig +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + menu "MediaTek pinctrl drivers" +- depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST ++ depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST + + config EINT_MTK + tristate "MediaTek External Interrupt Support" +@@ -126,6 +126,21 @@ config PINCTRL_MT8127 + select PINCTRL_MTK + + # For ARMv8 SoCs ++config PINCTRL_AIROHA ++ tristate "Airoha EN7581 pin control" ++ depends on OF ++ depends on ARM64 || COMPILE_TEST ++ select PINMUX ++ select GENERIC_PINCONF ++ select GENERIC_PINCTRL_GROUPS ++ select GENERIC_PINMUX_FUNCTIONS ++ select GPIOLIB ++ select GPIOLIB_IRQCHIP ++ select REGMAP_MMIO ++ help ++ Say yes here to support pin controller and gpio driver ++ on Airoha EN7581 SoC. ++ + config PINCTRL_MT2712 + bool "MediaTek MT2712 pin control" + depends on OF +--- a/drivers/pinctrl/mediatek/Makefile ++++ b/drivers/pinctrl/mediatek/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE) += pinc + obj-$(CONFIG_PINCTRL_MTK_PARIS) += pinctrl-paris.o + + # SoC Drivers ++obj-$(CONFIG_PINCTRL_AIROHA) += pinctrl-airoha.o + obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o + obj-$(CONFIG_PINCTRL_MT7621) += pinctrl-mt7621.o + obj-$(CONFIG_PINCTRL_MT76X8) += pinctrl-mt76x8.o +--- /dev/null ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -0,0 +1,2970 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Author: Lorenzo Bianconi ++ * Author: Benjamin Larsson ++ * Author: Markus Gothe ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../core.h" ++#include "../pinconf.h" ++#include "../pinmux.h" ++ ++#define PINCTRL_PIN_GROUP(id) \ ++ PINCTRL_PINGROUP(#id, id##_pins, ARRAY_SIZE(id##_pins)) ++ ++#define PINCTRL_FUNC_DESC(id) \ ++ { \ ++ .desc = { \ ++ .func = { \ ++ .name = #id, \ ++ .groups = id##_groups, \ ++ .ngroups = ARRAY_SIZE(id##_groups), \ ++ } \ ++ }, \ ++ .groups = id##_func_group, \ ++ .group_size = ARRAY_SIZE(id##_func_group), \ ++ } ++ ++#define PINCTRL_CONF_DESC(p, offset, mask) \ ++ { \ ++ .pin = p, \ ++ .reg = { offset, mask }, \ ++ } ++ ++/* MUX */ ++#define REG_GPIO_2ND_I2C_MODE 0x0214 ++#define GPIO_MDC_IO_MASTER_MODE_MODE BIT(14) ++#define GPIO_I2C_MASTER_MODE_MODE BIT(13) ++#define GPIO_I2S_MODE_MASK BIT(12) ++#define GPIO_I2C_SLAVE_MODE_MODE BIT(11) ++#define GPIO_LAN3_LED1_MODE_MASK BIT(10) ++#define GPIO_LAN3_LED0_MODE_MASK BIT(9) ++#define GPIO_LAN2_LED1_MODE_MASK BIT(8) ++#define GPIO_LAN2_LED0_MODE_MASK BIT(7) ++#define GPIO_LAN1_LED1_MODE_MASK BIT(6) ++#define GPIO_LAN1_LED0_MODE_MASK BIT(5) ++#define GPIO_LAN0_LED1_MODE_MASK BIT(4) ++#define GPIO_LAN0_LED0_MODE_MASK BIT(3) ++#define PON_TOD_1PPS_MODE_MASK BIT(2) ++#define GSW_TOD_1PPS_MODE_MASK BIT(1) ++#define GPIO_2ND_I2C_MODE_MASK BIT(0) ++ ++#define REG_GPIO_SPI_CS1_MODE 0x0218 ++#define GPIO_PCM_SPI_CS4_MODE_MASK BIT(21) ++#define GPIO_PCM_SPI_CS3_MODE_MASK BIT(20) ++#define GPIO_PCM_SPI_CS2_MODE_P156_MASK BIT(19) ++#define GPIO_PCM_SPI_CS2_MODE_P128_MASK BIT(18) ++#define GPIO_PCM_SPI_CS1_MODE_MASK BIT(17) ++#define GPIO_PCM_SPI_MODE_MASK BIT(16) ++#define GPIO_PCM2_MODE_MASK BIT(13) ++#define GPIO_PCM1_MODE_MASK BIT(12) ++#define GPIO_PCM_INT_MODE_MASK BIT(9) ++#define GPIO_PCM_RESET_MODE_MASK BIT(8) ++#define GPIO_SPI_QUAD_MODE_MASK BIT(4) ++#define GPIO_SPI_CS4_MODE_MASK BIT(3) ++#define GPIO_SPI_CS3_MODE_MASK BIT(2) ++#define GPIO_SPI_CS2_MODE_MASK BIT(1) ++#define GPIO_SPI_CS1_MODE_MASK BIT(0) ++ ++#define REG_GPIO_PON_MODE 0x021c ++#define GPIO_PARALLEL_NAND_MODE_MASK BIT(14) ++#define GPIO_SGMII_MDIO_MODE_MASK BIT(13) ++#define GPIO_PCIE_RESET2_MASK BIT(12) ++#define SIPO_RCLK_MODE_MASK BIT(11) ++#define GPIO_PCIE_RESET1_MASK BIT(10) ++#define GPIO_PCIE_RESET0_MASK BIT(9) ++#define GPIO_UART5_MODE_MASK BIT(8) ++#define GPIO_UART4_MODE_MASK BIT(7) ++#define GPIO_HSUART_CTS_RTS_MODE_MASK BIT(6) ++#define GPIO_HSUART_MODE_MASK BIT(5) ++#define GPIO_UART2_CTS_RTS_MODE_MASK BIT(4) ++#define GPIO_UART2_MODE_MASK BIT(3) ++#define GPIO_SIPO_MODE_MASK BIT(2) ++#define GPIO_EMMC_MODE_MASK BIT(1) ++#define GPIO_PON_MODE_MASK BIT(0) ++ ++#define REG_NPU_UART_EN 0x0224 ++#define JTAG_UDI_EN_MASK BIT(4) ++#define JTAG_DFD_EN_MASK BIT(3) ++ ++/* LED MAP */ ++#define REG_LAN_LED0_MAPPING 0x027c ++#define REG_LAN_LED1_MAPPING 0x0280 ++ ++#define LAN4_LED_MAPPING_MASK GENMASK(18, 16) ++#define LAN4_PHY4_LED_MAP BIT(18) ++#define LAN4_PHY2_LED_MAP BIT(17) ++#define LAN4_PHY1_LED_MAP BIT(16) ++#define LAN4_PHY0_LED_MAP 0 ++#define LAN4_PHY3_LED_MAP GENMASK(17, 16) ++ ++#define LAN3_LED_MAPPING_MASK GENMASK(14, 12) ++#define LAN3_PHY4_LED_MAP BIT(14) ++#define LAN3_PHY2_LED_MAP BIT(13) ++#define LAN3_PHY1_LED_MAP BIT(12) ++#define LAN3_PHY0_LED_MAP 0 ++#define LAN3_PHY3_LED_MAP GENMASK(13, 12) ++ ++#define LAN2_LED_MAPPING_MASK GENMASK(10, 8) ++#define LAN2_PHY4_LED_MAP BIT(12) ++#define LAN2_PHY2_LED_MAP BIT(11) ++#define LAN2_PHY1_LED_MAP BIT(10) ++#define LAN2_PHY0_LED_MAP 0 ++#define LAN2_PHY3_LED_MAP GENMASK(11, 10) ++ ++#define LAN1_LED_MAPPING_MASK GENMASK(6, 4) ++#define LAN1_PHY4_LED_MAP BIT(6) ++#define LAN1_PHY2_LED_MAP BIT(5) ++#define LAN1_PHY1_LED_MAP BIT(4) ++#define LAN1_PHY0_LED_MAP 0 ++#define LAN1_PHY3_LED_MAP GENMASK(5, 4) ++ ++#define LAN0_LED_MAPPING_MASK GENMASK(2, 0) ++#define LAN0_PHY4_LED_MAP BIT(3) ++#define LAN0_PHY2_LED_MAP BIT(2) ++#define LAN0_PHY1_LED_MAP BIT(1) ++#define LAN0_PHY0_LED_MAP 0 ++#define LAN0_PHY3_LED_MAP GENMASK(2, 1) ++ ++/* CONF */ ++#define REG_I2C_SDA_E2 0x001c ++#define SPI_MISO_E2_MASK BIT(14) ++#define SPI_MOSI_E2_MASK BIT(13) ++#define SPI_CLK_E2_MASK BIT(12) ++#define SPI_CS0_E2_MASK BIT(11) ++#define PCIE2_RESET_E2_MASK BIT(10) ++#define PCIE1_RESET_E2_MASK BIT(9) ++#define PCIE0_RESET_E2_MASK BIT(8) ++#define UART1_RXD_E2_MASK BIT(3) ++#define UART1_TXD_E2_MASK BIT(2) ++#define I2C_SCL_E2_MASK BIT(1) ++#define I2C_SDA_E2_MASK BIT(0) ++ ++#define REG_I2C_SDA_E4 0x0020 ++#define SPI_MISO_E4_MASK BIT(14) ++#define SPI_MOSI_E4_MASK BIT(13) ++#define SPI_CLK_E4_MASK BIT(12) ++#define SPI_CS0_E4_MASK BIT(11) ++#define PCIE2_RESET_E4_MASK BIT(10) ++#define PCIE1_RESET_E4_MASK BIT(9) ++#define PCIE0_RESET_E4_MASK BIT(8) ++#define UART1_RXD_E4_MASK BIT(3) ++#define UART1_TXD_E4_MASK BIT(2) ++#define I2C_SCL_E4_MASK BIT(1) ++#define I2C_SDA_E4_MASK BIT(0) ++ ++#define REG_GPIO_L_E2 0x0024 ++#define REG_GPIO_L_E4 0x0028 ++#define REG_GPIO_H_E2 0x002c ++#define REG_GPIO_H_E4 0x0030 ++ ++#define REG_I2C_SDA_PU 0x0044 ++#define SPI_MISO_PU_MASK BIT(14) ++#define SPI_MOSI_PU_MASK BIT(13) ++#define SPI_CLK_PU_MASK BIT(12) ++#define SPI_CS0_PU_MASK BIT(11) ++#define PCIE2_RESET_PU_MASK BIT(10) ++#define PCIE1_RESET_PU_MASK BIT(9) ++#define PCIE0_RESET_PU_MASK BIT(8) ++#define UART1_RXD_PU_MASK BIT(3) ++#define UART1_TXD_PU_MASK BIT(2) ++#define I2C_SCL_PU_MASK BIT(1) ++#define I2C_SDA_PU_MASK BIT(0) ++ ++#define REG_I2C_SDA_PD 0x0048 ++#define SPI_MISO_PD_MASK BIT(14) ++#define SPI_MOSI_PD_MASK BIT(13) ++#define SPI_CLK_PD_MASK BIT(12) ++#define SPI_CS0_PD_MASK BIT(11) ++#define PCIE2_RESET_PD_MASK BIT(10) ++#define PCIE1_RESET_PD_MASK BIT(9) ++#define PCIE0_RESET_PD_MASK BIT(8) ++#define UART1_RXD_PD_MASK BIT(3) ++#define UART1_TXD_PD_MASK BIT(2) ++#define I2C_SCL_PD_MASK BIT(1) ++#define I2C_SDA_PD_MASK BIT(0) ++ ++#define REG_GPIO_L_PU 0x004c ++#define REG_GPIO_L_PD 0x0050 ++#define REG_GPIO_H_PU 0x0054 ++#define REG_GPIO_H_PD 0x0058 ++ ++#define REG_PCIE_RESET_OD 0x018c ++#define PCIE2_RESET_OD_MASK BIT(2) ++#define PCIE1_RESET_OD_MASK BIT(1) ++#define PCIE0_RESET_OD_MASK BIT(0) ++ ++/* GPIOs */ ++#define REG_GPIO_CTRL 0x0000 ++#define REG_GPIO_DATA 0x0004 ++#define REG_GPIO_INT 0x0008 ++#define REG_GPIO_INT_EDGE 0x000c ++#define REG_GPIO_INT_LEVEL 0x0010 ++#define REG_GPIO_OE 0x0014 ++#define REG_GPIO_CTRL1 0x0020 ++ ++/* PWM MODE CONF */ ++#define REG_GPIO_FLASH_MODE_CFG 0x0034 ++#define GPIO15_FLASH_MODE_CFG BIT(15) ++#define GPIO14_FLASH_MODE_CFG BIT(14) ++#define GPIO13_FLASH_MODE_CFG BIT(13) ++#define GPIO12_FLASH_MODE_CFG BIT(12) ++#define GPIO11_FLASH_MODE_CFG BIT(11) ++#define GPIO10_FLASH_MODE_CFG BIT(10) ++#define GPIO9_FLASH_MODE_CFG BIT(9) ++#define GPIO8_FLASH_MODE_CFG BIT(8) ++#define GPIO7_FLASH_MODE_CFG BIT(7) ++#define GPIO6_FLASH_MODE_CFG BIT(6) ++#define GPIO5_FLASH_MODE_CFG BIT(5) ++#define GPIO4_FLASH_MODE_CFG BIT(4) ++#define GPIO3_FLASH_MODE_CFG BIT(3) ++#define GPIO2_FLASH_MODE_CFG BIT(2) ++#define GPIO1_FLASH_MODE_CFG BIT(1) ++#define GPIO0_FLASH_MODE_CFG BIT(0) ++ ++#define REG_GPIO_CTRL2 0x0060 ++#define REG_GPIO_CTRL3 0x0064 ++ ++/* PWM MODE CONF EXT */ ++#define REG_GPIO_FLASH_MODE_CFG_EXT 0x0068 ++#define GPIO51_FLASH_MODE_CFG BIT(31) ++#define GPIO50_FLASH_MODE_CFG BIT(30) ++#define GPIO49_FLASH_MODE_CFG BIT(29) ++#define GPIO48_FLASH_MODE_CFG BIT(28) ++#define GPIO47_FLASH_MODE_CFG BIT(27) ++#define GPIO46_FLASH_MODE_CFG BIT(26) ++#define GPIO45_FLASH_MODE_CFG BIT(25) ++#define GPIO44_FLASH_MODE_CFG BIT(24) ++#define GPIO43_FLASH_MODE_CFG BIT(23) ++#define GPIO42_FLASH_MODE_CFG BIT(22) ++#define GPIO41_FLASH_MODE_CFG BIT(21) ++#define GPIO40_FLASH_MODE_CFG BIT(20) ++#define GPIO39_FLASH_MODE_CFG BIT(19) ++#define GPIO38_FLASH_MODE_CFG BIT(18) ++#define GPIO37_FLASH_MODE_CFG BIT(17) ++#define GPIO36_FLASH_MODE_CFG BIT(16) ++#define GPIO31_FLASH_MODE_CFG BIT(15) ++#define GPIO30_FLASH_MODE_CFG BIT(14) ++#define GPIO29_FLASH_MODE_CFG BIT(13) ++#define GPIO28_FLASH_MODE_CFG BIT(12) ++#define GPIO27_FLASH_MODE_CFG BIT(11) ++#define GPIO26_FLASH_MODE_CFG BIT(10) ++#define GPIO25_FLASH_MODE_CFG BIT(9) ++#define GPIO24_FLASH_MODE_CFG BIT(8) ++#define GPIO23_FLASH_MODE_CFG BIT(7) ++#define GPIO22_FLASH_MODE_CFG BIT(6) ++#define GPIO21_FLASH_MODE_CFG BIT(5) ++#define GPIO20_FLASH_MODE_CFG BIT(4) ++#define GPIO19_FLASH_MODE_CFG BIT(3) ++#define GPIO18_FLASH_MODE_CFG BIT(2) ++#define GPIO17_FLASH_MODE_CFG BIT(1) ++#define GPIO16_FLASH_MODE_CFG BIT(0) ++ ++#define REG_GPIO_DATA1 0x0070 ++#define REG_GPIO_OE1 0x0078 ++#define REG_GPIO_INT1 0x007c ++#define REG_GPIO_INT_EDGE1 0x0080 ++#define REG_GPIO_INT_EDGE2 0x0084 ++#define REG_GPIO_INT_EDGE3 0x0088 ++#define REG_GPIO_INT_LEVEL1 0x008c ++#define REG_GPIO_INT_LEVEL2 0x0090 ++#define REG_GPIO_INT_LEVEL3 0x0094 ++ ++#define AIROHA_NUM_PINS 64 ++#define AIROHA_PIN_BANK_SIZE (AIROHA_NUM_PINS / 2) ++#define AIROHA_REG_GPIOCTRL_NUM_PIN (AIROHA_NUM_PINS / 4) ++ ++static const u32 gpio_data_regs[] = { ++ REG_GPIO_DATA, ++ REG_GPIO_DATA1 ++}; ++ ++static const u32 gpio_out_regs[] = { ++ REG_GPIO_OE, ++ REG_GPIO_OE1 ++}; ++ ++static const u32 gpio_dir_regs[] = { ++ REG_GPIO_CTRL, ++ REG_GPIO_CTRL1, ++ REG_GPIO_CTRL2, ++ REG_GPIO_CTRL3 ++}; ++ ++static const u32 irq_status_regs[] = { ++ REG_GPIO_INT, ++ REG_GPIO_INT1 ++}; ++ ++static const u32 irq_level_regs[] = { ++ REG_GPIO_INT_LEVEL, ++ REG_GPIO_INT_LEVEL1, ++ REG_GPIO_INT_LEVEL2, ++ REG_GPIO_INT_LEVEL3 ++}; ++ ++static const u32 irq_edge_regs[] = { ++ REG_GPIO_INT_EDGE, ++ REG_GPIO_INT_EDGE1, ++ REG_GPIO_INT_EDGE2, ++ REG_GPIO_INT_EDGE3 ++}; ++ ++struct airoha_pinctrl_reg { ++ u32 offset; ++ u32 mask; ++}; ++ ++enum airoha_pinctrl_mux_func { ++ AIROHA_FUNC_MUX, ++ AIROHA_FUNC_PWM_MUX, ++ AIROHA_FUNC_PWM_EXT_MUX, ++}; ++ ++struct airoha_pinctrl_func_group { ++ const char *name; ++ struct { ++ enum airoha_pinctrl_mux_func mux; ++ u32 offset; ++ u32 mask; ++ u32 val; ++ } regmap[2]; ++ int regmap_size; ++}; ++ ++struct airoha_pinctrl_func { ++ const struct function_desc desc; ++ const struct airoha_pinctrl_func_group *groups; ++ u8 group_size; ++}; ++ ++struct airoha_pinctrl_conf { ++ u32 pin; ++ struct airoha_pinctrl_reg reg; ++}; ++ ++struct airoha_pinctrl_gpiochip { ++ struct gpio_chip chip; ++ ++ /* gpio */ ++ const u32 *data; ++ const u32 *dir; ++ const u32 *out; ++ /* irq */ ++ const u32 *status; ++ const u32 *level; ++ const u32 *edge; ++ ++ u32 irq_type[AIROHA_NUM_PINS]; ++}; ++ ++struct airoha_pinctrl { ++ struct pinctrl_dev *ctrl; ++ ++ struct regmap *chip_scu; ++ struct regmap *regmap; ++ ++ struct airoha_pinctrl_gpiochip gpiochip; ++}; ++ ++static struct pinctrl_pin_desc airoha_pinctrl_pins[] = { ++ PINCTRL_PIN(0, "uart1_txd"), ++ PINCTRL_PIN(1, "uart1_rxd"), ++ PINCTRL_PIN(2, "i2c_scl"), ++ PINCTRL_PIN(3, "i2c_sda"), ++ PINCTRL_PIN(4, "spi_cs0"), ++ PINCTRL_PIN(5, "spi_clk"), ++ PINCTRL_PIN(6, "spi_mosi"), ++ PINCTRL_PIN(7, "spi_miso"), ++ PINCTRL_PIN(13, "gpio0"), ++ PINCTRL_PIN(14, "gpio1"), ++ PINCTRL_PIN(15, "gpio2"), ++ PINCTRL_PIN(16, "gpio3"), ++ PINCTRL_PIN(17, "gpio4"), ++ PINCTRL_PIN(18, "gpio5"), ++ PINCTRL_PIN(19, "gpio6"), ++ PINCTRL_PIN(20, "gpio7"), ++ PINCTRL_PIN(21, "gpio8"), ++ PINCTRL_PIN(22, "gpio9"), ++ PINCTRL_PIN(23, "gpio10"), ++ PINCTRL_PIN(24, "gpio11"), ++ PINCTRL_PIN(25, "gpio12"), ++ PINCTRL_PIN(26, "gpio13"), ++ PINCTRL_PIN(27, "gpio14"), ++ PINCTRL_PIN(28, "gpio15"), ++ PINCTRL_PIN(29, "gpio16"), ++ PINCTRL_PIN(30, "gpio17"), ++ PINCTRL_PIN(31, "gpio18"), ++ PINCTRL_PIN(32, "gpio19"), ++ PINCTRL_PIN(33, "gpio20"), ++ PINCTRL_PIN(34, "gpio21"), ++ PINCTRL_PIN(35, "gpio22"), ++ PINCTRL_PIN(36, "gpio23"), ++ PINCTRL_PIN(37, "gpio24"), ++ PINCTRL_PIN(38, "gpio25"), ++ PINCTRL_PIN(39, "gpio26"), ++ PINCTRL_PIN(40, "gpio27"), ++ PINCTRL_PIN(41, "gpio28"), ++ PINCTRL_PIN(42, "gpio29"), ++ PINCTRL_PIN(43, "gpio30"), ++ PINCTRL_PIN(44, "gpio31"), ++ PINCTRL_PIN(45, "gpio32"), ++ PINCTRL_PIN(46, "gpio33"), ++ PINCTRL_PIN(47, "gpio34"), ++ PINCTRL_PIN(48, "gpio35"), ++ PINCTRL_PIN(49, "gpio36"), ++ PINCTRL_PIN(50, "gpio37"), ++ PINCTRL_PIN(51, "gpio38"), ++ PINCTRL_PIN(52, "gpio39"), ++ PINCTRL_PIN(53, "gpio40"), ++ PINCTRL_PIN(54, "gpio41"), ++ PINCTRL_PIN(55, "gpio42"), ++ PINCTRL_PIN(56, "gpio43"), ++ PINCTRL_PIN(57, "gpio44"), ++ PINCTRL_PIN(58, "gpio45"), ++ PINCTRL_PIN(59, "gpio46"), ++ PINCTRL_PIN(61, "pcie_reset0"), ++ PINCTRL_PIN(62, "pcie_reset1"), ++ PINCTRL_PIN(63, "pcie_reset2"), ++}; ++ ++static const int pon_pins[] = { 49, 50, 51, 52, 53, 54 }; ++static const int pon_tod_1pps_pins[] = { 46 }; ++static const int gsw_tod_1pps_pins[] = { 46 }; ++static const int sipo_pins[] = { 16, 17 }; ++static const int sipo_rclk_pins[] = { 16, 17, 43 }; ++static const int mdio_pins[] = { 14, 15 }; ++static const int uart2_pins[] = { 48, 55 }; ++static const int uart2_cts_rts_pins[] = { 46, 47 }; ++static const int hsuart_pins[] = { 28, 29 }; ++static const int hsuart_cts_rts_pins[] = { 26, 27 }; ++static const int uart4_pins[] = { 38, 39 }; ++static const int uart5_pins[] = { 18, 19 }; ++static const int i2c0_pins[] = { 2, 3 }; ++static const int i2c1_pins[] = { 14, 15 }; ++static const int jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; ++static const int jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; ++static const int i2s_pins[] = { 26, 27, 28, 29 }; ++static const int pcm1_pins[] = { 22, 23, 24, 25 }; ++static const int pcm2_pins[] = { 18, 19, 20, 21 }; ++static const int spi_quad_pins[] = { 32, 33 }; ++static const int spi_pins[] = { 4, 5, 6, 7 }; ++static const int spi_cs1_pins[] = { 34 }; ++static const int pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; ++static const int pcm_spi_int_pins[] = { 14 }; ++static const int pcm_spi_rst_pins[] = { 15 }; ++static const int pcm_spi_cs1_pins[] = { 43 }; ++static const int pcm_spi_cs2_pins[] = { 40 }; ++static const int pcm_spi_cs2_p128_pins[] = { 40 }; ++static const int pcm_spi_cs2_p156_pins[] = { 40 }; ++static const int pcm_spi_cs3_pins[] = { 41 }; ++static const int pcm_spi_cs4_pins[] = { 42 }; ++static const int emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; ++static const int pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; ++static const int gpio0_pins[] = { 13 }; ++static const int gpio1_pins[] = { 14 }; ++static const int gpio2_pins[] = { 15 }; ++static const int gpio3_pins[] = { 16 }; ++static const int gpio4_pins[] = { 17 }; ++static const int gpio5_pins[] = { 18 }; ++static const int gpio6_pins[] = { 19 }; ++static const int gpio7_pins[] = { 20 }; ++static const int gpio8_pins[] = { 21 }; ++static const int gpio9_pins[] = { 22 }; ++static const int gpio10_pins[] = { 23 }; ++static const int gpio11_pins[] = { 24 }; ++static const int gpio12_pins[] = { 25 }; ++static const int gpio13_pins[] = { 26 }; ++static const int gpio14_pins[] = { 27 }; ++static const int gpio15_pins[] = { 28 }; ++static const int gpio16_pins[] = { 29 }; ++static const int gpio17_pins[] = { 30 }; ++static const int gpio18_pins[] = { 31 }; ++static const int gpio19_pins[] = { 32 }; ++static const int gpio20_pins[] = { 33 }; ++static const int gpio21_pins[] = { 34 }; ++static const int gpio22_pins[] = { 35 }; ++static const int gpio23_pins[] = { 36 }; ++static const int gpio24_pins[] = { 37 }; ++static const int gpio25_pins[] = { 38 }; ++static const int gpio26_pins[] = { 39 }; ++static const int gpio27_pins[] = { 40 }; ++static const int gpio28_pins[] = { 41 }; ++static const int gpio29_pins[] = { 42 }; ++static const int gpio30_pins[] = { 43 }; ++static const int gpio31_pins[] = { 44 }; ++static const int gpio33_pins[] = { 46 }; ++static const int gpio34_pins[] = { 47 }; ++static const int gpio35_pins[] = { 48 }; ++static const int gpio36_pins[] = { 49 }; ++static const int gpio37_pins[] = { 50 }; ++static const int gpio38_pins[] = { 51 }; ++static const int gpio39_pins[] = { 52 }; ++static const int gpio40_pins[] = { 53 }; ++static const int gpio41_pins[] = { 54 }; ++static const int gpio42_pins[] = { 55 }; ++static const int gpio43_pins[] = { 56 }; ++static const int gpio44_pins[] = { 57 }; ++static const int gpio45_pins[] = { 58 }; ++static const int gpio46_pins[] = { 59 }; ++static const int pcie_reset0_pins[] = { 61 }; ++static const int pcie_reset1_pins[] = { 62 }; ++static const int pcie_reset2_pins[] = { 63 }; ++ ++static const struct pingroup airoha_pinctrl_groups[] = { ++ PINCTRL_PIN_GROUP(pon), ++ PINCTRL_PIN_GROUP(pon_tod_1pps), ++ PINCTRL_PIN_GROUP(gsw_tod_1pps), ++ PINCTRL_PIN_GROUP(sipo), ++ PINCTRL_PIN_GROUP(sipo_rclk), ++ PINCTRL_PIN_GROUP(mdio), ++ PINCTRL_PIN_GROUP(uart2), ++ PINCTRL_PIN_GROUP(uart2_cts_rts), ++ PINCTRL_PIN_GROUP(hsuart), ++ PINCTRL_PIN_GROUP(hsuart_cts_rts), ++ PINCTRL_PIN_GROUP(uart4), ++ PINCTRL_PIN_GROUP(uart5), ++ PINCTRL_PIN_GROUP(i2c0), ++ PINCTRL_PIN_GROUP(i2c1), ++ PINCTRL_PIN_GROUP(jtag_udi), ++ PINCTRL_PIN_GROUP(jtag_dfd), ++ PINCTRL_PIN_GROUP(i2s), ++ PINCTRL_PIN_GROUP(pcm1), ++ PINCTRL_PIN_GROUP(pcm2), ++ PINCTRL_PIN_GROUP(spi), ++ PINCTRL_PIN_GROUP(spi_quad), ++ PINCTRL_PIN_GROUP(spi_cs1), ++ PINCTRL_PIN_GROUP(pcm_spi), ++ PINCTRL_PIN_GROUP(pcm_spi_int), ++ PINCTRL_PIN_GROUP(pcm_spi_rst), ++ PINCTRL_PIN_GROUP(pcm_spi_cs1), ++ PINCTRL_PIN_GROUP(pcm_spi_cs2_p128), ++ PINCTRL_PIN_GROUP(pcm_spi_cs2_p156), ++ PINCTRL_PIN_GROUP(pcm_spi_cs2), ++ PINCTRL_PIN_GROUP(pcm_spi_cs3), ++ PINCTRL_PIN_GROUP(pcm_spi_cs4), ++ PINCTRL_PIN_GROUP(emmc), ++ PINCTRL_PIN_GROUP(pnand), ++ PINCTRL_PIN_GROUP(gpio0), ++ PINCTRL_PIN_GROUP(gpio1), ++ PINCTRL_PIN_GROUP(gpio2), ++ PINCTRL_PIN_GROUP(gpio3), ++ PINCTRL_PIN_GROUP(gpio4), ++ PINCTRL_PIN_GROUP(gpio5), ++ PINCTRL_PIN_GROUP(gpio6), ++ PINCTRL_PIN_GROUP(gpio7), ++ PINCTRL_PIN_GROUP(gpio8), ++ PINCTRL_PIN_GROUP(gpio9), ++ PINCTRL_PIN_GROUP(gpio10), ++ PINCTRL_PIN_GROUP(gpio11), ++ PINCTRL_PIN_GROUP(gpio12), ++ PINCTRL_PIN_GROUP(gpio13), ++ PINCTRL_PIN_GROUP(gpio14), ++ PINCTRL_PIN_GROUP(gpio15), ++ PINCTRL_PIN_GROUP(gpio16), ++ PINCTRL_PIN_GROUP(gpio17), ++ PINCTRL_PIN_GROUP(gpio18), ++ PINCTRL_PIN_GROUP(gpio19), ++ PINCTRL_PIN_GROUP(gpio20), ++ PINCTRL_PIN_GROUP(gpio21), ++ PINCTRL_PIN_GROUP(gpio22), ++ PINCTRL_PIN_GROUP(gpio23), ++ PINCTRL_PIN_GROUP(gpio24), ++ PINCTRL_PIN_GROUP(gpio25), ++ PINCTRL_PIN_GROUP(gpio26), ++ PINCTRL_PIN_GROUP(gpio27), ++ PINCTRL_PIN_GROUP(gpio28), ++ PINCTRL_PIN_GROUP(gpio29), ++ PINCTRL_PIN_GROUP(gpio30), ++ PINCTRL_PIN_GROUP(gpio31), ++ PINCTRL_PIN_GROUP(gpio33), ++ PINCTRL_PIN_GROUP(gpio34), ++ PINCTRL_PIN_GROUP(gpio35), ++ PINCTRL_PIN_GROUP(gpio36), ++ PINCTRL_PIN_GROUP(gpio37), ++ PINCTRL_PIN_GROUP(gpio38), ++ PINCTRL_PIN_GROUP(gpio39), ++ PINCTRL_PIN_GROUP(gpio40), ++ PINCTRL_PIN_GROUP(gpio41), ++ PINCTRL_PIN_GROUP(gpio42), ++ PINCTRL_PIN_GROUP(gpio43), ++ PINCTRL_PIN_GROUP(gpio44), ++ PINCTRL_PIN_GROUP(gpio45), ++ PINCTRL_PIN_GROUP(gpio46), ++ PINCTRL_PIN_GROUP(pcie_reset0), ++ PINCTRL_PIN_GROUP(pcie_reset1), ++ PINCTRL_PIN_GROUP(pcie_reset2), ++}; ++ ++static const char *const pon_groups[] = { "pon" }; ++static const char *const tod_1pps_groups[] = { "pon_tod_1pps", "gsw_tod_1pps" }; ++static const char *const sipo_groups[] = { "sipo", "sipo_rclk" }; ++static const char *const mdio_groups[] = { "mdio" }; ++static const char *const uart_groups[] = { "uart2", "uart2_cts_rts", "hsuart", ++ "hsuart_cts_rts", "uart4", ++ "uart5" }; ++static const char *const i2c_groups[] = { "i2c1" }; ++static const char *const jtag_groups[] = { "jtag_udi", "jtag_dfd" }; ++static const char *const pcm_groups[] = { "pcm1", "pcm2" }; ++static const char *const spi_groups[] = { "spi_quad", "spi_cs1" }; ++static const char *const pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", ++ "pcm_spi_rst", "pcm_spi_cs1", ++ "pcm_spi_cs2_p156", ++ "pcm_spi_cs2_p128", ++ "pcm_spi_cs3", "pcm_spi_cs4" }; ++static const char *const i2s_groups[] = { "i2s" }; ++static const char *const emmc_groups[] = { "emmc" }; ++static const char *const pnand_groups[] = { "pnand" }; ++static const char *const pcie_reset_groups[] = { "pcie_reset0", "pcie_reset1", ++ "pcie_reset2" }; ++static const char *const pwm_groups[] = { "gpio0", "gpio1", ++ "gpio2", "gpio3", ++ "gpio4", "gpio5", ++ "gpio6", "gpio7", ++ "gpio8", "gpio9", ++ "gpio10", "gpio11", ++ "gpio12", "gpio13", ++ "gpio14", "gpio15", ++ "gpio16", "gpio17", ++ "gpio18", "gpio19", ++ "gpio20", "gpio21", ++ "gpio22", "gpio23", ++ "gpio24", "gpio25", ++ "gpio26", "gpio27", ++ "gpio28", "gpio29", ++ "gpio30", "gpio31", ++ "gpio36", "gpio37", ++ "gpio38", "gpio39", ++ "gpio40", "gpio41", ++ "gpio42", "gpio43", ++ "gpio44", "gpio45", ++ "gpio46", "gpio47" }; ++static const char *const phy1_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy2_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy3_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy4_led0_groups[] = { "gpio33", "gpio34", ++ "gpio35", "gpio42" }; ++static const char *const phy1_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++static const char *const phy2_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++static const char *const phy3_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++static const char *const phy4_led1_groups[] = { "gpio43", "gpio44", ++ "gpio45", "gpio46" }; ++ ++static const struct airoha_pinctrl_func_group pon_func_group[] = { ++ { ++ .name = "pon", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PON_MODE_MASK, ++ GPIO_PON_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group tod_1pps_func_group[] = { ++ { ++ .name = "pon_tod_1pps", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ PON_TOD_1PPS_MODE_MASK, ++ PON_TOD_1PPS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gsw_tod_1pps", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GSW_TOD_1PPS_MODE_MASK, ++ GSW_TOD_1PPS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group sipo_func_group[] = { ++ { ++ .name = "sipo", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK, ++ GPIO_SIPO_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "sipo_rclk", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK, ++ GPIO_SIPO_MODE_MASK | SIPO_RCLK_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group mdio_func_group[] = { ++ { ++ .name = "mdio", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SGMII_MDIO_MODE_MASK, ++ GPIO_SGMII_MDIO_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_MDC_IO_MASTER_MODE_MODE, ++ GPIO_MDC_IO_MASTER_MODE_MODE ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group uart_func_group[] = { ++ { ++ .name = "uart2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART2_MODE_MASK, ++ GPIO_UART2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "uart2_cts_rts", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK, ++ GPIO_UART2_MODE_MASK | GPIO_UART2_CTS_RTS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "hsuart", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK, ++ GPIO_HSUART_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++ { ++ .name = "hsuart_cts_rts", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK, ++ GPIO_HSUART_MODE_MASK | GPIO_HSUART_CTS_RTS_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "uart4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART4_MODE_MASK, ++ GPIO_UART4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "uart5", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_UART5_MODE_MASK, ++ GPIO_UART5_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group i2c_func_group[] = { ++ { ++ .name = "i2c1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_2ND_I2C_MODE_MASK, ++ GPIO_2ND_I2C_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group jtag_func_group[] = { ++ { ++ .name = "jtag_udi", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_NPU_UART_EN, ++ JTAG_UDI_EN_MASK, ++ JTAG_UDI_EN_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "jtag_dfd", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_NPU_UART_EN, ++ JTAG_DFD_EN_MASK, ++ JTAG_DFD_EN_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pcm_func_group[] = { ++ { ++ .name = "pcm1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM1_MODE_MASK, ++ GPIO_PCM1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM2_MODE_MASK, ++ GPIO_PCM2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group spi_func_group[] = { ++ { ++ .name = "spi_quad", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_QUAD_MODE_MASK, ++ GPIO_SPI_QUAD_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS1_MODE_MASK, ++ GPIO_SPI_CS1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS2_MODE_MASK, ++ GPIO_SPI_CS2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs3", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS3_MODE_MASK, ++ GPIO_SPI_CS3_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "spi_cs4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_SPI_CS4_MODE_MASK, ++ GPIO_SPI_CS4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pcm_spi_func_group[] = { ++ { ++ .name = "pcm_spi", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_MODE_MASK, ++ GPIO_PCM_SPI_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_int", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_INT_MODE_MASK, ++ GPIO_PCM_INT_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_rst", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_RESET_MODE_MASK, ++ GPIO_PCM_RESET_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS1_MODE_MASK, ++ GPIO_PCM_SPI_CS1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs2_p128", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS2_MODE_P128_MASK, ++ GPIO_PCM_SPI_CS2_MODE_P128_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs2_p156", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS2_MODE_P156_MASK, ++ GPIO_PCM_SPI_CS2_MODE_P156_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs3", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS3_MODE_MASK, ++ GPIO_PCM_SPI_CS3_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS4_MODE_MASK, ++ GPIO_PCM_SPI_CS4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group i2s_func_group[] = { ++ { ++ .name = "i2s", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_I2S_MODE_MASK, ++ GPIO_I2S_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group emmc_func_group[] = { ++ { ++ .name = "emmc", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_EMMC_MODE_MASK, ++ GPIO_EMMC_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pnand_func_group[] = { ++ { ++ .name = "pnand", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PARALLEL_NAND_MODE_MASK, ++ GPIO_PARALLEL_NAND_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group pcie_reset_func_group[] = { ++ { ++ .name = "pcie_reset0", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET0_MASK, ++ GPIO_PCIE_RESET0_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcie_reset1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET1_MASK, ++ GPIO_PCIE_RESET1_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcie_reset2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET2_MASK, ++ GPIO_PCIE_RESET2_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++/* PWM */ ++static const struct airoha_pinctrl_func_group pwm_func_group[] = { ++ { ++ .name = "gpio0", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO0_FLASH_MODE_CFG, ++ GPIO0_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio1", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO1_FLASH_MODE_CFG, ++ GPIO1_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio2", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO2_FLASH_MODE_CFG, ++ GPIO2_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio3", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO3_FLASH_MODE_CFG, ++ GPIO3_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio4", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO4_FLASH_MODE_CFG, ++ GPIO4_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio5", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO5_FLASH_MODE_CFG, ++ GPIO5_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio6", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO6_FLASH_MODE_CFG, ++ GPIO6_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio7", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO7_FLASH_MODE_CFG, ++ GPIO7_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio8", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO8_FLASH_MODE_CFG, ++ GPIO8_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio9", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO9_FLASH_MODE_CFG, ++ GPIO9_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio10", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO10_FLASH_MODE_CFG, ++ GPIO10_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio11", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO11_FLASH_MODE_CFG, ++ GPIO11_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio12", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO12_FLASH_MODE_CFG, ++ GPIO12_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio13", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO13_FLASH_MODE_CFG, ++ GPIO13_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio14", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO14_FLASH_MODE_CFG, ++ GPIO14_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio15", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_MUX, ++ REG_GPIO_FLASH_MODE_CFG, ++ GPIO15_FLASH_MODE_CFG, ++ GPIO15_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio16", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO16_FLASH_MODE_CFG, ++ GPIO16_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio17", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO17_FLASH_MODE_CFG, ++ GPIO17_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio18", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO18_FLASH_MODE_CFG, ++ GPIO18_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio19", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO19_FLASH_MODE_CFG, ++ GPIO19_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio20", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO20_FLASH_MODE_CFG, ++ GPIO20_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio21", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO21_FLASH_MODE_CFG, ++ GPIO21_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio22", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO22_FLASH_MODE_CFG, ++ GPIO22_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio23", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO23_FLASH_MODE_CFG, ++ GPIO23_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio24", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO24_FLASH_MODE_CFG, ++ GPIO24_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio25", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO25_FLASH_MODE_CFG, ++ GPIO25_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio26", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO26_FLASH_MODE_CFG, ++ GPIO26_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio27", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO27_FLASH_MODE_CFG, ++ GPIO27_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio28", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO28_FLASH_MODE_CFG, ++ GPIO28_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio29", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO29_FLASH_MODE_CFG, ++ GPIO29_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio30", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO30_FLASH_MODE_CFG, ++ GPIO30_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio31", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO31_FLASH_MODE_CFG, ++ GPIO31_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio36", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO36_FLASH_MODE_CFG, ++ GPIO36_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio37", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO37_FLASH_MODE_CFG, ++ GPIO37_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio38", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO38_FLASH_MODE_CFG, ++ GPIO38_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio39", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO39_FLASH_MODE_CFG, ++ GPIO39_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio40", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO40_FLASH_MODE_CFG, ++ GPIO40_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio41", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO41_FLASH_MODE_CFG, ++ GPIO41_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO42_FLASH_MODE_CFG, ++ GPIO42_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO43_FLASH_MODE_CFG, ++ GPIO43_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO44_FLASH_MODE_CFG, ++ GPIO44_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO45_FLASH_MODE_CFG, ++ GPIO45_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO46_FLASH_MODE_CFG, ++ GPIO46_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "gpio47", ++ .regmap[0] = { ++ AIROHA_FUNC_PWM_EXT_MUX, ++ REG_GPIO_FLASH_MODE_CFG_EXT, ++ GPIO47_FLASH_MODE_CFG, ++ GPIO47_FLASH_MODE_CFG ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy1_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy2_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy3_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy4_led0_func_group[] = { ++ { ++ .name = "gpio33", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED0_MODE_MASK, ++ GPIO_LAN0_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio34", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED0_MODE_MASK, ++ GPIO_LAN1_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio35", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED0_MODE_MASK, ++ GPIO_LAN2_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio42", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED0_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy1_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY1_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy2_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY2_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY3_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { ++ { ++ .name = "gpio43", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN0_LED1_MODE_MASK, ++ GPIO_LAN0_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio44", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN1_LED1_MODE_MASK, ++ GPIO_LAN1_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio45", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN2_LED1_MODE_MASK, ++ GPIO_LAN2_LED1_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, { ++ .name = "gpio46", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_2ND_I2C_MODE, ++ GPIO_LAN3_LED0_MODE_MASK, ++ GPIO_LAN3_LED0_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_LAN_LED1_MAPPING, ++ LAN4_LED_MAPPING_MASK, ++ LAN4_PHY4_LED_MAP ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ ++static const struct airoha_pinctrl_func airoha_pinctrl_funcs[] = { ++ PINCTRL_FUNC_DESC(pon), ++ PINCTRL_FUNC_DESC(tod_1pps), ++ PINCTRL_FUNC_DESC(sipo), ++ PINCTRL_FUNC_DESC(mdio), ++ PINCTRL_FUNC_DESC(uart), ++ PINCTRL_FUNC_DESC(i2c), ++ PINCTRL_FUNC_DESC(jtag), ++ PINCTRL_FUNC_DESC(pcm), ++ PINCTRL_FUNC_DESC(spi), ++ PINCTRL_FUNC_DESC(pcm_spi), ++ PINCTRL_FUNC_DESC(i2s), ++ PINCTRL_FUNC_DESC(emmc), ++ PINCTRL_FUNC_DESC(pnand), ++ PINCTRL_FUNC_DESC(pcie_reset), ++ PINCTRL_FUNC_DESC(pwm), ++ PINCTRL_FUNC_DESC(phy1_led0), ++ PINCTRL_FUNC_DESC(phy2_led0), ++ PINCTRL_FUNC_DESC(phy3_led0), ++ PINCTRL_FUNC_DESC(phy4_led0), ++ PINCTRL_FUNC_DESC(phy1_led1), ++ PINCTRL_FUNC_DESC(phy2_led1), ++ PINCTRL_FUNC_DESC(phy3_led1), ++ PINCTRL_FUNC_DESC(phy4_led1), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_PU, I2C_SDA_PU_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_PU, I2C_SCL_PU_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_PU, SPI_CS0_PU_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_PU, SPI_CLK_PU_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_PU, SPI_MOSI_PU_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_PU, SPI_MISO_PU_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_PU, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_PU, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_PU, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_PU, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_PU, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_PU, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_PU, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_PU, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_PU, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_PU, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_PU, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_PU, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_PU, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_PU, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_PU, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_PU, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_PU, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_PU, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_PU, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_PU, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_PU, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_PU, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_PU, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_PU, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_PU, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_PU, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_PU, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_PU, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_PU, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_PU, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_PU, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_PU, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_PU, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_PU, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_PU, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_PU, PCIE0_RESET_PU_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_PU, PCIE1_RESET_PU_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_PD, I2C_SDA_PD_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_PD, I2C_SCL_PD_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_PD, SPI_CS0_PD_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_PD, SPI_CLK_PD_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_PD, SPI_MOSI_PD_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_PD, SPI_MISO_PD_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_PD, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_PD, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_PD, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_PD, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_PD, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_PD, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_PD, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_PD, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_PD, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_PD, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_PD, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_PD, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_PD, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_PD, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_PD, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_PD, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_PD, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_PD, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_PD, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_PD, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_PD, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_PD, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_PD, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_PD, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_PD, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_PD, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_PD, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_PD, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_PD, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_PD, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_PD, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_PD, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_PD, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_PD, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_PD, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_PD, PCIE0_RESET_PD_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_PD, PCIE1_RESET_PD_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_E2, I2C_SDA_E2_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_E2, I2C_SCL_E2_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_E2, SPI_CS0_E2_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_E2, SPI_CLK_E2_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_E2, SPI_MOSI_E2_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_E2, SPI_MISO_E2_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_E2, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_E2, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_E2, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_E2, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_E2, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_E2, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_E2, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_E2, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_E2, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_E2, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_E2, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_E2, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_E2, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_E2, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_E2, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_E2, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_E2, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_E2, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_E2, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_E2, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_E2, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_E2, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_E2, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_E2, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_E2, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_E2, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_E2, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_E2, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_E2, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_E2, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_E2, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_E2, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_E2, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_E2, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_E2, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_E2, PCIE0_RESET_E2_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_E2, PCIE1_RESET_E2_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = { ++ PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), ++ PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), ++ PINCTRL_CONF_DESC(2, REG_I2C_SDA_E4, I2C_SDA_E4_MASK), ++ PINCTRL_CONF_DESC(3, REG_I2C_SDA_E4, I2C_SCL_E4_MASK), ++ PINCTRL_CONF_DESC(4, REG_I2C_SDA_E4, SPI_CS0_E4_MASK), ++ PINCTRL_CONF_DESC(5, REG_I2C_SDA_E4, SPI_CLK_E4_MASK), ++ PINCTRL_CONF_DESC(6, REG_I2C_SDA_E4, SPI_MOSI_E4_MASK), ++ PINCTRL_CONF_DESC(7, REG_I2C_SDA_E4, SPI_MISO_E4_MASK), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_E4, BIT(0)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_E4, BIT(1)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_E4, BIT(2)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_E4, BIT(3)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_E4, BIT(4)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(5)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(6)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(7)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(8)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(9)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(10)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(11)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_E4, BIT(12)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_E4, BIT(13)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_E4, BIT(14)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_E4, BIT(15)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(16)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(17)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(18)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(18)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(20)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_L_E4, BIT(21)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_L_E4, BIT(22)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_L_E4, BIT(23)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_L_E4, BIT(24)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_L_E4, BIT(25)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_L_E4, BIT(26)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_L_E4, BIT(27)), ++ PINCTRL_CONF_DESC(41, REG_GPIO_L_E4, BIT(28)), ++ PINCTRL_CONF_DESC(42, REG_GPIO_L_E4, BIT(29)), ++ PINCTRL_CONF_DESC(43, REG_GPIO_L_E4, BIT(30)), ++ PINCTRL_CONF_DESC(44, REG_GPIO_L_E4, BIT(31)), ++ PINCTRL_CONF_DESC(45, REG_GPIO_H_E4, BIT(0)), ++ PINCTRL_CONF_DESC(46, REG_GPIO_H_E4, BIT(1)), ++ PINCTRL_CONF_DESC(47, REG_GPIO_H_E4, BIT(2)), ++ PINCTRL_CONF_DESC(48, REG_GPIO_H_E4, BIT(3)), ++ PINCTRL_CONF_DESC(49, REG_GPIO_H_E4, BIT(4)), ++ PINCTRL_CONF_DESC(50, REG_GPIO_H_E4, BIT(5)), ++ PINCTRL_CONF_DESC(51, REG_GPIO_H_E4, BIT(6)), ++ PINCTRL_CONF_DESC(52, REG_GPIO_H_E4, BIT(7)), ++ PINCTRL_CONF_DESC(53, REG_GPIO_H_E4, BIT(8)), ++ PINCTRL_CONF_DESC(54, REG_GPIO_H_E4, BIT(9)), ++ PINCTRL_CONF_DESC(55, REG_GPIO_H_E4, BIT(10)), ++ PINCTRL_CONF_DESC(56, REG_GPIO_H_E4, BIT(11)), ++ PINCTRL_CONF_DESC(57, REG_GPIO_H_E4, BIT(12)), ++ PINCTRL_CONF_DESC(58, REG_GPIO_H_E4, BIT(13)), ++ PINCTRL_CONF_DESC(59, REG_GPIO_H_E4, BIT(14)), ++ PINCTRL_CONF_DESC(61, REG_I2C_SDA_E4, PCIE0_RESET_E4_MASK), ++ PINCTRL_CONF_DESC(62, REG_I2C_SDA_E4, PCIE1_RESET_E4_MASK), ++ PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK), ++}; ++ ++static const struct airoha_pinctrl_conf airoha_pinctrl_pcie_rst_od_conf[] = { ++ PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), ++ PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), ++ PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK), ++}; ++ ++static int airoha_convert_pin_to_reg_offset(struct pinctrl_dev *pctrl_dev, ++ struct pinctrl_gpio_range *range, ++ int pin) ++{ ++ if (!range) ++ range = pinctrl_find_gpio_range_from_pin_nolock(pctrl_dev, ++ pin); ++ if (!range) ++ return -EINVAL; ++ ++ return pin - range->pin_base; ++} ++ ++/* gpio callbacks */ ++static void airoha_gpio_set(struct gpio_chip *chip, unsigned int gpio, ++ int value) ++{ ++ struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); ++ u32 offset = gpio % AIROHA_PIN_BANK_SIZE; ++ u8 index = gpio / AIROHA_PIN_BANK_SIZE; ++ ++ regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.data[index], ++ BIT(offset), value ? BIT(offset) : 0); ++} ++ ++static int airoha_gpio_get(struct gpio_chip *chip, unsigned int gpio) ++{ ++ struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); ++ u32 val, pin = gpio % AIROHA_PIN_BANK_SIZE; ++ u8 index = gpio / AIROHA_PIN_BANK_SIZE; ++ int err; ++ ++ err = regmap_read(pinctrl->regmap, ++ pinctrl->gpiochip.data[index], &val); ++ ++ return err ? err : !!(val & BIT(pin)); ++} ++ ++static int airoha_gpio_direction_output(struct gpio_chip *chip, ++ unsigned int gpio, int value) ++{ ++ int err; ++ ++ err = pinctrl_gpio_direction_output(chip, gpio); ++ if (err) ++ return err; ++ ++ airoha_gpio_set(chip, gpio, value); ++ ++ return 0; ++} ++ ++/* irq callbacks */ ++static void airoha_irq_unmask(struct irq_data *data) ++{ ++ u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u32 mask = GENMASK(2 * offset + 1, 2 * offset); ++ struct airoha_pinctrl_gpiochip *gpiochip; ++ struct airoha_pinctrl *pinctrl; ++ u32 val = BIT(2 * offset); ++ ++ gpiochip = irq_data_get_irq_chip_data(data); ++ if (WARN_ON_ONCE(data->hwirq >= ARRAY_SIZE(gpiochip->irq_type))) ++ return; ++ ++ pinctrl = container_of(gpiochip, struct airoha_pinctrl, gpiochip); ++ switch (gpiochip->irq_type[data->hwirq]) { ++ case IRQ_TYPE_LEVEL_LOW: ++ val = val << 1; ++ fallthrough; ++ case IRQ_TYPE_LEVEL_HIGH: ++ regmap_update_bits(pinctrl->regmap, gpiochip->level[index], ++ mask, val); ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ val = val << 1; ++ fallthrough; ++ case IRQ_TYPE_EDGE_RISING: ++ regmap_update_bits(pinctrl->regmap, gpiochip->edge[index], ++ mask, val); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ regmap_set_bits(pinctrl->regmap, gpiochip->edge[index], mask); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void airoha_irq_mask(struct irq_data *data) ++{ ++ u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ u32 mask = GENMASK(2 * offset + 1, 2 * offset); ++ struct airoha_pinctrl_gpiochip *gpiochip; ++ struct airoha_pinctrl *pinctrl; ++ ++ gpiochip = irq_data_get_irq_chip_data(data); ++ pinctrl = container_of(gpiochip, struct airoha_pinctrl, gpiochip); ++ ++ regmap_clear_bits(pinctrl->regmap, gpiochip->level[index], mask); ++ regmap_clear_bits(pinctrl->regmap, gpiochip->edge[index], mask); ++} ++ ++static int airoha_irq_type(struct irq_data *data, unsigned int type) ++{ ++ struct airoha_pinctrl_gpiochip *gpiochip; ++ ++ gpiochip = irq_data_get_irq_chip_data(data); ++ if (data->hwirq >= ARRAY_SIZE(gpiochip->irq_type)) ++ return -EINVAL; ++ ++ if (type == IRQ_TYPE_PROBE) { ++ if (gpiochip->irq_type[data->hwirq]) ++ return 0; ++ ++ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; ++ } ++ gpiochip->irq_type[data->hwirq] = type & IRQ_TYPE_SENSE_MASK; ++ ++ return 0; ++} ++ ++static irqreturn_t airoha_irq_handler(int irq, void *data) ++{ ++ struct airoha_pinctrl *pinctrl = data; ++ bool handled = false; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(irq_status_regs); i++) { ++ struct gpio_irq_chip *girq = &pinctrl->gpiochip.chip.irq; ++ u32 status; ++ int irq; ++ ++ if (regmap_read(pinctrl->regmap, pinctrl->gpiochip.status[i], ++ &status)) ++ continue; ++ ++ for_each_set_bit(irq, (unsigned long *)&status, ++ AIROHA_PIN_BANK_SIZE) { ++ u32 offset = irq + i * AIROHA_PIN_BANK_SIZE; ++ ++ generic_handle_irq(irq_find_mapping(girq->domain, ++ offset)); ++ regmap_write(pinctrl->regmap, ++ pinctrl->gpiochip.status[i], BIT(irq)); ++ } ++ handled |= !!status; ++ } ++ ++ return handled ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static const struct irq_chip airoha_gpio_irq_chip = { ++ .name = "airoha-gpio-irq", ++ .irq_unmask = airoha_irq_unmask, ++ .irq_mask = airoha_irq_mask, ++ .irq_mask_ack = airoha_irq_mask, ++ .irq_set_type = airoha_irq_type, ++ .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_IMMUTABLE, ++}; ++ ++static int airoha_pinctrl_add_gpiochip(struct airoha_pinctrl *pinctrl, ++ struct platform_device *pdev) ++{ ++ struct airoha_pinctrl_gpiochip *chip = &pinctrl->gpiochip; ++ struct gpio_chip *gc = &chip->chip; ++ struct gpio_irq_chip *girq = &gc->irq; ++ struct device *dev = &pdev->dev; ++ int irq, err; ++ ++ chip->data = gpio_data_regs; ++ chip->dir = gpio_dir_regs; ++ chip->out = gpio_out_regs; ++ chip->status = irq_status_regs; ++ chip->level = irq_level_regs; ++ chip->edge = irq_edge_regs; ++ ++ gc->parent = dev; ++ gc->label = dev_name(dev); ++ gc->request = gpiochip_generic_request; ++ gc->free = gpiochip_generic_free; ++ gc->direction_input = pinctrl_gpio_direction_input; ++ gc->direction_output = airoha_gpio_direction_output; ++ gc->set = airoha_gpio_set; ++ gc->get = airoha_gpio_get; ++ gc->base = -1; ++ gc->ngpio = AIROHA_NUM_PINS; ++ ++ girq->default_type = IRQ_TYPE_NONE; ++ girq->handler = handle_simple_irq; ++ gpio_irq_chip_set_chip(girq, &airoha_gpio_irq_chip); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ err = devm_request_irq(dev, irq, airoha_irq_handler, IRQF_SHARED, ++ dev_name(dev), pinctrl); ++ if (err) { ++ dev_err(dev, "error requesting irq %d: %d\n", irq, err); ++ return err; ++ } ++ ++ return devm_gpiochip_add_data(dev, gc, pinctrl); ++} ++ ++/* pinmux callbacks */ ++static int airoha_pinmux_set_mux(struct pinctrl_dev *pctrl_dev, ++ unsigned int selector, ++ unsigned int group) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ const struct airoha_pinctrl_func *func; ++ struct function_desc *desc; ++ struct group_desc *grp; ++ int i; ++ ++ desc = pinmux_generic_get_function(pctrl_dev, selector); ++ if (!desc) ++ return -EINVAL; ++ ++ grp = pinctrl_generic_get_group(pctrl_dev, group); ++ if (!grp) ++ return -EINVAL; ++ ++ dev_dbg(pctrl_dev->dev, "enable function %s group %s\n", ++ desc->func.name, grp->grp.name); ++ ++ func = desc->data; ++ for (i = 0; i < func->group_size; i++) { ++ const struct airoha_pinctrl_func_group *group; ++ int j; ++ ++ group = &func->groups[i]; ++ if (strcmp(group->name, grp->grp.name)) ++ continue; ++ ++ for (j = 0; j < group->regmap_size; j++) { ++ switch (group->regmap[j].mux) { ++ case AIROHA_FUNC_PWM_EXT_MUX: ++ case AIROHA_FUNC_PWM_MUX: ++ regmap_update_bits(pinctrl->regmap, ++ group->regmap[j].offset, ++ group->regmap[j].mask, ++ group->regmap[j].val); ++ break; ++ default: ++ regmap_update_bits(pinctrl->chip_scu, ++ group->regmap[j].offset, ++ group->regmap[j].mask, ++ group->regmap[j].val); ++ break; ++ } ++ } ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int airoha_pinmux_set_direction(struct pinctrl_dev *pctrl_dev, ++ struct pinctrl_gpio_range *range, ++ unsigned int p, bool input) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ u32 mask, index; ++ int err, pin; ++ ++ pin = airoha_convert_pin_to_reg_offset(pctrl_dev, range, p); ++ if (pin < 0) ++ return pin; ++ ++ /* set output enable */ ++ mask = BIT(pin % AIROHA_PIN_BANK_SIZE); ++ index = pin / AIROHA_PIN_BANK_SIZE; ++ err = regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.out[index], ++ mask, !input ? mask : 0); ++ if (err) ++ return err; ++ ++ /* set direction */ ++ mask = BIT(2 * (pin % AIROHA_REG_GPIOCTRL_NUM_PIN)); ++ index = pin / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ return regmap_update_bits(pinctrl->regmap, ++ pinctrl->gpiochip.dir[index], mask, ++ !input ? mask : 0); ++} ++ ++static const struct pinmux_ops airoha_pmxops = { ++ .get_functions_count = pinmux_generic_get_function_count, ++ .get_function_name = pinmux_generic_get_function_name, ++ .get_function_groups = pinmux_generic_get_function_groups, ++ .gpio_set_direction = airoha_pinmux_set_direction, ++ .set_mux = airoha_pinmux_set_mux, ++ .strict = true, ++}; ++ ++/* pinconf callbacks */ ++static const struct airoha_pinctrl_reg * ++airoha_pinctrl_get_conf_reg(const struct airoha_pinctrl_conf *conf, ++ int conf_size, int pin) ++{ ++ int i; ++ ++ for (i = 0; i < conf_size; i++) { ++ if (conf[i].pin == pin) ++ return &conf[i].reg; ++ } ++ ++ return NULL; ++} ++ ++static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, ++ const struct airoha_pinctrl_conf *conf, ++ int conf_size, int pin, u32 *val) ++{ ++ const struct airoha_pinctrl_reg *reg; ++ ++ reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); ++ if (!reg) ++ return -EINVAL; ++ ++ if (regmap_read(pinctrl->chip_scu, reg->offset, val)) ++ return -EINVAL; ++ ++ *val = (*val & reg->mask) >> __ffs(reg->mask); ++ ++ return 0; ++} ++ ++static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, ++ const struct airoha_pinctrl_conf *conf, ++ int conf_size, int pin, u32 val) ++{ ++ const struct airoha_pinctrl_reg *reg = NULL; ++ ++ reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); ++ if (!reg) ++ return -EINVAL; ++ ++ ++ if (regmap_update_bits(pinctrl->chip_scu, reg->offset, reg->mask, ++ val << __ffs(reg->mask))) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++#define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pullup_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pullup_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ ++ (pin), (val)) ++#define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val) \ ++ airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ ++ ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ ++ (pin), (val)) ++ ++static int airoha_pinconf_get_direction(struct pinctrl_dev *pctrl_dev, u32 p) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ u32 val, mask; ++ int err, pin; ++ u8 index; ++ ++ pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); ++ if (pin < 0) ++ return pin; ++ ++ index = pin / AIROHA_REG_GPIOCTRL_NUM_PIN; ++ err = regmap_read(pinctrl->regmap, pinctrl->gpiochip.dir[index], &val); ++ if (err) ++ return err; ++ ++ mask = BIT(2 * (pin % AIROHA_REG_GPIOCTRL_NUM_PIN)); ++ return val & mask ? PIN_CONFIG_OUTPUT_ENABLE : PIN_CONFIG_INPUT_ENABLE; ++} ++ ++static int airoha_pinconf_get(struct pinctrl_dev *pctrl_dev, ++ unsigned int pin, unsigned long *config) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ enum pin_config_param param = pinconf_to_config_param(*config); ++ u32 arg; ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ case PIN_CONFIG_BIAS_DISABLE: ++ case PIN_CONFIG_BIAS_PULL_UP: { ++ u32 pull_up, pull_down; ++ ++ if (airoha_pinctrl_get_pullup_conf(pinctrl, pin, &pull_up) || ++ airoha_pinctrl_get_pulldown_conf(pinctrl, pin, &pull_down)) ++ return -EINVAL; ++ ++ if (param == PIN_CONFIG_BIAS_PULL_UP && ++ !(pull_up && !pull_down)) ++ return -EINVAL; ++ else if (param == PIN_CONFIG_BIAS_PULL_DOWN && ++ !(pull_down && !pull_up)) ++ return -EINVAL; ++ else if (pull_up || pull_down) ++ return -EINVAL; ++ ++ arg = 1; ++ break; ++ } ++ case PIN_CONFIG_DRIVE_STRENGTH: { ++ u32 e2, e4; ++ ++ if (airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, &e2) || ++ airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, &e4)) ++ return -EINVAL; ++ ++ arg = e4 << 1 | e2; ++ break; ++ } ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ if (airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, &arg)) ++ return -EINVAL; ++ break; ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ case PIN_CONFIG_INPUT_ENABLE: ++ arg = airoha_pinconf_get_direction(pctrl_dev, pin); ++ if (arg != param) ++ return -EINVAL; ++ ++ arg = 1; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ *config = pinconf_to_config_packed(param, arg); ++ ++ return 0; ++} ++ ++static int airoha_pinconf_set_pin_value(struct pinctrl_dev *pctrl_dev, ++ unsigned int p, bool value) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ int pin; ++ ++ pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); ++ if (pin < 0) ++ return pin; ++ ++ airoha_gpio_set(&pinctrl->gpiochip.chip, pin, value); ++ ++ return 0; ++} ++ ++static int airoha_pinconf_set(struct pinctrl_dev *pctrl_dev, ++ unsigned int pin, unsigned long *configs, ++ unsigned int num_configs) ++{ ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); ++ int i; ++ ++ for (i = 0; i < num_configs; i++) { ++ u32 param = pinconf_to_config_param(configs[i]); ++ u32 arg = pinconf_to_config_argument(configs[i]); ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_DISABLE: ++ airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); ++ airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); ++ break; ++ case PIN_CONFIG_BIAS_PULL_UP: ++ airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); ++ airoha_pinctrl_set_pullup_conf(pinctrl, pin, 1); ++ break; ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 1); ++ airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); ++ break; ++ case PIN_CONFIG_DRIVE_STRENGTH: { ++ u32 e2 = 0, e4 = 0; ++ ++ switch (arg) { ++ case MTK_DRIVE_2mA: ++ break; ++ case MTK_DRIVE_4mA: ++ e2 = 1; ++ break; ++ case MTK_DRIVE_6mA: ++ e4 = 1; ++ break; ++ case MTK_DRIVE_8mA: ++ e2 = 1; ++ e4 = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, e2); ++ airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, e4); ++ break; ++ } ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, !!arg); ++ break; ++ case PIN_CONFIG_OUTPUT_ENABLE: ++ case PIN_CONFIG_INPUT_ENABLE: ++ case PIN_CONFIG_OUTPUT: { ++ bool input = param == PIN_CONFIG_INPUT_ENABLE; ++ int err; ++ ++ err = airoha_pinmux_set_direction(pctrl_dev, NULL, pin, ++ input); ++ if (err) ++ return err; ++ ++ if (param == PIN_CONFIG_OUTPUT) { ++ err = airoha_pinconf_set_pin_value(pctrl_dev, ++ pin, !!arg); ++ if (err) ++ return err; ++ } ++ break; ++ } ++ default: ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ return 0; ++} ++ ++static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, ++ unsigned int group, unsigned long *config) ++{ ++ u32 cur_config = 0; ++ int i; ++ ++ for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { ++ if (airoha_pinconf_get(pctrl_dev, ++ airoha_pinctrl_groups[group].pins[i], ++ config)) ++ return -EOPNOTSUPP; ++ ++ if (i && cur_config != *config) ++ return -EOPNOTSUPP; ++ ++ cur_config = *config; ++ } ++ ++ return 0; ++} ++ ++static int airoha_pinconf_group_set(struct pinctrl_dev *pctrl_dev, ++ unsigned int group, unsigned long *configs, ++ unsigned int num_configs) ++{ ++ int i; ++ ++ for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { ++ int err; ++ ++ err = airoha_pinconf_set(pctrl_dev, ++ airoha_pinctrl_groups[group].pins[i], ++ configs, num_configs); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static const struct pinconf_ops airoha_confops = { ++ .is_generic = true, ++ .pin_config_get = airoha_pinconf_get, ++ .pin_config_set = airoha_pinconf_set, ++ .pin_config_group_get = airoha_pinconf_group_get, ++ .pin_config_group_set = airoha_pinconf_group_set, ++ .pin_config_config_dbg_show = pinconf_generic_dump_config, ++}; ++ ++static const struct pinctrl_ops airoha_pctlops = { ++ .get_groups_count = pinctrl_generic_get_group_count, ++ .get_group_name = pinctrl_generic_get_group_name, ++ .get_group_pins = pinctrl_generic_get_group_pins, ++ .dt_node_to_map = pinconf_generic_dt_node_to_map_all, ++ .dt_free_map = pinconf_generic_dt_free_map, ++}; ++ ++static struct pinctrl_desc airoha_pinctrl_desc = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .pctlops = &airoha_pctlops, ++ .pmxops = &airoha_pmxops, ++ .confops = &airoha_confops, ++ .pins = airoha_pinctrl_pins, ++ .npins = ARRAY_SIZE(airoha_pinctrl_pins), ++}; ++ ++static int airoha_pinctrl_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_pinctrl *pinctrl; ++ struct regmap *map; ++ int err, i; ++ ++ pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); ++ if (!pinctrl) ++ return -ENOMEM; ++ ++ pinctrl->regmap = device_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(pinctrl->regmap)) ++ return PTR_ERR(pinctrl->regmap); ++ ++ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); ++ ++ pinctrl->chip_scu = map; ++ ++ err = devm_pinctrl_register_and_init(dev, &airoha_pinctrl_desc, ++ pinctrl, &pinctrl->ctrl); ++ if (err) ++ return err; ++ ++ /* build pin groups */ ++ for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_groups); i++) { ++ const struct pingroup *grp = &airoha_pinctrl_groups[i]; ++ ++ err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name, ++ grp->pins, grp->npins, ++ (void *)grp); ++ if (err < 0) { ++ dev_err(&pdev->dev, "Failed to register group %s\n", ++ grp->name); ++ return err; ++ } ++ } ++ ++ /* build functions */ ++ for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_funcs); i++) { ++ const struct airoha_pinctrl_func *func; ++ ++ func = &airoha_pinctrl_funcs[i]; ++ err = pinmux_generic_add_function(pinctrl->ctrl, ++ func->desc.func.name, ++ func->desc.func.groups, ++ func->desc.func.ngroups, ++ (void *)func); ++ if (err < 0) { ++ dev_err(dev, "Failed to register function %s\n", ++ func->desc.func.name); ++ return err; ++ } ++ } ++ ++ err = pinctrl_enable(pinctrl->ctrl); ++ if (err) ++ return err; ++ ++ /* build gpio-chip */ ++ return airoha_pinctrl_add_gpiochip(pinctrl, pdev); ++} ++ ++static const struct of_device_id airoha_pinctrl_of_match[] = { ++ { .compatible = "airoha,en7581-pinctrl" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_pinctrl_of_match); ++ ++static struct platform_driver airoha_pinctrl_driver = { ++ .probe = airoha_pinctrl_probe, ++ .driver = { ++ .name = "pinctrl-airoha", ++ .of_match_table = airoha_pinctrl_of_match, ++ }, ++}; ++module_platform_driver(airoha_pinctrl_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_AUTHOR("Benjamin Larsson "); ++MODULE_AUTHOR("Markus Gothe "); ++MODULE_DESCRIPTION("Pinctrl driver for Airoha SoC"); diff --git a/target/linux/airoha/patches-6.12/034-02-v6.13-pinctrl-airoha-Use-unsigned-long-for-bit-search.patch b/target/linux/airoha/patches-6.12/034-02-v6.13-pinctrl-airoha-Use-unsigned-long-for-bit-search.patch new file mode 100644 index 00000000000..62fae6aa38c --- /dev/null +++ b/target/linux/airoha/patches-6.12/034-02-v6.13-pinctrl-airoha-Use-unsigned-long-for-bit-search.patch @@ -0,0 +1,61 @@ +From ac6f0825e582f2216a582c9edf0cee7bfe347ba6 Mon Sep 17 00:00:00 2001 +From: Kees Cook +Date: Sun, 17 Nov 2024 03:45:38 -0800 +Subject: [PATCH] pinctrl: airoha: Use unsigned long for bit search + +Instead of risking alignment problems and causing (false positive) array +bound warnings when casting a u32 to (64-bit) unsigned long, just use a +native unsigned long for doing bit searches. Avoids warning with GCC 15's +-Warray-bounds -fdiagnostics-details: + +In file included from ../include/linux/bitmap.h:11, + from ../include/linux/cpumask.h:12, + from ../arch/x86/include/asm/paravirt.h:21, + from ../arch/x86/include/asm/irqflags.h:80, + from ../include/linux/irqflags.h:18, + from ../include/linux/spinlock.h:59, + from ../include/linux/irq.h:14, + from ../include/linux/irqchip/chained_irq.h:10, + from ../include/linux/gpio/driver.h:8, + from ../drivers/pinctrl/mediatek/pinctrl-airoha.c:11: +In function 'find_next_bit', + inlined from 'airoha_irq_handler' at ../drivers/pinctrl/mediatek/pinctrl-airoha.c:2394:3: +../include/linux/find.h:65:23: error: array subscript 'long unsigned int[0]' is partly outside array bounds of 'u32[1]' {aka 'unsigned int[1]'} [-Werror=array-bounds=] + 65 | val = *addr & GENMASK(size - 1, offset); + | ^~~~~ +../drivers/pinctrl/mediatek/pinctrl-airoha.c: In function 'airoha_irq_handler': +../drivers/pinctrl/mediatek/pinctrl-airoha.c:2387:21: note: object 'status' of size 4 + 2387 | u32 status; + | ^~~~~~ + +Signed-off-by: Kees Cook +Reviewed-by: AngeloGioacchino Del Regno +Link: https://lore.kernel.org/20241117114534.work.292-kees@kernel.org +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -2384,15 +2384,16 @@ static irqreturn_t airoha_irq_handler(in + + for (i = 0; i < ARRAY_SIZE(irq_status_regs); i++) { + struct gpio_irq_chip *girq = &pinctrl->gpiochip.chip.irq; +- u32 status; ++ u32 regmap; ++ unsigned long status; + int irq; + + if (regmap_read(pinctrl->regmap, pinctrl->gpiochip.status[i], +- &status)) ++ ®map)) + continue; + +- for_each_set_bit(irq, (unsigned long *)&status, +- AIROHA_PIN_BANK_SIZE) { ++ status = regmap; ++ for_each_set_bit(irq, &status, AIROHA_PIN_BANK_SIZE) { + u32 offset = irq + i * AIROHA_PIN_BANK_SIZE; + + generic_handle_irq(irq_find_mapping(girq->domain, diff --git a/target/linux/airoha/patches-6.12/036-v6.13-net-airoha-Fix-typo-in-REG_CDM2_FWD_CFG-configuratio.patch b/target/linux/airoha/patches-6.12/036-v6.13-net-airoha-Fix-typo-in-REG_CDM2_FWD_CFG-configuratio.patch new file mode 100644 index 00000000000..a71197150f4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/036-v6.13-net-airoha-Fix-typo-in-REG_CDM2_FWD_CFG-configuratio.patch @@ -0,0 +1,35 @@ +From 30d9d8f6a2d7e44a9f91737dd409dbc87ac6f6b7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 15 Oct 2024 09:58:09 +0200 +Subject: [PATCH] net: airoha: Fix typo in REG_CDM2_FWD_CFG configuration + +Fix typo in airoha_fe_init routine configuring CDM2_OAM_QSEL_MASK field +of REG_CDM2_FWD_CFG register. +This bug is not introducing any user visible problem since Frame Engine +CDM2 port is used just by the second QDMA block and we currently enable +just QDMA1 block connected to the MT7530 dsa switch via CDM1 port. + +Introduced by commit 23020f049327 ("net: airoha: Introduce ethernet +support for EN7581 SoC") + +Reported-by: ChihWei Cheng +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Message-ID: <20241015-airoha-eth-cdm2-fixes-v1-1-9dc6993286c3@kernel.org> +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1369,7 +1369,8 @@ static int airoha_fe_init(struct airoha_ + airoha_fe_set(eth, REG_GDM_MISC_CFG, + GDM2_RDM_ACK_WAIT_PREF_MASK | + GDM2_CHN_VLD_MODE_MASK); +- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, 15); ++ airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, ++ FIELD_PREP(CDM2_OAM_QSEL_MASK, 15)); + + /* init fragment and assemble Force Port */ + /* NPU Core-3, NPU Bridge Channel-3 */ diff --git a/target/linux/airoha/patches-6.12/038-01-v6.14-net-airoha-Enable-Tx-drop-capability-for-each-Tx-DMA.patch b/target/linux/airoha/patches-6.12/038-01-v6.14-net-airoha-Enable-Tx-drop-capability-for-each-Tx-DMA.patch new file mode 100644 index 00000000000..27acb76b1e9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/038-01-v6.14-net-airoha-Enable-Tx-drop-capability-for-each-Tx-DMA.patch @@ -0,0 +1,27 @@ +From 5f795590380476f1c9b7ed0ac945c9b0269dc23a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 3 Jan 2025 13:17:02 +0100 +Subject: [PATCH 1/4] net: airoha: Enable Tx drop capability for each Tx DMA + ring + +This is a preliminary patch in order to enable hw Qdisc offloading. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -1810,6 +1810,10 @@ static int airoha_qdma_init_tx_queue(str + WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); + } + ++ /* xmit ring drop default setting */ ++ airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), ++ TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK); ++ + airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); diff --git a/target/linux/airoha/patches-6.12/038-02-v6.14-net-airoha-Introduce-ndo_select_queue-callback.patch b/target/linux/airoha/patches-6.12/038-02-v6.14-net-airoha-Introduce-ndo_select_queue-callback.patch new file mode 100644 index 00000000000..3110777ed94 --- /dev/null +++ b/target/linux/airoha/patches-6.12/038-02-v6.14-net-airoha-Introduce-ndo_select_queue-callback.patch @@ -0,0 +1,86 @@ +From 2b288b81560b94958cd68bbe54673e55a1730c95 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 3 Jan 2025 13:17:03 +0100 +Subject: [PATCH 2/4] net: airoha: Introduce ndo_select_queue callback + +Airoha EN7581 SoC supports 32 Tx DMA rings used to feed packets to QoS +channels. Each channels supports 8 QoS queues where the user can apply +QoS scheduling policies. In a similar way, the user can configure hw +rate shaping for each QoS channel. +Introduce ndo_select_queue callback in order to select the tx queue +based on QoS channel and QoS queue. In particular, for dsa device select +QoS channel according to the dsa user port index, rely on port id +otherwise. Select QoS queue based on the skb priority. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 30 ++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -23,6 +23,8 @@ + #define AIROHA_MAX_NUM_XSI_RSTS 5 + #define AIROHA_MAX_MTU 2000 + #define AIROHA_MAX_PACKET_SIZE 2048 ++#define AIROHA_NUM_QOS_CHANNELS 4 ++#define AIROHA_NUM_QOS_QUEUES 8 + #define AIROHA_NUM_TX_RING 32 + #define AIROHA_NUM_RX_RING 32 + #define AIROHA_FE_MC_MAX_VLAN_TABLE 64 +@@ -2442,21 +2444,44 @@ static void airoha_dev_get_stats64(struc + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + ++static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, ++ struct net_device *sb_dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ int queue, channel; ++ ++ /* For dsa device select QoS channel according to the dsa user port ++ * index, rely on port id otherwise. Select QoS queue based on the ++ * skb priority. ++ */ ++ channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; ++ channel = channel % AIROHA_NUM_QOS_CHANNELS; ++ queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ ++ queue = channel * AIROHA_NUM_QOS_QUEUES + queue; ++ ++ return queue < dev->num_tx_queues ? queue : 0; ++} ++ + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, + struct net_device *dev) + { + struct skb_shared_info *sinfo = skb_shinfo(skb); + struct airoha_gdm_port *port = netdev_priv(dev); +- u32 msg0 = 0, msg1, len = skb_headlen(skb); +- int i, qid = skb_get_queue_mapping(skb); ++ u32 msg0, msg1, len = skb_headlen(skb); + struct airoha_qdma *qdma = port->qdma; + u32 nr_frags = 1 + sinfo->nr_frags; + struct netdev_queue *txq; + struct airoha_queue *q; + void *data = skb->data; ++ int i, qid; + u16 index; + u8 fport; + ++ qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); ++ msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, ++ qid / AIROHA_NUM_QOS_QUEUES) | ++ FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK, ++ qid % AIROHA_NUM_QOS_QUEUES); + if (skb->ip_summed == CHECKSUM_PARTIAL) + msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) | + FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) | +@@ -2630,6 +2655,7 @@ static const struct net_device_ops airoh + .ndo_init = airoha_dev_init, + .ndo_open = airoha_dev_open, + .ndo_stop = airoha_dev_stop, ++ .ndo_select_queue = airoha_dev_select_queue, + .ndo_start_xmit = airoha_dev_xmit, + .ndo_get_stats64 = airoha_dev_get_stats64, + .ndo_set_mac_address = airoha_dev_set_macaddr, diff --git a/target/linux/airoha/patches-6.12/038-03-v6.14-net-airoha-Add-sched-ETS-offload-support.patch b/target/linux/airoha/patches-6.12/038-03-v6.14-net-airoha-Add-sched-ETS-offload-support.patch new file mode 100644 index 00000000000..aa4b0514659 --- /dev/null +++ b/target/linux/airoha/patches-6.12/038-03-v6.14-net-airoha-Add-sched-ETS-offload-support.patch @@ -0,0 +1,292 @@ +From 20bf7d07c956e5c7a22d3076c599cbb7a6054917 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 3 Jan 2025 13:17:04 +0100 +Subject: [PATCH 3/4] net: airoha: Add sched ETS offload support + +Introduce support for ETS Qdisc offload available on the Airoha EN7581 +ethernet controller. In order to be effective, ETS Qdisc must configured +as leaf of a HTB Qdisc (HTB Qdisc offload will be added in the following +patch). ETS Qdisc available on EN7581 ethernet controller supports at +most 8 concurrent bands (QoS queues). We can enable an ETS Qdisc for +each available QoS channel. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 196 ++++++++++++++++++++- + 1 file changed, 195 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + + #define AIROHA_MAX_NUM_GDM_PORTS 1 +@@ -543,9 +544,24 @@ + #define INGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) + #define INGRESS_FAST_TICK_MASK GENMASK(15, 0) + ++#define REG_QUEUE_CLOSE_CFG(_n) (0x00a0 + ((_n) & 0xfc)) ++#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) BIT((_m) + (((_n) & 0x3) << 3)) ++ + #define REG_TXQ_DIS_CFG_BASE(_n) ((_n) ? 0x20a0 : 0x00a0) + #define REG_TXQ_DIS_CFG(_n, _m) (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2) + ++#define REG_CNTR_CFG(_n) (0x0400 + ((_n) << 3)) ++#define CNTR_EN_MASK BIT(31) ++#define CNTR_ALL_CHAN_EN_MASK BIT(30) ++#define CNTR_ALL_QUEUE_EN_MASK BIT(29) ++#define CNTR_ALL_DSCP_RING_EN_MASK BIT(28) ++#define CNTR_SRC_MASK GENMASK(27, 24) ++#define CNTR_DSCP_RING_MASK GENMASK(20, 16) ++#define CNTR_CHAN_MASK GENMASK(7, 3) ++#define CNTR_QUEUE_MASK GENMASK(2, 0) ++ ++#define REG_CNTR_VAL(_n) (0x0404 + ((_n) << 3)) ++ + #define REG_LMGR_INIT_CFG 0x1000 + #define LMGR_INIT_START BIT(31) + #define LMGR_SRAM_MODE_MASK BIT(30) +@@ -571,9 +587,19 @@ + #define TWRR_WEIGHT_SCALE_MASK BIT(31) + #define TWRR_WEIGHT_BASE_MASK BIT(3) + ++#define REG_TXWRR_WEIGHT_CFG 0x1024 ++#define TWRR_RW_CMD_MASK BIT(31) ++#define TWRR_RW_CMD_DONE BIT(30) ++#define TWRR_CHAN_IDX_MASK GENMASK(23, 19) ++#define TWRR_QUEUE_IDX_MASK GENMASK(18, 16) ++#define TWRR_VALUE_MASK GENMASK(15, 0) ++ + #define REG_PSE_BUF_USAGE_CFG 0x1028 + #define PSE_BUF_ESTIMATE_EN_MASK BIT(29) + ++#define REG_CHAN_QOS_MODE(_n) (0x1040 + ((_n) << 2)) ++#define CHAN_QOS_MODE_MASK(_n) GENMASK(2 + ((_n) << 2), (_n) << 2) ++ + #define REG_GLB_TRTCM_CFG 0x1080 + #define GLB_TRTCM_EN_MASK BIT(31) + #define GLB_TRTCM_MODE_MASK BIT(30) +@@ -722,6 +748,17 @@ enum { + FE_PSE_PORT_DROP = 0xf, + }; + ++enum tx_sched_mode { ++ TC_SCH_WRR8, ++ TC_SCH_SP, ++ TC_SCH_WRR7, ++ TC_SCH_WRR6, ++ TC_SCH_WRR5, ++ TC_SCH_WRR4, ++ TC_SCH_WRR3, ++ TC_SCH_WRR2, ++}; ++ + struct airoha_queue_entry { + union { + void *buf; +@@ -812,6 +849,10 @@ struct airoha_gdm_port { + int id; + + struct airoha_hw_stats stats; ++ ++ /* qos stats counters */ ++ u64 cpu_tx_packets; ++ u64 fwd_tx_packets; + }; + + struct airoha_eth { +@@ -1982,6 +2023,27 @@ static void airoha_qdma_init_qos(struct + FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); + } + ++static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_NUM_QOS_CHANNELS; i++) { ++ /* Tx-cpu transferred count */ ++ airoha_qdma_wr(qdma, REG_CNTR_VAL(i << 1), 0); ++ airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), ++ CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | ++ CNTR_ALL_DSCP_RING_EN_MASK | ++ FIELD_PREP(CNTR_CHAN_MASK, i)); ++ /* Tx-fwd transferred count */ ++ airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); ++ airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), ++ CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | ++ CNTR_ALL_DSCP_RING_EN_MASK | ++ FIELD_PREP(CNTR_SRC_MASK, 1) | ++ FIELD_PREP(CNTR_CHAN_MASK, i)); ++ } ++} ++ + static int airoha_qdma_hw_init(struct airoha_qdma *qdma) + { + int i; +@@ -2032,6 +2094,7 @@ static int airoha_qdma_hw_init(struct ai + + airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG, + TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN); ++ airoha_qdma_init_qos_stats(qdma); + + return 0; + } +@@ -2651,6 +2714,135 @@ airoha_ethtool_get_rmon_stats(struct net + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + ++static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port, ++ int channel, enum tx_sched_mode mode, ++ const u16 *weights, u8 n_weights) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_NUM_TX_RING; i++) ++ airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel), ++ TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); ++ ++ for (i = 0; i < n_weights; i++) { ++ u32 status; ++ int err; ++ ++ airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG, ++ TWRR_RW_CMD_MASK | ++ FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | ++ FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | ++ FIELD_PREP(TWRR_VALUE_MASK, weights[i])); ++ err = read_poll_timeout(airoha_qdma_rr, status, ++ status & TWRR_RW_CMD_DONE, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, ++ true, port->qdma, ++ REG_TXWRR_WEIGHT_CFG); ++ if (err) ++ return err; ++ } ++ ++ airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), ++ CHAN_QOS_MODE_MASK(channel), ++ mode << __ffs(CHAN_QOS_MODE_MASK(channel))); ++ ++ return 0; ++} ++ ++static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port, ++ int channel) ++{ ++ static const u16 w[AIROHA_NUM_QOS_QUEUES] = {}; ++ ++ return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w, ++ ARRAY_SIZE(w)); ++} ++ ++static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port, ++ int channel, ++ struct tc_ets_qopt_offload *opt) ++{ ++ struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; ++ enum tx_sched_mode mode = TC_SCH_SP; ++ u16 w[AIROHA_NUM_QOS_QUEUES] = {}; ++ int i, nstrict = 0; ++ ++ if (p->bands > AIROHA_NUM_QOS_QUEUES) ++ return -EINVAL; ++ ++ for (i = 0; i < p->bands; i++) { ++ if (!p->quanta[i]) ++ nstrict++; ++ } ++ ++ /* this configuration is not supported by the hw */ ++ if (nstrict == AIROHA_NUM_QOS_QUEUES - 1) ++ return -EINVAL; ++ ++ for (i = 0; i < p->bands - nstrict; i++) ++ w[i] = p->weights[nstrict + i]; ++ ++ if (!nstrict) ++ mode = TC_SCH_WRR8; ++ else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1) ++ mode = nstrict + 1; ++ ++ return airoha_qdma_set_chan_tx_sched(port, channel, mode, w, ++ ARRAY_SIZE(w)); ++} ++ ++static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port, ++ int channel, ++ struct tc_ets_qopt_offload *opt) ++{ ++ u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, ++ REG_CNTR_VAL(channel << 1)); ++ u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, ++ REG_CNTR_VAL((channel << 1) + 1)); ++ u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + ++ (fwd_tx_packets - port->fwd_tx_packets); ++ _bstats_update(opt->stats.bstats, 0, tx_packets); ++ ++ port->cpu_tx_packets = cpu_tx_packets; ++ port->fwd_tx_packets = fwd_tx_packets; ++ ++ return 0; ++} ++ ++static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, ++ struct tc_ets_qopt_offload *opt) ++{ ++ int channel = TC_H_MAJ(opt->handle) >> 16; ++ ++ if (opt->parent == TC_H_ROOT) ++ return -EINVAL; ++ ++ switch (opt->command) { ++ case TC_ETS_REPLACE: ++ return airoha_qdma_set_tx_ets_sched(port, channel, opt); ++ case TC_ETS_DESTROY: ++ /* PRIO is default qdisc scheduler */ ++ return airoha_qdma_set_tx_prio_sched(port, channel); ++ case TC_ETS_STATS: ++ return airoha_qdma_get_tx_ets_stats(port, channel, opt); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, ++ void *type_data) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ ++ switch (type) { ++ case TC_SETUP_QDISC_ETS: ++ return airoha_tc_setup_qdisc_ets(port, type_data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ + static const struct net_device_ops airoha_netdev_ops = { + .ndo_init = airoha_dev_init, + .ndo_open = airoha_dev_open, +@@ -2659,6 +2851,7 @@ static const struct net_device_ops airoh + .ndo_start_xmit = airoha_dev_xmit, + .ndo_get_stats64 = airoha_dev_get_stats64, + .ndo_set_mac_address = airoha_dev_set_macaddr, ++ .ndo_setup_tc = airoha_dev_tc_setup, + }; + + static const struct ethtool_ops airoha_ethtool_ops = { +@@ -2708,7 +2901,8 @@ static int airoha_alloc_gdm_port(struct + dev->watchdog_timeo = 5 * HZ; + dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | + NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | +- NETIF_F_SG | NETIF_F_TSO; ++ NETIF_F_SG | NETIF_F_TSO | ++ NETIF_F_HW_TC; + dev->features |= dev->hw_features; + dev->dev.of_node = np; + dev->irq = qdma->irq; diff --git a/target/linux/airoha/patches-6.12/038-04-v6.14-net-airoha-Add-sched-HTB-offload-support.patch b/target/linux/airoha/patches-6.12/038-04-v6.14-net-airoha-Add-sched-HTB-offload-support.patch new file mode 100644 index 00000000000..405f095d590 --- /dev/null +++ b/target/linux/airoha/patches-6.12/038-04-v6.14-net-airoha-Add-sched-HTB-offload-support.patch @@ -0,0 +1,371 @@ +From ef1ca9271313b4ea7b03de69576aacef1e78f381 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 3 Jan 2025 13:17:05 +0100 +Subject: [PATCH 4/4] net: airoha: Add sched HTB offload support + +Introduce support for HTB Qdisc offload available in the Airoha EN7581 +ethernet controller. EN7581 can offload only one level of HTB leafs. +Each HTB leaf represents a QoS channel supported by EN7581 SoC. +The typical use-case is creating a HTB leaf for QoS channel to rate +limit the egress traffic and attach an ETS Qdisc to each HTB leaf in +order to enforce traffic prioritization. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 288 ++++++++++++++++++++- + 1 file changed, 287 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -28,6 +28,8 @@ + #define AIROHA_NUM_QOS_QUEUES 8 + #define AIROHA_NUM_TX_RING 32 + #define AIROHA_NUM_RX_RING 32 ++#define AIROHA_NUM_NETDEV_TX_RINGS (AIROHA_NUM_TX_RING + \ ++ AIROHA_NUM_QOS_CHANNELS) + #define AIROHA_FE_MC_MAX_VLAN_TABLE 64 + #define AIROHA_FE_MC_MAX_VLAN_PORT 16 + #define AIROHA_NUM_TX_IRQ 2 +@@ -43,6 +45,9 @@ + #define PSE_RSV_PAGES 128 + #define PSE_QUEUE_RSV_PAGES 64 + ++#define QDMA_METER_IDX(_n) ((_n) & 0xff) ++#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) ++ + /* FE */ + #define PSE_BASE 0x0100 + #define CSR_IFC_BASE 0x0200 +@@ -583,6 +588,17 @@ + #define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) + #define EGRESS_FAST_TICK_MASK GENMASK(15, 0) + ++#define TRTCM_PARAM_RW_MASK BIT(31) ++#define TRTCM_PARAM_RW_DONE_MASK BIT(30) ++#define TRTCM_PARAM_TYPE_MASK GENMASK(29, 28) ++#define TRTCM_METER_GROUP_MASK GENMASK(27, 26) ++#define TRTCM_PARAM_INDEX_MASK GENMASK(23, 17) ++#define TRTCM_PARAM_RATE_TYPE_MASK BIT(16) ++ ++#define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4) ++#define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) ++#define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) ++ + #define REG_TXWRR_MODE_CFG 0x1020 + #define TWRR_WEIGHT_SCALE_MASK BIT(31) + #define TWRR_WEIGHT_BASE_MASK BIT(3) +@@ -759,6 +775,29 @@ enum tx_sched_mode { + TC_SCH_WRR2, + }; + ++enum trtcm_param_type { ++ TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ ++ TRTCM_TOKEN_RATE_MODE, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, ++ TRTCM_BUCKET_COUNTER_MODE, ++}; ++ ++enum trtcm_mode_type { ++ TRTCM_COMMIT_MODE, ++ TRTCM_PEAK_MODE, ++}; ++ ++enum trtcm_param { ++ TRTCM_TICK_SEL = BIT(0), ++ TRTCM_PKT_MODE = BIT(1), ++ TRTCM_METER_MODE = BIT(2), ++}; ++ ++#define MIN_TOKEN_SIZE 4096 ++#define MAX_TOKEN_SIZE_OFFSET 17 ++#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6) ++#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) ++ + struct airoha_queue_entry { + union { + void *buf; +@@ -850,6 +889,8 @@ struct airoha_gdm_port { + + struct airoha_hw_stats stats; + ++ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); ++ + /* qos stats counters */ + u64 cpu_tx_packets; + u64 fwd_tx_packets; +@@ -2830,6 +2871,243 @@ static int airoha_tc_setup_qdisc_ets(str + } + } + ++static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, ++ u32 addr, enum trtcm_param_type param, ++ enum trtcm_mode_type mode, ++ u32 *val_low, u32 *val_high) ++{ ++ u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); ++ u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | ++ FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | ++ FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ if (read_poll_timeout(airoha_qdma_rr, val, ++ val & TRTCM_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, ++ qdma, REG_TRTCM_CFG_PARAM(addr))) ++ return -ETIMEDOUT; ++ ++ *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); ++ if (val_high) ++ *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel, ++ u32 addr, enum trtcm_param_type param, ++ enum trtcm_mode_type mode, u32 val) ++{ ++ u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); ++ u32 config = TRTCM_PARAM_RW_MASK | ++ FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | ++ FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | ++ FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ ++ return read_poll_timeout(airoha_qdma_rr, val, ++ val & TRTCM_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, ++ qdma, REG_TRTCM_CFG_PARAM(addr)); ++} ++ ++static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel, ++ u32 addr, enum trtcm_mode_type mode, ++ bool enable, u32 enable_mask) ++{ ++ u32 val; ++ ++ if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, ++ mode, &val, NULL)) ++ return -EINVAL; ++ ++ val = enable ? val | enable_mask : val & ~enable_mask; ++ ++ return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, ++ mode, val); ++} ++ ++static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma, ++ int channel, u32 addr, ++ enum trtcm_mode_type mode, ++ u32 rate_val, u32 bucket_size) ++{ ++ u32 val, config, tick, unit, rate, rate_frac; ++ int err; ++ ++ if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, ++ mode, &config, NULL)) ++ return -EINVAL; ++ ++ val = airoha_qdma_rr(qdma, addr); ++ tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); ++ if (config & TRTCM_TICK_SEL) ++ tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); ++ if (!tick) ++ return -EINVAL; ++ ++ unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; ++ if (!unit) ++ return -EINVAL; ++ ++ rate = rate_val / unit; ++ rate_frac = rate_val % unit; ++ rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; ++ rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | ++ FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); ++ ++ err = airoha_qdma_set_trtcm_param(qdma, channel, addr, ++ TRTCM_TOKEN_RATE_MODE, mode, rate); ++ if (err) ++ return err; ++ ++ val = max_t(u32, bucket_size, MIN_TOKEN_SIZE); ++ val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); ++ ++ return airoha_qdma_set_trtcm_param(qdma, channel, addr, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, ++ mode, val); ++} ++ ++static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port, ++ int channel, u32 rate, ++ u32 bucket_size) ++{ ++ int i, err; ++ ++ for (i = 0; i <= TRTCM_PEAK_MODE; i++) { ++ err = airoha_qdma_set_trtcm_config(port->qdma, channel, ++ REG_EGRESS_TRTCM_CFG, i, ++ !!rate, TRTCM_METER_MODE); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel, ++ REG_EGRESS_TRTCM_CFG, ++ i, rate, bucket_size); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ ++ struct net_device *dev = port->dev; ++ int num_tx_queues = dev->real_num_tx_queues; ++ int err; ++ ++ if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { ++ NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); ++ return -EINVAL; ++ } ++ ++ err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum); ++ if (err) { ++ NL_SET_ERR_MSG_MOD(opt->extack, ++ "failed configuring htb offload"); ++ return err; ++ } ++ ++ if (opt->command == TC_HTB_NODE_MODIFY) ++ return 0; ++ ++ err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); ++ if (err) { ++ airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum); ++ NL_SET_ERR_MSG_MOD(opt->extack, ++ "failed setting real_num_tx_queues"); ++ return err; ++ } ++ ++ set_bit(channel, port->qos_sq_bmap); ++ opt->qid = AIROHA_NUM_TX_RING + channel; ++ ++ return 0; ++} ++ ++static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue) ++{ ++ struct net_device *dev = port->dev; ++ ++ netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); ++ airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0); ++ clear_bit(queue, port->qos_sq_bmap); ++} ++ ++static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ ++ if (!test_bit(channel, port->qos_sq_bmap)) { ++ NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); ++ return -EINVAL; ++ } ++ ++ airoha_tc_remove_htb_queue(port, channel); ++ ++ return 0; ++} ++ ++static int airoha_tc_htb_destroy(struct airoha_gdm_port *port) ++{ ++ int q; ++ ++ for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) ++ airoha_tc_remove_htb_queue(port, q); ++ ++ return 0; ++} ++ ++static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ ++ if (!test_bit(channel, port->qos_sq_bmap)) { ++ NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); ++ return -EINVAL; ++ } ++ ++ opt->qid = channel; ++ ++ return 0; ++} ++ ++static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ switch (opt->command) { ++ case TC_HTB_CREATE: ++ break; ++ case TC_HTB_DESTROY: ++ return airoha_tc_htb_destroy(port); ++ case TC_HTB_NODE_MODIFY: ++ case TC_HTB_LEAF_ALLOC_QUEUE: ++ return airoha_tc_htb_alloc_leaf_queue(port, opt); ++ case TC_HTB_LEAF_DEL: ++ case TC_HTB_LEAF_DEL_LAST: ++ case TC_HTB_LEAF_DEL_LAST_FORCE: ++ return airoha_tc_htb_delete_leaf_queue(port, opt); ++ case TC_HTB_LEAF_QUERY_QUEUE: ++ return airoha_tc_get_htb_get_leaf_queue(port, opt); ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ + static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, + void *type_data) + { +@@ -2838,6 +3116,8 @@ static int airoha_dev_tc_setup(struct ne + switch (type) { + case TC_SETUP_QDISC_ETS: + return airoha_tc_setup_qdisc_ets(port, type_data); ++ case TC_SETUP_QDISC_HTB: ++ return airoha_tc_setup_qdisc_htb(port, type_data); + default: + return -EOPNOTSUPP; + } +@@ -2888,7 +3168,8 @@ static int airoha_alloc_gdm_port(struct + } + + dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), +- AIROHA_NUM_TX_RING, AIROHA_NUM_RX_RING); ++ AIROHA_NUM_NETDEV_TX_RINGS, ++ AIROHA_NUM_RX_RING); + if (!dev) { + dev_err(eth->dev, "alloc_etherdev failed\n"); + return -ENOMEM; +@@ -2908,6 +3189,11 @@ static int airoha_alloc_gdm_port(struct + dev->irq = qdma->irq; + SET_NETDEV_DEV(dev, eth->dev); + ++ /* reserve hw queues for HTB offloading */ ++ err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING); ++ if (err) ++ return err; ++ + err = of_get_ethdev_address(np, dev); + if (err) { + if (err == -EPROBE_DEFER) diff --git a/target/linux/airoha/patches-6.12/039-v6.14-cpufreq-airoha-Add-EN7581-CPUFreq-SMCCC-driver.patch b/target/linux/airoha/patches-6.12/039-v6.14-cpufreq-airoha-Add-EN7581-CPUFreq-SMCCC-driver.patch new file mode 100644 index 00000000000..96d7f66da9e --- /dev/null +++ b/target/linux/airoha/patches-6.12/039-v6.14-cpufreq-airoha-Add-EN7581-CPUFreq-SMCCC-driver.patch @@ -0,0 +1,232 @@ +From 84cf9e541cccb8cb698518a9897942e8c78f1d83 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 9 Jan 2025 14:12:58 +0100 +Subject: [PATCH] cpufreq: airoha: Add EN7581 CPUFreq SMCCC driver + +Add simple CPU Freq driver for Airoha EN7581 SoC that control CPU +frequency scaling with SMC APIs and register a generic "cpufreq-dt" +device. + +All CPU share the same frequency and can't be controlled independently. +CPU frequency is controlled by the attached PM domain. + +Add SoC compatible to cpufreq-dt-plat block list as a dedicated cpufreq +driver is needed with OPP v2 nodes declared in DTS. + +Signed-off-by: Christian Marangi +Signed-off-by: Viresh Kumar +--- + drivers/cpufreq/Kconfig.arm | 8 ++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/airoha-cpufreq.c | 152 +++++++++++++++++++++++++++ + drivers/cpufreq/cpufreq-dt-platdev.c | 2 + + 4 files changed, 163 insertions(+) + create mode 100644 drivers/cpufreq/airoha-cpufreq.c + +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -15,6 +15,14 @@ config ARM_ALLWINNER_SUN50I_CPUFREQ_NVME + To compile this driver as a module, choose M here: the + module will be called sun50i-cpufreq-nvmem. + ++config ARM_AIROHA_SOC_CPUFREQ ++ tristate "Airoha EN7581 SoC CPUFreq support" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ select PM_OPP ++ default ARCH_AIROHA ++ help ++ This adds the CPUFreq driver for Airoha EN7581 SoCs. ++ + config ARM_APPLE_SOC_CPUFREQ + tristate "Apple Silicon SoC CPUFreq support" + depends on ARCH_APPLE || (COMPILE_TEST && 64BIT) +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) + + + ################################################################################## + # ARM SoC drivers ++obj-$(CONFIG_ARM_AIROHA_SOC_CPUFREQ) += airoha-cpufreq.o + obj-$(CONFIG_ARM_APPLE_SOC_CPUFREQ) += apple-soc-cpufreq.o + obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o + obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o +--- /dev/null ++++ b/drivers/cpufreq/airoha-cpufreq.c +@@ -0,0 +1,166 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpufreq-dt.h" ++ ++struct airoha_cpufreq_priv { ++ int opp_token; ++ struct device **virt_devs; ++ struct platform_device *cpufreq_dt; ++}; ++ ++static struct platform_device *cpufreq_pdev; ++ ++/* NOP function to disable OPP from setting clock */ ++static int airoha_cpufreq_config_clks_nop(struct device *dev, ++ struct opp_table *opp_table, ++ struct dev_pm_opp *old_opp, ++ struct dev_pm_opp *opp, ++ void *data, bool scaling_down) ++{ ++ return 0; ++} ++ ++static const char * const airoha_cpufreq_clk_names[] = { "cpu", NULL }; ++static const char * const airoha_cpufreq_pd_names[] = { "perf", NULL }; ++ ++static int airoha_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct dev_pm_opp_config config = { ++ .clk_names = airoha_cpufreq_clk_names, ++ .config_clks = airoha_cpufreq_config_clks_nop, ++ .genpd_names = airoha_cpufreq_pd_names, ++ }; ++ struct platform_device *cpufreq_dt; ++ struct airoha_cpufreq_priv *priv; ++ struct device *dev = &pdev->dev; ++ struct device **virt_devs = NULL; ++ struct device *cpu_dev; ++ int ret; ++ ++ /* CPUs refer to the same OPP table */ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) ++ return -ENODEV; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* Set OPP table conf with NOP config_clks */ ++ priv->opp_token = dev_pm_opp_set_config(cpu_dev, &config); ++ if (priv->opp_token < 0) ++ return dev_err_probe(dev, priv->opp_token, "Failed to set OPP config\n"); ++ ++ /* Set Attached PM for OPP ACTIVE */ ++ if (virt_devs) { ++ const char * const *name = airoha_cpufreq_pd_names; ++ int i, j; ++ ++ for (i = 0; *name; i++, name++) { ++ ret = pm_runtime_resume_and_get(virt_devs[i]); ++ if (ret) { ++ dev_err(cpu_dev, "failed to resume %s: %d\n", ++ *name, ret); ++ ++ /* Rollback previous PM runtime calls */ ++ name = config.genpd_names; ++ for (j = 0; *name && j < i; j++, name++) ++ pm_runtime_put(virt_devs[j]); ++ ++ goto err_register_cpufreq; ++ } ++ } ++ priv->virt_devs = virt_devs; ++ } ++ ++ cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); ++ ret = PTR_ERR_OR_ZERO(cpufreq_dt); ++ if (ret) { ++ dev_err(dev, "failed to create cpufreq-dt device: %d\n", ret); ++ goto err_register_cpufreq; ++ } ++ ++ priv->cpufreq_dt = cpufreq_dt; ++ platform_set_drvdata(pdev, priv); ++ ++ return 0; ++ ++err_register_cpufreq: ++ dev_pm_opp_clear_config(priv->opp_token); ++ ++ return ret; ++} ++ ++static void airoha_cpufreq_remove(struct platform_device *pdev) ++{ ++ struct airoha_cpufreq_priv *priv = platform_get_drvdata(pdev); ++ const char * const *name = airoha_cpufreq_pd_names; ++ int i; ++ ++ platform_device_unregister(priv->cpufreq_dt); ++ ++ dev_pm_opp_clear_config(priv->opp_token); ++ ++ for (i = 0; *name; i++, name++) ++ pm_runtime_put(priv->virt_devs[i]); ++} ++ ++static struct platform_driver airoha_cpufreq_driver = { ++ .probe = airoha_cpufreq_probe, ++ .remove_new = airoha_cpufreq_remove, ++ .driver = { ++ .name = "airoha-cpufreq", ++ }, ++}; ++ ++static const struct of_device_id airoha_cpufreq_match_list[] __initconst = { ++ { .compatible = "airoha,en7581" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, airoha_cpufreq_match_list); ++ ++static int __init airoha_cpufreq_init(void) ++{ ++ struct device_node *np = of_find_node_by_path("/"); ++ const struct of_device_id *match; ++ int ret; ++ ++ if (!np) ++ return -ENODEV; ++ ++ match = of_match_node(airoha_cpufreq_match_list, np); ++ of_node_put(np); ++ if (!match) ++ return -ENODEV; ++ ++ ret = platform_driver_register(&airoha_cpufreq_driver); ++ if (unlikely(ret < 0)) ++ return ret; ++ ++ cpufreq_pdev = platform_device_register_data(NULL, "airoha-cpufreq", ++ -1, match, sizeof(*match)); ++ ret = PTR_ERR_OR_ZERO(cpufreq_pdev); ++ if (ret) ++ platform_driver_unregister(&airoha_cpufreq_driver); ++ ++ return ret; ++} ++module_init(airoha_cpufreq_init); ++ ++static void __exit airoha_cpufreq_exit(void) ++{ ++ platform_device_unregister(cpufreq_pdev); ++ platform_driver_unregister(&airoha_cpufreq_driver); ++} ++module_exit(airoha_cpufreq_exit); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("CPUfreq driver for Airoha SoCs"); ++MODULE_LICENSE("GPL"); +--- a/drivers/cpufreq/cpufreq-dt-platdev.c ++++ b/drivers/cpufreq/cpufreq-dt-platdev.c +@@ -104,6 +104,8 @@ static const struct of_device_id allowli + * platforms using "operating-points-v2" property. + */ + static const struct of_device_id blocklist[] __initconst = { ++ { .compatible = "airoha,en7581", }, ++ + { .compatible = "allwinner,sun50i-h6", }, + { .compatible = "allwinner,sun50i-h616", }, + { .compatible = "allwinner,sun50i-h618", }, diff --git a/target/linux/airoha/patches-6.12/039-v6.14-net-airoha-Enforce-ETS-Qdisc-priomap.patch b/target/linux/airoha/patches-6.12/039-v6.14-net-airoha-Enforce-ETS-Qdisc-priomap.patch new file mode 100644 index 00000000000..daa7c2264bb --- /dev/null +++ b/target/linux/airoha/patches-6.12/039-v6.14-net-airoha-Enforce-ETS-Qdisc-priomap.patch @@ -0,0 +1,53 @@ +From b56e4d660a9688ff83f5cbdc6e3ea063352d0d79 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Jan 2025 19:32:45 +0100 +Subject: [PATCH] net: airoha: Enforce ETS Qdisc priomap + +EN7581 SoC supports fixed QoS band priority where WRR queues have lowest +priorities with respect to SP ones. +E.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn + +Enforce ETS Qdisc priomap according to the hw capabilities. + +Suggested-by: Davide Caratti +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Davide Caratti +Link: https://patch.msgid.link/20250112-airoha_ets_priomap-v1-1-fb616de159ba@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2806,7 +2806,7 @@ static int airoha_qdma_set_tx_ets_sched( + struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; + enum tx_sched_mode mode = TC_SCH_SP; + u16 w[AIROHA_NUM_QOS_QUEUES] = {}; +- int i, nstrict = 0; ++ int i, nstrict = 0, nwrr, qidx; + + if (p->bands > AIROHA_NUM_QOS_QUEUES) + return -EINVAL; +@@ -2820,7 +2820,20 @@ static int airoha_qdma_set_tx_ets_sched( + if (nstrict == AIROHA_NUM_QOS_QUEUES - 1) + return -EINVAL; + +- for (i = 0; i < p->bands - nstrict; i++) ++ /* EN7581 SoC supports fixed QoS band priority where WRR queues have ++ * lowest priorities with respect to SP ones. ++ * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn ++ */ ++ nwrr = p->bands - nstrict; ++ qidx = nstrict && nwrr ? nstrict : 0; ++ for (i = 1; i <= p->bands; i++) { ++ if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx) ++ return -EINVAL; ++ ++ qidx = i == nwrr ? 0 : qidx + 1; ++ } ++ ++ for (i = 0; i < nwrr; i++) + w[i] = p->weights[nstrict + i]; + + if (!nstrict) diff --git a/target/linux/airoha/patches-6.12/040-v6.14-pmdomain-airoha-Add-Airoha-CPU-PM-Domain-support.patch b/target/linux/airoha/patches-6.12/040-v6.14-pmdomain-airoha-Add-Airoha-CPU-PM-Domain-support.patch new file mode 100644 index 00000000000..8dc8a3d304a --- /dev/null +++ b/target/linux/airoha/patches-6.12/040-v6.14-pmdomain-airoha-Add-Airoha-CPU-PM-Domain-support.patch @@ -0,0 +1,196 @@ +From 82e703dd438b71432cc0ccbb90925d1e32dd014a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 9 Jan 2025 14:12:57 +0100 +Subject: [PATCH] pmdomain: airoha: Add Airoha CPU PM Domain support + +Add Airoha CPU PM Domain support to control frequency and power of CPU +present on Airoha EN7581 SoC. + +Frequency and power can be controlled with the use of the SMC command by +passing the performance state. The driver also expose a read-only clock +that expose the current CPU frequency with SMC command. + +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250109131313.32317-1-ansuelsmth@gmail.com +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/mediatek/Kconfig | 12 ++ + drivers/pmdomain/mediatek/Makefile | 1 + + .../pmdomain/mediatek/airoha-cpu-pmdomain.c | 144 ++++++++++++++++++ + 3 files changed, 157 insertions(+) + create mode 100644 drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c + +--- a/drivers/soc/mediatek/Kconfig ++++ b/drivers/soc/mediatek/Kconfig +@@ -2,6 +2,17 @@ + # + # MediaTek SoC drivers + # ++config AIROHA_CPU_PM_DOMAIN ++ tristate "Airoha CPU power domain" ++ default ARCH_AIROHA ++ depends on PM ++ select PM_GENERIC_DOMAINS ++ help ++ Say y here to enable CPU power domain support for Airoha SoC. ++ ++ CPU frequency and power is controlled by ATF with SMC command to ++ set performance states. ++ + menu "MediaTek SoC drivers" + depends on ARCH_MEDIATEK || COMPILE_TEST + +--- a/drivers/pmdomain/mediatek/Makefile ++++ b/drivers/pmdomain/mediatek/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o + obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o ++obj-$(CONFIG_AIROHA_CPU_PM_DOMAIN) += airoha-cpu-pmdomain.o +--- /dev/null ++++ b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c +@@ -0,0 +1,144 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AIROHA_SIP_AVS_HANDLE 0x82000301 ++#define AIROHA_AVS_OP_BASE 0xddddddd0 ++#define AIROHA_AVS_OP_MASK GENMASK(1, 0) ++#define AIROHA_AVS_OP_FREQ_DYN_ADJ (AIROHA_AVS_OP_BASE | \ ++ FIELD_PREP(AIROHA_AVS_OP_MASK, 0x1)) ++#define AIROHA_AVS_OP_GET_FREQ (AIROHA_AVS_OP_BASE | \ ++ FIELD_PREP(AIROHA_AVS_OP_MASK, 0x2)) ++ ++struct airoha_cpu_pmdomain_priv { ++ struct clk_hw hw; ++ struct generic_pm_domain pd; ++}; ++ ++static long airoha_cpu_pmdomain_clk_round(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ return rate; ++} ++ ++static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct arm_smccc_res res; ++ ++ arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_GET_FREQ, ++ 0, 0, 0, 0, 0, 0, &res); ++ ++ /* SMCCC returns freq in MHz */ ++ return (int)(res.a0 * 1000 * 1000); ++} ++ ++/* Airoha CPU clk SMCC is always enabled */ ++static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw) ++{ ++ return true; ++} ++ ++static const struct clk_ops airoha_cpu_pmdomain_clk_ops = { ++ .recalc_rate = airoha_cpu_pmdomain_clk_get, ++ .is_enabled = airoha_cpu_pmdomain_clk_is_enabled, ++ .round_rate = airoha_cpu_pmdomain_clk_round, ++}; ++ ++static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain, ++ unsigned int state) ++{ ++ struct arm_smccc_res res; ++ ++ arm_smccc_1_1_invoke(AIROHA_SIP_AVS_HANDLE, AIROHA_AVS_OP_FREQ_DYN_ADJ, ++ 0, state, 0, 0, 0, 0, &res); ++ ++ /* SMC signal correct apply by unsetting BIT 0 */ ++ return res.a0 & BIT(0) ? -EINVAL : 0; ++} ++ ++static int airoha_cpu_pmdomain_probe(struct platform_device *pdev) ++{ ++ struct airoha_cpu_pmdomain_priv *priv; ++ struct device *dev = &pdev->dev; ++ const struct clk_init_data init = { ++ .name = "cpu", ++ .ops = &airoha_cpu_pmdomain_clk_ops, ++ /* Clock with no set_rate, can't cache */ ++ .flags = CLK_GET_RATE_NOCACHE, ++ }; ++ struct generic_pm_domain *pd; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* Init and register a get-only clk for Cpufreq */ ++ priv->hw.init = &init; ++ ret = devm_clk_hw_register(dev, &priv->hw); ++ if (ret) ++ return ret; ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, ++ &priv->hw); ++ if (ret) ++ return ret; ++ ++ /* Init and register a PD for CPU */ ++ pd = &priv->pd; ++ pd->name = "cpu_pd"; ++ pd->flags = GENPD_FLAG_ALWAYS_ON; ++ pd->set_performance_state = airoha_cpu_pmdomain_set_performance_state; ++ ++ ret = pm_genpd_init(pd, NULL, false); ++ if (ret) ++ return ret; ++ ++ ret = of_genpd_add_provider_simple(dev->of_node, pd); ++ if (ret) ++ goto err_add_provider; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ return 0; ++ ++err_add_provider: ++ pm_genpd_remove(pd); ++ ++ return ret; ++} ++ ++static void airoha_cpu_pmdomain_remove(struct platform_device *pdev) ++{ ++ struct airoha_cpu_pmdomain_priv *priv = platform_get_drvdata(pdev); ++ ++ of_genpd_del_provider(pdev->dev.of_node); ++ pm_genpd_remove(&priv->pd); ++} ++ ++static const struct of_device_id airoha_cpu_pmdomain_of_match[] = { ++ { .compatible = "airoha,en7581-cpufreq" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, airoha_cpu_pmdomain_of_match); ++ ++static struct platform_driver airoha_cpu_pmdomain_driver = { ++ .probe = airoha_cpu_pmdomain_probe, ++ .remove_new = airoha_cpu_pmdomain_remove, ++ .driver = { ++ .name = "airoha-cpu-pmdomain", ++ .of_match_table = airoha_cpu_pmdomain_of_match, ++ }, ++}; ++module_platform_driver(airoha_cpu_pmdomain_driver); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("CPU PM domain driver for Airoha SoCs"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/041-01-v6.14-clk-en7523-Rework-clock-handling-for-different-clock.patch b/target/linux/airoha/patches-6.12/041-01-v6.14-clk-en7523-Rework-clock-handling-for-different-clock.patch new file mode 100644 index 00000000000..96d2bbf28ef --- /dev/null +++ b/target/linux/airoha/patches-6.12/041-01-v6.14-clk-en7523-Rework-clock-handling-for-different-clock.patch @@ -0,0 +1,83 @@ +From e4a9748e7103c47e575459db2b6a77d14f34da2b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Jan 2025 00:10:02 +0100 +Subject: [PATCH 1/4] clk: en7523: Rework clock handling for different clock + numbers + +Airoha EN7581 SoC have additional clock compared to EN7523 but current +driver permits to only support up to EN7523 clock numbers. + +To handle this, rework the clock handling and permit to declare the +clocks number in match_data and alloca clk_data based on the compatible +match_data. + +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250113231030.6735-2-ansuelsmth@gmail.com +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -75,6 +75,7 @@ struct en_rst_data { + }; + + struct en_clk_soc_data { ++ u32 num_clocks; + const struct clk_ops pcie_ops; + int (*hw_init)(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data); +@@ -504,8 +505,6 @@ static void en7523_register_clocks(struc + u32 rate; + int i; + +- clk_data->num = EN7523_NUM_CLOCKS; +- + for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { + const struct en_clk_desc *desc = &en7523_base_clks[i]; + u32 reg = desc->div_reg ? desc->div_reg : desc->base_reg; +@@ -587,8 +586,6 @@ static void en7581_register_clocks(struc + + hw = en7523_register_pcie_clk(dev, base); + clk_data->hws[EN7523_CLK_PCIE] = hw; +- +- clk_data->num = EN7523_NUM_CLOCKS; + } + + static int en7523_reset_update(struct reset_controller_dev *rcdev, +@@ -702,13 +699,15 @@ static int en7523_clk_probe(struct platf + struct clk_hw_onecell_data *clk_data; + int r; + ++ soc_data = device_get_match_data(&pdev->dev); ++ + clk_data = devm_kzalloc(&pdev->dev, +- struct_size(clk_data, hws, EN7523_NUM_CLOCKS), ++ struct_size(clk_data, hws, soc_data->num_clocks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + +- soc_data = device_get_match_data(&pdev->dev); ++ clk_data->num = soc_data->num_clocks; + r = soc_data->hw_init(pdev, clk_data); + if (r) + return r; +@@ -717,6 +716,7 @@ static int en7523_clk_probe(struct platf + } + + static const struct en_clk_soc_data en7523_data = { ++ .num_clocks = ARRAY_SIZE(en7523_base_clks) + 1, + .pcie_ops = { + .is_enabled = en7523_pci_is_enabled, + .prepare = en7523_pci_prepare, +@@ -726,6 +726,8 @@ static const struct en_clk_soc_data en75 + }; + + static const struct en_clk_soc_data en7581_data = { ++ /* We increment num_clocks by 1 to account for additional PCIe clock */ ++ .num_clocks = ARRAY_SIZE(en7581_base_clks) + 1, + .pcie_ops = { + .is_enabled = en7581_pci_is_enabled, + .enable = en7581_pci_enable, diff --git a/target/linux/airoha/patches-6.12/041-02-v6.14-dt-bindings-clock-drop-NUM_CLOCKS-define-for-EN7581.patch b/target/linux/airoha/patches-6.12/041-02-v6.14-dt-bindings-clock-drop-NUM_CLOCKS-define-for-EN7581.patch new file mode 100644 index 00000000000..5db79a47488 --- /dev/null +++ b/target/linux/airoha/patches-6.12/041-02-v6.14-dt-bindings-clock-drop-NUM_CLOCKS-define-for-EN7581.patch @@ -0,0 +1,26 @@ +From 02d3b7557ce28c373ea1e925ae16ab5988284313 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Jan 2025 00:10:03 +0100 +Subject: [PATCH 2/4] dt-bindings: clock: drop NUM_CLOCKS define for EN7581 + +Drop NUM_CLOCKS define for EN7581 include. This is not a binding and +should not be placed here. Value is derived internally in the user +driver. + +Signed-off-by: Christian Marangi +Acked-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20250113231030.6735-3-ansuelsmth@gmail.com +Signed-off-by: Stephen Boyd +--- + include/dt-bindings/clock/en7523-clk.h | 2 -- + 1 file changed, 2 deletions(-) + +--- a/include/dt-bindings/clock/en7523-clk.h ++++ b/include/dt-bindings/clock/en7523-clk.h +@@ -12,6 +12,4 @@ + #define EN7523_CLK_CRYPTO 6 + #define EN7523_CLK_PCIE 7 + +-#define EN7523_NUM_CLOCKS 8 +- + #endif /* _DT_BINDINGS_CLOCK_AIROHA_EN7523_H_ */ diff --git a/target/linux/airoha/patches-6.12/041-03-v6.14-dt-bindings-clock-add-ID-for-eMMC-for-EN7581.patch b/target/linux/airoha/patches-6.12/041-03-v6.14-dt-bindings-clock-add-ID-for-eMMC-for-EN7581.patch new file mode 100644 index 00000000000..a3f0c9e6fea --- /dev/null +++ b/target/linux/airoha/patches-6.12/041-03-v6.14-dt-bindings-clock-add-ID-for-eMMC-for-EN7581.patch @@ -0,0 +1,25 @@ +From 82108ad3285f58f314ad41398f44017c7dbe44de Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Jan 2025 00:10:04 +0100 +Subject: [PATCH 3/4] dt-bindings: clock: add ID for eMMC for EN7581 + +Add ID for eMMC for EN7581. This is to control clock selection of eMMC +between 200MHz and 150MHz. + +Signed-off-by: Christian Marangi +Acked-by: Conor Dooley +Link: https://lore.kernel.org/r/20250113231030.6735-4-ansuelsmth@gmail.com +Signed-off-by: Stephen Boyd +--- + include/dt-bindings/clock/en7523-clk.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/dt-bindings/clock/en7523-clk.h ++++ b/include/dt-bindings/clock/en7523-clk.h +@@ -12,4 +12,6 @@ + #define EN7523_CLK_CRYPTO 6 + #define EN7523_CLK_PCIE 7 + ++#define EN7581_CLK_EMMC 8 ++ + #endif /* _DT_BINDINGS_CLOCK_AIROHA_EN7523_H_ */ diff --git a/target/linux/airoha/patches-6.12/041-04-v6.14-clk-en7523-Add-clock-for-eMMC-for-EN7581.patch b/target/linux/airoha/patches-6.12/041-04-v6.14-clk-en7523-Add-clock-for-eMMC-for-EN7581.patch new file mode 100644 index 00000000000..6c8a3300bee --- /dev/null +++ b/target/linux/airoha/patches-6.12/041-04-v6.14-clk-en7523-Add-clock-for-eMMC-for-EN7581.patch @@ -0,0 +1,41 @@ +From bfe257f9780d8f77045a7da6ec959ee0659d2f98 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Jan 2025 00:10:05 +0100 +Subject: [PATCH 4/4] clk: en7523: Add clock for eMMC for EN7581 + +Add clock for eMMC for EN7581. This is used to give info of the current +eMMC source clock and to switch it from 200MHz or 150MHz. + +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250113231030.6735-5-ansuelsmth@gmail.com +Signed-off-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -91,6 +91,7 @@ static const u32 emi7581_base[] = { 5400 + static const u32 bus7581_base[] = { 600000000, 540000000 }; + static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; + static const u32 crypto_base[] = { 540000000, 480000000 }; ++static const u32 emmc7581_base[] = { 200000000, 150000000 }; + + static const struct en_clk_desc en7523_base_clks[] = { + { +@@ -281,6 +282,15 @@ static const struct en_clk_desc en7581_b + .base_shift = 0, + .base_values = crypto_base, + .n_base_values = ARRAY_SIZE(crypto_base), ++ }, { ++ .id = EN7581_CLK_EMMC, ++ .name = "emmc", ++ ++ .base_reg = REG_CRYPTO_CLKSRC2, ++ .base_bits = 1, ++ .base_shift = 12, ++ .base_values = emmc7581_base, ++ .n_base_values = ARRAY_SIZE(emmc7581_base), + } + }; + diff --git a/target/linux/airoha/patches-6.12/042-01-v6.14-PCI-mediatek-gen3-Rely-on-clk_bulk_prepare_enable-in.patch b/target/linux/airoha/patches-6.12/042-01-v6.14-PCI-mediatek-gen3-Rely-on-clk_bulk_prepare_enable-in.patch new file mode 100644 index 00000000000..c77ae7bfcae --- /dev/null +++ b/target/linux/airoha/patches-6.12/042-01-v6.14-PCI-mediatek-gen3-Rely-on-clk_bulk_prepare_enable-in.patch @@ -0,0 +1,57 @@ +From 0e7a622da17da0042294860cdb7a2fac091d25b1 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Jan 2025 10:50:40 +0100 +Subject: [PATCH 1/6] PCI: mediatek-gen3: Rely on clk_bulk_prepare_enable() in + mtk_pcie_en7581_power_up() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace clk_bulk_prepare() and clk_bulk_enable() with +clk_bulk_prepare_enable() in mtk_pcie_en7581_power_up() routine. + +Link: https://lore.kernel.org/r/20250108-pcie-en7581-fixes-v6-1-21ac939a3b9b@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Manivannan Sadhasivam +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 14 +++----------- + 1 file changed, 3 insertions(+), 11 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -900,12 +900,6 @@ static int mtk_pcie_en7581_power_up(stru + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + +- err = clk_bulk_prepare(pcie->num_clks, pcie->clks); +- if (err) { +- dev_err(dev, "failed to prepare clock\n"); +- goto err_clk_prepare; +- } +- + val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | + FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | + FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | +@@ -918,17 +912,15 @@ static int mtk_pcie_en7581_power_up(stru + FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); + writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); + +- err = clk_bulk_enable(pcie->num_clks, pcie->clks); ++ err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (err) { + dev_err(dev, "failed to prepare clock\n"); +- goto err_clk_enable; ++ goto err_clk_prepare_enable; + } + + return 0; + +-err_clk_enable: +- clk_bulk_unprepare(pcie->num_clks, pcie->clks); +-err_clk_prepare: ++err_clk_prepare_enable: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); diff --git a/target/linux/airoha/patches-6.12/042-02-v6.14-PCI-mediatek-gen3-Move-reset-assert-callbacks-in-.po.patch b/target/linux/airoha/patches-6.12/042-02-v6.14-PCI-mediatek-gen3-Move-reset-assert-callbacks-in-.po.patch new file mode 100644 index 00000000000..f56a2ce7ebb --- /dev/null +++ b/target/linux/airoha/patches-6.12/042-02-v6.14-PCI-mediatek-gen3-Move-reset-assert-callbacks-in-.po.patch @@ -0,0 +1,86 @@ +From e4c7dfd953f7618f0ccb70d87c1629634f306fab Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Jan 2025 10:50:41 +0100 +Subject: [PATCH 2/6] PCI: mediatek-gen3: Move reset/assert callbacks in + .power_up() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In order to make the code more readable, the reset_control_bulk_assert() +function for PHY reset lines is moved to make it pair with +reset_control_bulk_deassert() in mtk_pcie_power_up() and +mtk_pcie_en7581_power_up(). The same change is done for +reset_control_assert() used to assert MAC reset line. + +Introduce PCIE_MTK_RESET_TIME_US macro for the time needed to +complete PCIe reset on MediaTek controller. + +Link: https://lore.kernel.org/r/20250108-pcie-en7581-fixes-v6-2-21ac939a3b9b@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Manivannan Sadhasivam +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 28 +++++++++++++-------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -120,6 +120,8 @@ + + #define MAX_NUM_PHY_RESETS 3 + ++#define PCIE_MTK_RESET_TIME_US 10 ++ + /* Time in ms needed to complete PCIe reset on EN7581 SoC */ + #define PCIE_EN7581_RESET_TIME_MS 100 + +@@ -868,9 +870,14 @@ static int mtk_pcie_en7581_power_up(stru + u32 val; + + /* +- * Wait for the time needed to complete the bulk assert in +- * mtk_pcie_setup for EN7581 SoC. ++ * The controller may have been left out of reset by the bootloader ++ * so make sure that we get a clean start by asserting resets here. + */ ++ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, ++ pcie->phy_resets); ++ reset_control_assert(pcie->mac_reset); ++ ++ /* Wait for the time needed to complete the reset lines assert. */ + mdelay(PCIE_EN7581_RESET_TIME_MS); + + err = phy_init(pcie->phy); +@@ -937,6 +944,15 @@ static int mtk_pcie_power_up(struct mtk_ + struct device *dev = pcie->dev; + int err; + ++ /* ++ * The controller may have been left out of reset by the bootloader ++ * so make sure that we get a clean start by asserting resets here. ++ */ ++ reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, ++ pcie->phy_resets); ++ reset_control_assert(pcie->mac_reset); ++ usleep_range(PCIE_MTK_RESET_TIME_US, 2 * PCIE_MTK_RESET_TIME_US); ++ + /* PHY power on and enable pipe clock */ + err = reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); + if (err) { +@@ -1009,14 +1025,6 @@ static int mtk_pcie_setup(struct mtk_gen + * counter since the bulk is shared. + */ + reset_control_bulk_deassert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); +- /* +- * The controller may have been left out of reset by the bootloader +- * so make sure that we get a clean start by asserting resets here. +- */ +- reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); +- +- reset_control_assert(pcie->mac_reset); +- usleep_range(10, 20); + + /* Don't touch the hardware registers before power up */ + err = pcie->soc->power_up(pcie); diff --git a/target/linux/airoha/patches-6.12/042-03-v6.14-PCI-mediatek-gen3-Add-comment-about-initialization-o.patch b/target/linux/airoha/patches-6.12/042-03-v6.14-PCI-mediatek-gen3-Add-comment-about-initialization-o.patch new file mode 100644 index 00000000000..056d7733178 --- /dev/null +++ b/target/linux/airoha/patches-6.12/042-03-v6.14-PCI-mediatek-gen3-Add-comment-about-initialization-o.patch @@ -0,0 +1,35 @@ +From 0c9d2d2ef0d916b490a9222ed20ff4616fca876d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Jan 2025 10:50:42 +0100 +Subject: [PATCH 3/6] PCI: mediatek-gen3: Add comment about initialization + order in mtk_pcie_en7581_power_up() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add a comment in mtk_pcie_en7581_power_up() to clarify, unlike the other +MediaTek Gen3 controllers, the Airoha EN7581 requires PHY initialization +and power-on before PHY reset deassert. + +Link: https://lore.kernel.org/r/20250108-pcie-en7581-fixes-v6-3-21ac939a3b9b@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Reviewed-by: Manivannan Sadhasivam +Reviewed-by: AngeloGioacchino Del Regno +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -880,6 +880,10 @@ static int mtk_pcie_en7581_power_up(stru + /* Wait for the time needed to complete the reset lines assert. */ + mdelay(PCIE_EN7581_RESET_TIME_MS); + ++ /* ++ * Unlike the other MediaTek Gen3 controllers, the Airoha EN7581 ++ * requires PHY initialization and power-on before PHY reset deassert. ++ */ + err = phy_init(pcie->phy); + if (err) { + dev_err(dev, "failed to initialize PHY\n"); diff --git a/target/linux/airoha/patches-6.12/042-04-v6.14-PCI-mediatek-gen3-Move-reset-delay-in-mtk_pcie_en758.patch b/target/linux/airoha/patches-6.12/042-04-v6.14-PCI-mediatek-gen3-Move-reset-delay-in-mtk_pcie_en758.patch new file mode 100644 index 00000000000..845209d58d8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/042-04-v6.14-PCI-mediatek-gen3-Move-reset-delay-in-mtk_pcie_en758.patch @@ -0,0 +1,59 @@ +From 90d4e466c9ea2010f33880a36317a8486ccbe082 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Jan 2025 10:50:43 +0100 +Subject: [PATCH 4/6] PCI: mediatek-gen3: Move reset delay in + mtk_pcie_en7581_power_up() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal +causing occasional PCIe link down issues. In order to overcome the +problem, PCIe block is reset using REG_PCI_CONTROL (0x88) and +REG_RESET_CONTROL (0x834) registers available in the clock module +running clk_bulk_prepare_enable() in mtk_pcie_en7581_power_up(). + +In order to make the code more readable, move the wait for the time +needed to complete the PCIe reset from en7581_pci_enable() to +mtk_pcie_en7581_power_up(). + +Reduce reset timeout from 250ms to the standard PCIE_T_PVPERL_MS value +(100ms) since it has no impact on the driver behavior. + +Link: https://lore.kernel.org/r/20250108-pcie-en7581-fixes-v6-4-21ac939a3b9b@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Manivannan Sadhasivam +Acked-by: Stephen Boyd +--- + drivers/clk/clk-en7523.c | 1 - + drivers/pci/controller/pcie-mediatek-gen3.c | 7 +++++++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -489,7 +489,6 @@ static int en7581_pci_enable(struct clk_ + REG_PCI_CONTROL_PERSTOUT; + val = readl(np_base + REG_PCI_CONTROL); + writel(val | mask, np_base + REG_PCI_CONTROL); +- msleep(250); + + return 0; + } +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -929,6 +929,13 @@ static int mtk_pcie_en7581_power_up(stru + goto err_clk_prepare_enable; + } + ++ /* ++ * Airoha EN7581 performs PCIe reset via clk callbacks since it has a ++ * hw issue with PCIE_PE_RSTB signal. Add wait for the time needed to ++ * complete the PCIe reset. ++ */ ++ msleep(PCIE_T_PVPERL_MS); ++ + return 0; + + err_clk_prepare_enable: diff --git a/target/linux/airoha/patches-6.12/042-05-v6.14-PCI-mediatek-gen3-Rely-on-msleep-in-mtk_pcie_en7581_.patch b/target/linux/airoha/patches-6.12/042-05-v6.14-PCI-mediatek-gen3-Rely-on-msleep-in-mtk_pcie_en7581_.patch new file mode 100644 index 00000000000..92a33a0afd8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/042-05-v6.14-PCI-mediatek-gen3-Rely-on-msleep-in-mtk_pcie_en7581_.patch @@ -0,0 +1,41 @@ +From c98bee18d0a094e37100c85effe5e161418f8644 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Jan 2025 10:50:44 +0100 +Subject: [PATCH 5/6] PCI: mediatek-gen3: Rely on msleep() in + mtk_pcie_en7581_power_up() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Since mtk_pcie_en7581_power_up() runs in non-atomic context, rely on +msleep() routine instead of mdelay(). + +Link: https://lore.kernel.org/r/20250108-pcie-en7581-fixes-v6-5-21ac939a3b9b@kernel.org +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +Reviewed-by: AngeloGioacchino Del Regno +Reviewed-by: Manivannan Sadhasivam +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -878,7 +878,7 @@ static int mtk_pcie_en7581_power_up(stru + reset_control_assert(pcie->mac_reset); + + /* Wait for the time needed to complete the reset lines assert. */ +- mdelay(PCIE_EN7581_RESET_TIME_MS); ++ msleep(PCIE_EN7581_RESET_TIME_MS); + + /* + * Unlike the other MediaTek Gen3 controllers, the Airoha EN7581 +@@ -906,7 +906,7 @@ static int mtk_pcie_en7581_power_up(stru + * Wait for the time needed to complete the bulk de-assert above. + * This time is specific for EN7581 SoC. + */ +- mdelay(PCIE_EN7581_RESET_TIME_MS); ++ msleep(PCIE_EN7581_RESET_TIME_MS); + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); diff --git a/target/linux/airoha/patches-6.12/042-06-v6.14-PCI-mediatek-gen3-Avoid-PCIe-resetting-via-PERST-for.patch b/target/linux/airoha/patches-6.12/042-06-v6.14-PCI-mediatek-gen3-Avoid-PCIe-resetting-via-PERST-for.patch new file mode 100644 index 00000000000..080d5016987 --- /dev/null +++ b/target/linux/airoha/patches-6.12/042-06-v6.14-PCI-mediatek-gen3-Avoid-PCIe-resetting-via-PERST-for.patch @@ -0,0 +1,128 @@ +From 491cb9c5084790aafa02e843349492c284373231 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 9 Jan 2025 00:30:45 +0100 +Subject: [PATCH 6/6] PCI: mediatek-gen3: Avoid PCIe resetting via PERST# for + Airoha EN7581 SoC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Airoha EN7581 has a hw bug asserting/releasing PERST# signal causing +occasional PCIe link down issues. In order to overcome the problem, +PERST# signal is not asserted/released during device probe or +suspend/resume phase and the PCIe block is reset using +en7523_reset_assert() and en7581_pci_enable(). + +Introduce flags field in the mtk_gen3_pcie_pdata struct in order to +specify per-SoC capabilities. + +Link: https://lore.kernel.org/r/20250109-pcie-en7581-rst-fix-v4-1-4a45c89fb143@kernel.org +Tested-by: Hui Ma +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Krzysztof Wilczyński +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 59 ++++++++++++++------- + 1 file changed, 41 insertions(+), 18 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -127,10 +127,18 @@ + + struct mtk_gen3_pcie; + ++enum mtk_gen3_pcie_flags { ++ SKIP_PCIE_RSTB = BIT(0), /* Skip PERST# assertion during device ++ * probing or suspend/resume phase to ++ * avoid hw bugs/issues. ++ */ ++}; ++ + /** + * struct mtk_gen3_pcie_pdata - differentiate between host generations + * @power_up: pcie power_up callback + * @phy_resets: phy reset lines SoC data. ++ * @flags: pcie device flags. + */ + struct mtk_gen3_pcie_pdata { + int (*power_up)(struct mtk_gen3_pcie *pcie); +@@ -138,6 +146,7 @@ struct mtk_gen3_pcie_pdata { + const char *id[MAX_NUM_PHY_RESETS]; + int num_resets; + } phy_resets; ++ u32 flags; + }; + + /** +@@ -404,22 +413,33 @@ static int mtk_pcie_startup_port(struct + val |= PCIE_DISABLE_DVFSRC_VLT_REQ; + writel_relaxed(val, pcie->base + PCIE_MISC_CTRL_REG); + +- /* Assert all reset signals */ +- val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); +- val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB; +- writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); +- + /* +- * Described in PCIe CEM specification sections 2.2 (PERST# Signal) +- * and 2.2.1 (Initial Power-Up (G3 to S0)). +- * The deassertion of PERST# should be delayed 100ms (TPVPERL) +- * for the power and clock to become stable. ++ * Airoha EN7581 has a hw bug asserting/releasing PCIE_PE_RSTB signal ++ * causing occasional PCIe link down. In order to overcome the issue, ++ * PCIE_RSTB signals are not asserted/released at this stage and the ++ * PCIe block is reset using en7523_reset_assert() and ++ * en7581_pci_enable(). + */ +- msleep(100); +- +- /* De-assert reset signals */ +- val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | PCIE_PE_RSTB); +- writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); ++ if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { ++ /* Assert all reset signals */ ++ val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); ++ val |= PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | ++ PCIE_PE_RSTB; ++ writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); ++ ++ /* ++ * Described in PCIe CEM specification revision 6.0. ++ * ++ * The deassertion of PERST# should be delayed 100ms (TPVPERL) ++ * for the power and clock to become stable. ++ */ ++ msleep(PCIE_T_PVPERL_MS); ++ ++ /* De-assert reset signals */ ++ val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | ++ PCIE_PE_RSTB); ++ writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); ++ } + + /* Check if the link is up or not */ + err = readl_poll_timeout(pcie->base + PCIE_LINK_STATUS_REG, val, +@@ -1171,10 +1191,12 @@ static int mtk_pcie_suspend_noirq(struct + return err; + } + +- /* Pull down the PERST# pin */ +- val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); +- val |= PCIE_PE_RSTB; +- writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); ++ if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { ++ /* Assert the PERST# pin */ ++ val = readl_relaxed(pcie->base + PCIE_RST_CTRL_REG); ++ val |= PCIE_PE_RSTB; ++ writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); ++ } + + dev_dbg(pcie->dev, "entered L2 states successfully"); + +@@ -1225,6 +1247,7 @@ static const struct mtk_gen3_pcie_pdata + .id[2] = "phy-lane2", + .num_resets = 3, + }, ++ .flags = SKIP_PCIE_RSTB, + }; + + static const struct of_device_id mtk_pcie_of_match[] = { diff --git a/target/linux/airoha/patches-6.12/043-v6.15-PCI-mediatek-gen3-Remove-leftover-mac_reset-assert-f.patch b/target/linux/airoha/patches-6.12/043-v6.15-PCI-mediatek-gen3-Remove-leftover-mac_reset-assert-f.patch new file mode 100644 index 00000000000..7d7ee4aae1f --- /dev/null +++ b/target/linux/airoha/patches-6.12/043-v6.15-PCI-mediatek-gen3-Remove-leftover-mac_reset-assert-f.patch @@ -0,0 +1,34 @@ +From b6d7bb0d3bd74b491e2e6fd59c4d5110d06fd63b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 1 Feb 2025 12:00:18 +0100 +Subject: [PATCH] PCI: mediatek-gen3: Remove leftover mac_reset assert for + Airoha EN7581 SoC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove a leftover assert for mac_reset line in mtk_pcie_en7581_power_up(). + +This is not harmful since EN7581 does not requires mac_reset and +mac_reset is not defined in EN7581 device tree. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Manivannan Sadhasivam +Reviewed-by: Philipp Zabel +Link: https://lore.kernel.org/r/20250201-pcie-en7581-remove-mac_reset-v2-1-a06786cdc683@kernel.org +[kwilczynski: commit log] +Signed-off-by: Krzysztof Wilczyński +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -895,7 +895,6 @@ static int mtk_pcie_en7581_power_up(stru + */ + reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, + pcie->phy_resets); +- reset_control_assert(pcie->mac_reset); + + /* Wait for the time needed to complete the reset lines assert. */ + msleep(PCIE_EN7581_RESET_TIME_MS); diff --git a/target/linux/airoha/patches-6.12/044-v6.15-PCI-mediatek-gen3-Configure-PBUS_CSR-registers-for-E.patch b/target/linux/airoha/patches-6.12/044-v6.15-PCI-mediatek-gen3-Configure-PBUS_CSR-registers-for-E.patch new file mode 100644 index 00000000000..4415eac0ee5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/044-v6.15-PCI-mediatek-gen3-Configure-PBUS_CSR-registers-for-E.patch @@ -0,0 +1,81 @@ +From 249b78298078448a699c39356d27d8183af4b281 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 25 Feb 2025 09:04:07 +0100 +Subject: [PATCH] PCI: mediatek-gen3: Configure PBUS_CSR registers for EN7581 + SoC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Configure PBus base address and address mask to allow the hw +to detect if a given address is accessible on PCIe controller. + +Fixes: f6ab898356dd ("PCI: mediatek-gen3: Add Airoha EN7581 support") +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/20250225-en7581-pcie-pbus-csr-v4-2-24324382424a@kernel.org +Signed-off-by: Krzysztof Wilczyński +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 28 ++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -24,6 +25,7 @@ + #include + #include + #include ++#include + #include + + #include "../pci.h" +@@ -885,9 +887,13 @@ static int mtk_pcie_parse_port(struct mt + + static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) + { ++ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct device *dev = pcie->dev; ++ struct resource_entry *entry; ++ struct regmap *pbus_regmap; ++ u32 val, args[2], size; ++ resource_size_t addr; + int err; +- u32 val; + + /* + * The controller may have been left out of reset by the bootloader +@@ -900,6 +906,26 @@ static int mtk_pcie_en7581_power_up(stru + msleep(PCIE_EN7581_RESET_TIME_MS); + + /* ++ * Configure PBus base address and base address mask to allow the ++ * hw to detect if a given address is accessible on PCIe controller. ++ */ ++ pbus_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, ++ "mediatek,pbus-csr", ++ ARRAY_SIZE(args), ++ args); ++ if (IS_ERR(pbus_regmap)) ++ return PTR_ERR(pbus_regmap); ++ ++ entry = resource_list_first_type(&host->windows, IORESOURCE_MEM); ++ if (!entry) ++ return -ENODEV; ++ ++ addr = entry->res->start - entry->offset; ++ regmap_write(pbus_regmap, args[0], lower_32_bits(addr)); ++ size = lower_32_bits(resource_size(entry->res)); ++ regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size))); ++ ++ /* + * Unlike the other MediaTek Gen3 controllers, the Airoha EN7581 + * requires PHY initialization and power-on before PHY reset deassert. + */ diff --git a/target/linux/airoha/patches-6.12/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch b/target/linux/airoha/patches-6.12/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch new file mode 100644 index 00000000000..894cc2c9c92 --- /dev/null +++ b/target/linux/airoha/patches-6.12/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch @@ -0,0 +1,60 @@ +From c6287e1a858e336cc202b484c6138a0fe252c6b3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 13 Feb 2025 16:34:20 +0100 +Subject: [PATCH] net: airoha: Fix TSO support for header cloned skbs + +For GSO packets, skb_cow_head() will reallocate the skb for TSO header +cloned skbs in airoha_dev_xmit(). For this reason, sinfo pointer can be +no more valid. Fix the issue relying on skb_shinfo() macro directly in +airoha_dev_xmit(). + +The problem exists since +commit 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +but it is not a user visible, since we can't currently enable TSO +for DSA user ports since we are missing to initialize net_device +vlan_features field. + +Reviewed-by: Mateusz Polchlopek +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250213-airoha-en7581-flowtable-offload-v4-1-b69ca16d74db@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2569,11 +2569,10 @@ static u16 airoha_dev_select_queue(struc + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, + struct net_device *dev) + { +- struct skb_shared_info *sinfo = skb_shinfo(skb); + struct airoha_gdm_port *port = netdev_priv(dev); ++ u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags; + u32 msg0, msg1, len = skb_headlen(skb); + struct airoha_qdma *qdma = port->qdma; +- u32 nr_frags = 1 + sinfo->nr_frags; + struct netdev_queue *txq; + struct airoha_queue *q; + void *data = skb->data; +@@ -2596,8 +2595,9 @@ static netdev_tx_t airoha_dev_xmit(struc + if (skb_cow_head(skb, 0)) + goto error; + +- if (sinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { +- __be16 csum = cpu_to_be16(sinfo->gso_size); ++ if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | ++ SKB_GSO_TCPV6)) { ++ __be16 csum = cpu_to_be16(skb_shinfo(skb)->gso_size); + + tcp_hdr(skb)->check = (__force __sum16)csum; + msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1); +@@ -2626,7 +2626,7 @@ static netdev_tx_t airoha_dev_xmit(struc + for (i = 0; i < nr_frags; i++) { + struct airoha_qdma_desc *desc = &q->desc[index]; + struct airoha_queue_entry *e = &q->entry[index]; +- skb_frag_t *frag = &sinfo->frags[i]; ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t addr; + u32 val; + diff --git a/target/linux/airoha/patches-6.12/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch b/target/linux/airoha/patches-6.12/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch new file mode 100644 index 00000000000..ca73e019665 --- /dev/null +++ b/target/linux/airoha/patches-6.12/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch @@ -0,0 +1,42 @@ +From c9f947769b77c8e8f318bfc8a0777e5d20c44d8d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 17 Oct 2024 16:01:41 +0200 +Subject: [PATCH] net: airoha: Reset BQL stopping the netdevice + +Run airoha_qdma_cleanup_tx_queue() in ndo_stop callback in order to +unmap pending skbs. Moreover, reset BQL txq state stopping the netdevice, + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Hariprasad Kelam +Message-ID: <20241017-airoha-en7581-reset-bql-v1-1-08c0c9888de5@kernel.org> +Signed-off-by: Andrew Lunn +--- + drivers/net/ethernet/mediatek/airoha_eth.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ b/drivers/net/ethernet/mediatek/airoha_eth.c +@@ -2489,7 +2489,7 @@ static int airoha_dev_stop(struct net_de + { + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; +- int err; ++ int i, err; + + netif_tx_disable(dev); + err = airoha_set_gdm_ports(qdma->eth, false); +@@ -2500,6 +2500,14 @@ static int airoha_dev_stop(struct net_de + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) ++ continue; ++ ++ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); ++ netdev_tx_reset_subqueue(dev, i); ++ } ++ + return 0; + } + diff --git a/target/linux/airoha/patches-6.12/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch b/target/linux/airoha/patches-6.12/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch new file mode 100644 index 00000000000..e3c54f1c045 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch @@ -0,0 +1,6863 @@ +From fb3dda82fd38ca42140f29b3082324dcdc128293 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:09 +0100 +Subject: [PATCH 01/15] net: airoha: Move airoha_eth driver in a dedicated + folder + +The airoha_eth driver has no codebase shared with mtk_eth_soc one. +Moreover, the upcoming features (flowtable hw offloading, PCS, ..) will +not reuse any code from MediaTek driver. Move the Airoha driver in a +dedicated folder. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/Kconfig | 2 ++ + drivers/net/ethernet/Makefile | 1 + + drivers/net/ethernet/airoha/Kconfig | 18 ++++++++++++++++++ + drivers/net/ethernet/airoha/Makefile | 6 ++++++ + .../ethernet/{mediatek => airoha}/airoha_eth.c | 0 + drivers/net/ethernet/mediatek/Kconfig | 8 -------- + drivers/net/ethernet/mediatek/Makefile | 1 - + 7 files changed, 27 insertions(+), 9 deletions(-) + create mode 100644 drivers/net/ethernet/airoha/Kconfig + create mode 100644 drivers/net/ethernet/airoha/Makefile + rename drivers/net/ethernet/{mediatek => airoha}/airoha_eth.c (100%) + +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -20,6 +20,8 @@ source "drivers/net/ethernet/actions/Kco + source "drivers/net/ethernet/adaptec/Kconfig" + source "drivers/net/ethernet/aeroflex/Kconfig" + source "drivers/net/ethernet/agere/Kconfig" ++source "drivers/net/ethernet/airoha/Kconfig" ++source "drivers/net/ethernet/mellanox/Kconfig" + source "drivers/net/ethernet/alacritech/Kconfig" + source "drivers/net/ethernet/allwinner/Kconfig" + source "drivers/net/ethernet/alteon/Kconfig" +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adap + obj-$(CONFIG_GRETH) += aeroflex/ + obj-$(CONFIG_NET_VENDOR_ADI) += adi/ + obj-$(CONFIG_NET_VENDOR_AGERE) += agere/ ++obj-$(CONFIG_NET_VENDOR_AIROHA) += airoha/ + obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/ + obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ + obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/ +--- /dev/null ++++ b/drivers/net/ethernet/airoha/Kconfig +@@ -0,0 +1,18 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config NET_VENDOR_AIROHA ++ bool "Airoha devices" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ help ++ If you have a Airoha SoC with ethernet, say Y. ++ ++if NET_VENDOR_AIROHA ++ ++config NET_AIROHA ++ tristate "Airoha SoC Gigabit Ethernet support" ++ depends on NET_DSA || !NET_DSA ++ select PAGE_POOL ++ help ++ This driver supports the gigabit ethernet MACs in the ++ Airoha SoC family. ++ ++endif #NET_VENDOR_AIROHA +--- /dev/null ++++ b/drivers/net/ethernet/airoha/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# Airoha for the Mediatek SoCs built-in ethernet macs ++# ++ ++obj-$(CONFIG_NET_AIROHA) += airoha_eth.o +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -7,14 +7,6 @@ config NET_VENDOR_MEDIATEK + + if NET_VENDOR_MEDIATEK + +-config NET_AIROHA +- tristate "Airoha SoC Gigabit Ethernet support" +- depends on NET_DSA || !NET_DSA +- select PAGE_POOL +- help +- This driver supports the gigabit ethernet MACs in the +- Airoha SoC family. +- + config NET_MEDIATEK_SOC_WED + depends on ARCH_MEDIATEK || COMPILE_TEST + def_bool NET_MEDIATEK_SOC != n +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -11,4 +11,3 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) + + endif + obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o + obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o +-obj-$(CONFIG_NET_AIROHA) += airoha_eth.o +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -0,0 +1,3378 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AIROHA_MAX_NUM_GDM_PORTS 1 ++#define AIROHA_MAX_NUM_QDMA 2 ++#define AIROHA_MAX_NUM_RSTS 3 ++#define AIROHA_MAX_NUM_XSI_RSTS 5 ++#define AIROHA_MAX_MTU 2000 ++#define AIROHA_MAX_PACKET_SIZE 2048 ++#define AIROHA_NUM_QOS_CHANNELS 4 ++#define AIROHA_NUM_QOS_QUEUES 8 ++#define AIROHA_NUM_TX_RING 32 ++#define AIROHA_NUM_RX_RING 32 ++#define AIROHA_NUM_NETDEV_TX_RINGS (AIROHA_NUM_TX_RING + \ ++ AIROHA_NUM_QOS_CHANNELS) ++#define AIROHA_FE_MC_MAX_VLAN_TABLE 64 ++#define AIROHA_FE_MC_MAX_VLAN_PORT 16 ++#define AIROHA_NUM_TX_IRQ 2 ++#define HW_DSCP_NUM 2048 ++#define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) ++#define TX_DSCP_NUM 1024 ++#define RX_DSCP_NUM(_n) \ ++ ((_n) == 2 ? 128 : \ ++ (_n) == 11 ? 128 : \ ++ (_n) == 15 ? 128 : \ ++ (_n) == 0 ? 1024 : 16) ++ ++#define PSE_RSV_PAGES 128 ++#define PSE_QUEUE_RSV_PAGES 64 ++ ++#define QDMA_METER_IDX(_n) ((_n) & 0xff) ++#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) ++ ++/* FE */ ++#define PSE_BASE 0x0100 ++#define CSR_IFC_BASE 0x0200 ++#define CDM1_BASE 0x0400 ++#define GDM1_BASE 0x0500 ++#define PPE1_BASE 0x0c00 ++ ++#define CDM2_BASE 0x1400 ++#define GDM2_BASE 0x1500 ++ ++#define GDM3_BASE 0x1100 ++#define GDM4_BASE 0x2500 ++ ++#define GDM_BASE(_n) \ ++ ((_n) == 4 ? GDM4_BASE : \ ++ (_n) == 3 ? GDM3_BASE : \ ++ (_n) == 2 ? GDM2_BASE : GDM1_BASE) ++ ++#define REG_FE_DMA_GLO_CFG 0x0000 ++#define FE_DMA_GLO_L2_SPACE_MASK GENMASK(7, 4) ++#define FE_DMA_GLO_PG_SZ_MASK BIT(3) ++ ++#define REG_FE_RST_GLO_CFG 0x0004 ++#define FE_RST_GDM4_MBI_ARB_MASK BIT(3) ++#define FE_RST_GDM3_MBI_ARB_MASK BIT(2) ++#define FE_RST_CORE_MASK BIT(0) ++ ++#define REG_FE_WAN_MAC_H 0x0030 ++#define REG_FE_LAN_MAC_H 0x0040 ++ ++#define REG_FE_MAC_LMIN(_n) ((_n) + 0x04) ++#define REG_FE_MAC_LMAX(_n) ((_n) + 0x08) ++ ++#define REG_FE_CDM1_OQ_MAP0 0x0050 ++#define REG_FE_CDM1_OQ_MAP1 0x0054 ++#define REG_FE_CDM1_OQ_MAP2 0x0058 ++#define REG_FE_CDM1_OQ_MAP3 0x005c ++ ++#define REG_FE_PCE_CFG 0x0070 ++#define PCE_DPI_EN_MASK BIT(2) ++#define PCE_KA_EN_MASK BIT(1) ++#define PCE_MC_EN_MASK BIT(0) ++ ++#define REG_FE_PSE_QUEUE_CFG_WR 0x0080 ++#define PSE_CFG_PORT_ID_MASK GENMASK(27, 24) ++#define PSE_CFG_QUEUE_ID_MASK GENMASK(20, 16) ++#define PSE_CFG_WR_EN_MASK BIT(8) ++#define PSE_CFG_OQRSV_SEL_MASK BIT(0) ++ ++#define REG_FE_PSE_QUEUE_CFG_VAL 0x0084 ++#define PSE_CFG_OQ_RSV_MASK GENMASK(13, 0) ++ ++#define PSE_FQ_CFG 0x008c ++#define PSE_FQ_LIMIT_MASK GENMASK(14, 0) ++ ++#define REG_FE_PSE_BUF_SET 0x0090 ++#define PSE_SHARE_USED_LTHD_MASK GENMASK(31, 16) ++#define PSE_ALLRSV_MASK GENMASK(14, 0) ++ ++#define REG_PSE_SHARE_USED_THD 0x0094 ++#define PSE_SHARE_USED_MTHD_MASK GENMASK(31, 16) ++#define PSE_SHARE_USED_HTHD_MASK GENMASK(15, 0) ++ ++#define REG_GDM_MISC_CFG 0x0148 ++#define GDM2_RDM_ACK_WAIT_PREF_MASK BIT(9) ++#define GDM2_CHN_VLD_MODE_MASK BIT(5) ++ ++#define REG_FE_CSR_IFC_CFG CSR_IFC_BASE ++#define FE_IFC_EN_MASK BIT(0) ++ ++#define REG_FE_VIP_PORT_EN 0x01f0 ++#define REG_FE_IFC_PORT_EN 0x01f4 ++ ++#define REG_PSE_IQ_REV1 (PSE_BASE + 0x08) ++#define PSE_IQ_RES1_P2_MASK GENMASK(23, 16) ++ ++#define REG_PSE_IQ_REV2 (PSE_BASE + 0x0c) ++#define PSE_IQ_RES2_P5_MASK GENMASK(15, 8) ++#define PSE_IQ_RES2_P4_MASK GENMASK(7, 0) ++ ++#define REG_FE_VIP_EN(_n) (0x0300 + ((_n) << 3)) ++#define PATN_FCPU_EN_MASK BIT(7) ++#define PATN_SWP_EN_MASK BIT(6) ++#define PATN_DP_EN_MASK BIT(5) ++#define PATN_SP_EN_MASK BIT(4) ++#define PATN_TYPE_MASK GENMASK(3, 1) ++#define PATN_EN_MASK BIT(0) ++ ++#define REG_FE_VIP_PATN(_n) (0x0304 + ((_n) << 3)) ++#define PATN_DP_MASK GENMASK(31, 16) ++#define PATN_SP_MASK GENMASK(15, 0) ++ ++#define REG_CDM1_VLAN_CTRL CDM1_BASE ++#define CDM1_VLAN_MASK GENMASK(31, 16) ++ ++#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08) ++#define CDM1_VIP_QSEL_MASK GENMASK(24, 20) ++ ++#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2)) ++#define CDM1_CRSN_QSEL_REASON_MASK(_n) \ ++ GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) ++ ++#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08) ++#define CDM2_OAM_QSEL_MASK GENMASK(31, 27) ++#define CDM2_VIP_QSEL_MASK GENMASK(24, 20) ++ ++#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2)) ++#define CDM2_CRSN_QSEL_REASON_MASK(_n) \ ++ GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) ++ ++#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) ++#define GDM_DROP_CRC_ERR BIT(23) ++#define GDM_IP4_CKSUM BIT(22) ++#define GDM_TCP_CKSUM BIT(21) ++#define GDM_UDP_CKSUM BIT(20) ++#define GDM_UCFQ_MASK GENMASK(15, 12) ++#define GDM_BCFQ_MASK GENMASK(11, 8) ++#define GDM_MCFQ_MASK GENMASK(7, 4) ++#define GDM_OCFQ_MASK GENMASK(3, 0) ++ ++#define REG_GDM_INGRESS_CFG(_n) (GDM_BASE(_n) + 0x10) ++#define GDM_INGRESS_FC_EN_MASK BIT(1) ++#define GDM_STAG_EN_MASK BIT(0) ++ ++#define REG_GDM_LEN_CFG(_n) (GDM_BASE(_n) + 0x14) ++#define GDM_SHORT_LEN_MASK GENMASK(13, 0) ++#define GDM_LONG_LEN_MASK GENMASK(29, 16) ++ ++#define REG_FE_CPORT_CFG (GDM1_BASE + 0x40) ++#define FE_CPORT_PAD BIT(26) ++#define FE_CPORT_PORT_XFC_MASK BIT(25) ++#define FE_CPORT_QUEUE_XFC_MASK BIT(24) ++ ++#define REG_FE_GDM_MIB_CLEAR(_n) (GDM_BASE(_n) + 0xf0) ++#define FE_GDM_MIB_RX_CLEAR_MASK BIT(1) ++#define FE_GDM_MIB_TX_CLEAR_MASK BIT(0) ++ ++#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4) ++#define FE_STRICT_RFC2819_MODE_MASK BIT(31) ++#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17) ++#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16) ++#define FE_TX_MIB_ID_MASK GENMASK(15, 8) ++#define FE_RX_MIB_ID_MASK GENMASK(7, 0) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x104) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x10c) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x110) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x114) ++#define REG_FE_GDM_TX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x118) ++#define REG_FE_GDM_TX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x11c) ++#define REG_FE_GDM_TX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x120) ++#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x124) ++#define REG_FE_GDM_TX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x128) ++#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x12c) ++#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x130) ++#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x134) ++#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x138) ++#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x13c) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x140) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x148) ++#define REG_FE_GDM_RX_FC_DROP_CNT(_n) (GDM_BASE(_n) + 0x14c) ++#define REG_FE_GDM_RX_RC_DROP_CNT(_n) (GDM_BASE(_n) + 0x150) ++#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n) (GDM_BASE(_n) + 0x154) ++#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n) (GDM_BASE(_n) + 0x158) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x15c) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x160) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x164) ++#define REG_FE_GDM_RX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x168) ++#define REG_FE_GDM_RX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x16c) ++#define REG_FE_GDM_RX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x170) ++#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n) (GDM_BASE(_n) + 0x174) ++#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n) (GDM_BASE(_n) + 0x178) ++#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n) (GDM_BASE(_n) + 0x17c) ++#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x180) ++#define REG_FE_GDM_RX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x184) ++#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x188) ++#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x18c) ++#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x190) ++#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x194) ++#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) ++ ++#define REG_PPE1_TB_HASH_CFG (PPE1_BASE + 0x250) ++#define PPE1_SRAM_TABLE_EN_MASK BIT(0) ++#define PPE1_SRAM_HASH1_EN_MASK BIT(8) ++#define PPE1_DRAM_TABLE_EN_MASK BIT(16) ++#define PPE1_DRAM_HASH1_EN_MASK BIT(24) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) ++#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) ++#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) ++#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) ++#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) ++#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) ++#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) ++#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) ++#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) ++#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) ++#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) ++ ++#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) ++#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) ++#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) ++ ++#define REG_GDM3_FWD_CFG GDM3_BASE ++#define GDM3_PAD_EN_MASK BIT(28) ++ ++#define REG_GDM4_FWD_CFG GDM4_BASE ++#define GDM4_PAD_EN_MASK BIT(28) ++#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8) ++ ++#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x23c) ++#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16) ++#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12) ++#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8) ++ ++#define REG_IP_FRAG_FP 0x2010 ++#define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21) ++#define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16) ++#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) ++#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) ++ ++#define REG_MC_VLAN_EN 0x2100 ++#define MC_VLAN_EN_MASK BIT(0) ++ ++#define REG_MC_VLAN_CFG 0x2104 ++#define MC_VLAN_CFG_CMD_DONE_MASK BIT(31) ++#define MC_VLAN_CFG_TABLE_ID_MASK GENMASK(21, 16) ++#define MC_VLAN_CFG_PORT_ID_MASK GENMASK(11, 8) ++#define MC_VLAN_CFG_TABLE_SEL_MASK BIT(4) ++#define MC_VLAN_CFG_RW_MASK BIT(0) ++ ++#define REG_MC_VLAN_DATA 0x2108 ++ ++#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 ++ ++/* QDMA */ ++#define REG_QDMA_GLOBAL_CFG 0x0004 ++#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) ++#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) ++#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) ++#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) ++#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) ++#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) ++#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) ++#define GLOBAL_CFG_RESET_MASK BIT(23) ++#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) ++#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) ++#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) ++#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) ++#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) ++#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) ++#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) ++#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) ++#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) ++#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) ++#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) ++#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) ++#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) ++#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) ++#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) ++ ++#define REG_FWD_DSCP_BASE 0x0010 ++#define REG_FWD_BUF_BASE 0x0014 ++ ++#define REG_HW_FWD_DSCP_CFG 0x0018 ++#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) ++#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) ++#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) ++ ++#define REG_INT_STATUS(_n) \ ++ (((_n) == 4) ? 0x0730 : \ ++ ((_n) == 3) ? 0x0724 : \ ++ ((_n) == 2) ? 0x0720 : \ ++ ((_n) == 1) ? 0x0024 : 0x0020) ++ ++#define REG_INT_ENABLE(_n) \ ++ (((_n) == 4) ? 0x0750 : \ ++ ((_n) == 3) ? 0x0744 : \ ++ ((_n) == 2) ? 0x0740 : \ ++ ((_n) == 1) ? 0x002c : 0x0028) ++ ++/* QDMA_CSR_INT_ENABLE1 */ ++#define RX15_COHERENT_INT_MASK BIT(31) ++#define RX14_COHERENT_INT_MASK BIT(30) ++#define RX13_COHERENT_INT_MASK BIT(29) ++#define RX12_COHERENT_INT_MASK BIT(28) ++#define RX11_COHERENT_INT_MASK BIT(27) ++#define RX10_COHERENT_INT_MASK BIT(26) ++#define RX9_COHERENT_INT_MASK BIT(25) ++#define RX8_COHERENT_INT_MASK BIT(24) ++#define RX7_COHERENT_INT_MASK BIT(23) ++#define RX6_COHERENT_INT_MASK BIT(22) ++#define RX5_COHERENT_INT_MASK BIT(21) ++#define RX4_COHERENT_INT_MASK BIT(20) ++#define RX3_COHERENT_INT_MASK BIT(19) ++#define RX2_COHERENT_INT_MASK BIT(18) ++#define RX1_COHERENT_INT_MASK BIT(17) ++#define RX0_COHERENT_INT_MASK BIT(16) ++#define TX7_COHERENT_INT_MASK BIT(15) ++#define TX6_COHERENT_INT_MASK BIT(14) ++#define TX5_COHERENT_INT_MASK BIT(13) ++#define TX4_COHERENT_INT_MASK BIT(12) ++#define TX3_COHERENT_INT_MASK BIT(11) ++#define TX2_COHERENT_INT_MASK BIT(10) ++#define TX1_COHERENT_INT_MASK BIT(9) ++#define TX0_COHERENT_INT_MASK BIT(8) ++#define CNT_OVER_FLOW_INT_MASK BIT(7) ++#define IRQ1_FULL_INT_MASK BIT(5) ++#define IRQ1_INT_MASK BIT(4) ++#define HWFWD_DSCP_LOW_INT_MASK BIT(3) ++#define HWFWD_DSCP_EMPTY_INT_MASK BIT(2) ++#define IRQ0_FULL_INT_MASK BIT(1) ++#define IRQ0_INT_MASK BIT(0) ++ ++#define TX_DONE_INT_MASK(_n) \ ++ ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ ++ : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) ++ ++#define INT_TX_MASK \ ++ (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ ++ IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) ++ ++#define INT_IDX0_MASK \ ++ (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ ++ TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ ++ TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ ++ TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ ++ RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ ++ RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ ++ RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ ++ RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ ++ RX15_COHERENT_INT_MASK | INT_TX_MASK) ++ ++/* QDMA_CSR_INT_ENABLE2 */ ++#define RX15_NO_CPU_DSCP_INT_MASK BIT(31) ++#define RX14_NO_CPU_DSCP_INT_MASK BIT(30) ++#define RX13_NO_CPU_DSCP_INT_MASK BIT(29) ++#define RX12_NO_CPU_DSCP_INT_MASK BIT(28) ++#define RX11_NO_CPU_DSCP_INT_MASK BIT(27) ++#define RX10_NO_CPU_DSCP_INT_MASK BIT(26) ++#define RX9_NO_CPU_DSCP_INT_MASK BIT(25) ++#define RX8_NO_CPU_DSCP_INT_MASK BIT(24) ++#define RX7_NO_CPU_DSCP_INT_MASK BIT(23) ++#define RX6_NO_CPU_DSCP_INT_MASK BIT(22) ++#define RX5_NO_CPU_DSCP_INT_MASK BIT(21) ++#define RX4_NO_CPU_DSCP_INT_MASK BIT(20) ++#define RX3_NO_CPU_DSCP_INT_MASK BIT(19) ++#define RX2_NO_CPU_DSCP_INT_MASK BIT(18) ++#define RX1_NO_CPU_DSCP_INT_MASK BIT(17) ++#define RX0_NO_CPU_DSCP_INT_MASK BIT(16) ++#define RX15_DONE_INT_MASK BIT(15) ++#define RX14_DONE_INT_MASK BIT(14) ++#define RX13_DONE_INT_MASK BIT(13) ++#define RX12_DONE_INT_MASK BIT(12) ++#define RX11_DONE_INT_MASK BIT(11) ++#define RX10_DONE_INT_MASK BIT(10) ++#define RX9_DONE_INT_MASK BIT(9) ++#define RX8_DONE_INT_MASK BIT(8) ++#define RX7_DONE_INT_MASK BIT(7) ++#define RX6_DONE_INT_MASK BIT(6) ++#define RX5_DONE_INT_MASK BIT(5) ++#define RX4_DONE_INT_MASK BIT(4) ++#define RX3_DONE_INT_MASK BIT(3) ++#define RX2_DONE_INT_MASK BIT(2) ++#define RX1_DONE_INT_MASK BIT(1) ++#define RX0_DONE_INT_MASK BIT(0) ++ ++#define RX_DONE_INT_MASK \ ++ (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ ++ RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ ++ RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ ++ RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ ++ RX15_DONE_INT_MASK) ++#define INT_IDX1_MASK \ ++ (RX_DONE_INT_MASK | \ ++ RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ ++ RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ ++ RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ ++ RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ ++ RX15_NO_CPU_DSCP_INT_MASK) ++ ++/* QDMA_CSR_INT_ENABLE5 */ ++#define TX31_COHERENT_INT_MASK BIT(31) ++#define TX30_COHERENT_INT_MASK BIT(30) ++#define TX29_COHERENT_INT_MASK BIT(29) ++#define TX28_COHERENT_INT_MASK BIT(28) ++#define TX27_COHERENT_INT_MASK BIT(27) ++#define TX26_COHERENT_INT_MASK BIT(26) ++#define TX25_COHERENT_INT_MASK BIT(25) ++#define TX24_COHERENT_INT_MASK BIT(24) ++#define TX23_COHERENT_INT_MASK BIT(23) ++#define TX22_COHERENT_INT_MASK BIT(22) ++#define TX21_COHERENT_INT_MASK BIT(21) ++#define TX20_COHERENT_INT_MASK BIT(20) ++#define TX19_COHERENT_INT_MASK BIT(19) ++#define TX18_COHERENT_INT_MASK BIT(18) ++#define TX17_COHERENT_INT_MASK BIT(17) ++#define TX16_COHERENT_INT_MASK BIT(16) ++#define TX15_COHERENT_INT_MASK BIT(15) ++#define TX14_COHERENT_INT_MASK BIT(14) ++#define TX13_COHERENT_INT_MASK BIT(13) ++#define TX12_COHERENT_INT_MASK BIT(12) ++#define TX11_COHERENT_INT_MASK BIT(11) ++#define TX10_COHERENT_INT_MASK BIT(10) ++#define TX9_COHERENT_INT_MASK BIT(9) ++#define TX8_COHERENT_INT_MASK BIT(8) ++ ++#define INT_IDX4_MASK \ ++ (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ ++ TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ ++ TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ ++ TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ ++ TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ ++ TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ ++ TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ ++ TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ ++ TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ ++ TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ ++ TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ ++ TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) ++ ++#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) ++ ++#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) ++#define TX_IRQ_THR_MASK GENMASK(27, 16) ++#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) ++ ++#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) ++#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) ++ ++#define REG_IRQ_STATUS(_n) ((_n) ? 0x0068 : 0x005c) ++#define IRQ_ENTRY_LEN_MASK GENMASK(27, 16) ++#define IRQ_HEAD_IDX_MASK GENMASK(11, 0) ++ ++#define REG_TX_RING_BASE(_n) \ ++ (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) ++ ++#define REG_TX_RING_BLOCKING(_n) \ ++ (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5)) ++ ++#define TX_RING_IRQ_BLOCKING_MAP_MASK BIT(6) ++#define TX_RING_IRQ_BLOCKING_CFG_MASK BIT(4) ++#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK BIT(2) ++#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK BIT(1) ++#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK BIT(0) ++ ++#define REG_TX_CPU_IDX(_n) \ ++ (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) ++ ++#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) ++ ++#define REG_TX_DMA_IDX(_n) \ ++ (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) ++ ++#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) ++ ++#define IRQ_RING_IDX_MASK GENMASK(20, 16) ++#define IRQ_DESC_IDX_MASK GENMASK(15, 0) ++ ++#define REG_RX_RING_BASE(_n) \ ++ (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) ++ ++#define REG_RX_RING_SIZE(_n) \ ++ (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) ++ ++#define RX_RING_THR_MASK GENMASK(31, 16) ++#define RX_RING_SIZE_MASK GENMASK(15, 0) ++ ++#define REG_RX_CPU_IDX(_n) \ ++ (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) ++ ++#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) ++ ++#define REG_RX_DMA_IDX(_n) \ ++ (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) ++ ++#define REG_RX_DELAY_INT_IDX(_n) \ ++ (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) ++ ++#define RX_DELAY_INT_MASK GENMASK(15, 0) ++ ++#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) ++ ++#define REG_INGRESS_TRTCM_CFG 0x0070 ++#define INGRESS_TRTCM_EN_MASK BIT(31) ++#define INGRESS_TRTCM_MODE_MASK BIT(30) ++#define INGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define INGRESS_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_QUEUE_CLOSE_CFG(_n) (0x00a0 + ((_n) & 0xfc)) ++#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) BIT((_m) + (((_n) & 0x3) << 3)) ++ ++#define REG_TXQ_DIS_CFG_BASE(_n) ((_n) ? 0x20a0 : 0x00a0) ++#define REG_TXQ_DIS_CFG(_n, _m) (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2) ++ ++#define REG_CNTR_CFG(_n) (0x0400 + ((_n) << 3)) ++#define CNTR_EN_MASK BIT(31) ++#define CNTR_ALL_CHAN_EN_MASK BIT(30) ++#define CNTR_ALL_QUEUE_EN_MASK BIT(29) ++#define CNTR_ALL_DSCP_RING_EN_MASK BIT(28) ++#define CNTR_SRC_MASK GENMASK(27, 24) ++#define CNTR_DSCP_RING_MASK GENMASK(20, 16) ++#define CNTR_CHAN_MASK GENMASK(7, 3) ++#define CNTR_QUEUE_MASK GENMASK(2, 0) ++ ++#define REG_CNTR_VAL(_n) (0x0404 + ((_n) << 3)) ++ ++#define REG_LMGR_INIT_CFG 0x1000 ++#define LMGR_INIT_START BIT(31) ++#define LMGR_SRAM_MODE_MASK BIT(30) ++#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) ++#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) ++ ++#define REG_FWD_DSCP_LOW_THR 0x1004 ++#define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) ++ ++#define REG_EGRESS_RATE_METER_CFG 0x100c ++#define EGRESS_RATE_METER_EN_MASK BIT(31) ++#define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) ++#define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) ++#define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) ++ ++#define REG_EGRESS_TRTCM_CFG 0x1010 ++#define EGRESS_TRTCM_EN_MASK BIT(31) ++#define EGRESS_TRTCM_MODE_MASK BIT(30) ++#define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define EGRESS_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define TRTCM_PARAM_RW_MASK BIT(31) ++#define TRTCM_PARAM_RW_DONE_MASK BIT(30) ++#define TRTCM_PARAM_TYPE_MASK GENMASK(29, 28) ++#define TRTCM_METER_GROUP_MASK GENMASK(27, 26) ++#define TRTCM_PARAM_INDEX_MASK GENMASK(23, 17) ++#define TRTCM_PARAM_RATE_TYPE_MASK BIT(16) ++ ++#define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4) ++#define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) ++#define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) ++ ++#define REG_TXWRR_MODE_CFG 0x1020 ++#define TWRR_WEIGHT_SCALE_MASK BIT(31) ++#define TWRR_WEIGHT_BASE_MASK BIT(3) ++ ++#define REG_TXWRR_WEIGHT_CFG 0x1024 ++#define TWRR_RW_CMD_MASK BIT(31) ++#define TWRR_RW_CMD_DONE BIT(30) ++#define TWRR_CHAN_IDX_MASK GENMASK(23, 19) ++#define TWRR_QUEUE_IDX_MASK GENMASK(18, 16) ++#define TWRR_VALUE_MASK GENMASK(15, 0) ++ ++#define REG_PSE_BUF_USAGE_CFG 0x1028 ++#define PSE_BUF_ESTIMATE_EN_MASK BIT(29) ++ ++#define REG_CHAN_QOS_MODE(_n) (0x1040 + ((_n) << 2)) ++#define CHAN_QOS_MODE_MASK(_n) GENMASK(2 + ((_n) << 2), (_n) << 2) ++ ++#define REG_GLB_TRTCM_CFG 0x1080 ++#define GLB_TRTCM_EN_MASK BIT(31) ++#define GLB_TRTCM_MODE_MASK BIT(30) ++#define GLB_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define GLB_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_TXQ_CNGST_CFG 0x10a0 ++#define TXQ_CNGST_DROP_EN BIT(31) ++#define TXQ_CNGST_DEI_DROP_EN BIT(30) ++ ++#define REG_SLA_TRTCM_CFG 0x1150 ++#define SLA_TRTCM_EN_MASK BIT(31) ++#define SLA_TRTCM_MODE_MASK BIT(30) ++#define SLA_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define SLA_FAST_TICK_MASK GENMASK(15, 0) ++ ++/* CTRL */ ++#define QDMA_DESC_DONE_MASK BIT(31) ++#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ ++#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ ++#define QDMA_DESC_DEI_MASK BIT(25) ++#define QDMA_DESC_NO_DROP_MASK BIT(24) ++#define QDMA_DESC_LEN_MASK GENMASK(15, 0) ++/* DATA */ ++#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) ++/* TX MSG0 */ ++#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) ++#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) ++#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) ++#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) ++#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) ++#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) ++#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) ++#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) ++#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) ++#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) ++/* TX MSG1 */ ++#define QDMA_ETH_TXMSG_NO_DROP BIT(31) ++#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ ++#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) ++#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) ++#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) ++#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) ++#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) ++#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ ++#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ ++ ++/* RX MSG1 */ ++#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) ++#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) ++#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) ++#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) ++#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) ++#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) ++#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) ++#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) ++#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) ++ ++struct airoha_qdma_desc { ++ __le32 rsv; ++ __le32 ctrl; ++ __le32 addr; ++ __le32 data; ++ __le32 msg0; ++ __le32 msg1; ++ __le32 msg2; ++ __le32 msg3; ++}; ++ ++/* CTRL0 */ ++#define QDMA_FWD_DESC_CTX_MASK BIT(31) ++#define QDMA_FWD_DESC_RING_MASK GENMASK(30, 28) ++#define QDMA_FWD_DESC_IDX_MASK GENMASK(27, 16) ++#define QDMA_FWD_DESC_LEN_MASK GENMASK(15, 0) ++/* CTRL1 */ ++#define QDMA_FWD_DESC_FIRST_IDX_MASK GENMASK(15, 0) ++/* CTRL2 */ ++#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK GENMASK(2, 0) ++ ++struct airoha_qdma_fwd_desc { ++ __le32 addr; ++ __le32 ctrl0; ++ __le32 ctrl1; ++ __le32 ctrl2; ++ __le32 msg0; ++ __le32 msg1; ++ __le32 rsv0; ++ __le32 rsv1; ++}; ++ ++enum { ++ QDMA_INT_REG_IDX0, ++ QDMA_INT_REG_IDX1, ++ QDMA_INT_REG_IDX2, ++ QDMA_INT_REG_IDX3, ++ QDMA_INT_REG_IDX4, ++ QDMA_INT_REG_MAX ++}; ++ ++enum { ++ XSI_PCIE0_PORT, ++ XSI_PCIE1_PORT, ++ XSI_USB_PORT, ++ XSI_AE_PORT, ++ XSI_ETH_PORT, ++}; ++ ++enum { ++ XSI_PCIE0_VIP_PORT_MASK = BIT(22), ++ XSI_PCIE1_VIP_PORT_MASK = BIT(23), ++ XSI_USB_VIP_PORT_MASK = BIT(25), ++ XSI_ETH_VIP_PORT_MASK = BIT(24), ++}; ++ ++enum { ++ DEV_STATE_INITIALIZED, ++}; ++ ++enum { ++ CDM_CRSN_QSEL_Q1 = 1, ++ CDM_CRSN_QSEL_Q5 = 5, ++ CDM_CRSN_QSEL_Q6 = 6, ++ CDM_CRSN_QSEL_Q15 = 15, ++}; ++ ++enum { ++ CRSN_08 = 0x8, ++ CRSN_21 = 0x15, /* KA */ ++ CRSN_22 = 0x16, /* hit bind and force route to CPU */ ++ CRSN_24 = 0x18, ++ CRSN_25 = 0x19, ++}; ++ ++enum { ++ FE_PSE_PORT_CDM1, ++ FE_PSE_PORT_GDM1, ++ FE_PSE_PORT_GDM2, ++ FE_PSE_PORT_GDM3, ++ FE_PSE_PORT_PPE1, ++ FE_PSE_PORT_CDM2, ++ FE_PSE_PORT_CDM3, ++ FE_PSE_PORT_CDM4, ++ FE_PSE_PORT_PPE2, ++ FE_PSE_PORT_GDM4, ++ FE_PSE_PORT_CDM5, ++ FE_PSE_PORT_DROP = 0xf, ++}; ++ ++enum tx_sched_mode { ++ TC_SCH_WRR8, ++ TC_SCH_SP, ++ TC_SCH_WRR7, ++ TC_SCH_WRR6, ++ TC_SCH_WRR5, ++ TC_SCH_WRR4, ++ TC_SCH_WRR3, ++ TC_SCH_WRR2, ++}; ++ ++enum trtcm_param_type { ++ TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ ++ TRTCM_TOKEN_RATE_MODE, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, ++ TRTCM_BUCKET_COUNTER_MODE, ++}; ++ ++enum trtcm_mode_type { ++ TRTCM_COMMIT_MODE, ++ TRTCM_PEAK_MODE, ++}; ++ ++enum trtcm_param { ++ TRTCM_TICK_SEL = BIT(0), ++ TRTCM_PKT_MODE = BIT(1), ++ TRTCM_METER_MODE = BIT(2), ++}; ++ ++#define MIN_TOKEN_SIZE 4096 ++#define MAX_TOKEN_SIZE_OFFSET 17 ++#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6) ++#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) ++ ++struct airoha_queue_entry { ++ union { ++ void *buf; ++ struct sk_buff *skb; ++ }; ++ dma_addr_t dma_addr; ++ u16 dma_len; ++}; ++ ++struct airoha_queue { ++ struct airoha_qdma *qdma; ++ ++ /* protect concurrent queue accesses */ ++ spinlock_t lock; ++ struct airoha_queue_entry *entry; ++ struct airoha_qdma_desc *desc; ++ u16 head; ++ u16 tail; ++ ++ int queued; ++ int ndesc; ++ int free_thr; ++ int buf_size; ++ ++ struct napi_struct napi; ++ struct page_pool *page_pool; ++}; ++ ++struct airoha_tx_irq_queue { ++ struct airoha_qdma *qdma; ++ ++ struct napi_struct napi; ++ ++ int size; ++ u32 *q; ++}; ++ ++struct airoha_hw_stats { ++ /* protect concurrent hw_stats accesses */ ++ spinlock_t lock; ++ struct u64_stats_sync syncp; ++ ++ /* get_stats64 */ ++ u64 rx_ok_pkts; ++ u64 tx_ok_pkts; ++ u64 rx_ok_bytes; ++ u64 tx_ok_bytes; ++ u64 rx_multicast; ++ u64 rx_errors; ++ u64 rx_drops; ++ u64 tx_drops; ++ u64 rx_crc_error; ++ u64 rx_over_errors; ++ /* ethtool stats */ ++ u64 tx_broadcast; ++ u64 tx_multicast; ++ u64 tx_len[7]; ++ u64 rx_broadcast; ++ u64 rx_fragment; ++ u64 rx_jabber; ++ u64 rx_len[7]; ++}; ++ ++struct airoha_qdma { ++ struct airoha_eth *eth; ++ void __iomem *regs; ++ ++ /* protect concurrent irqmask accesses */ ++ spinlock_t irq_lock; ++ u32 irqmask[QDMA_INT_REG_MAX]; ++ int irq; ++ ++ struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; ++ ++ struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; ++ struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; ++ ++ /* descriptor and packet buffers for qdma hw forward */ ++ struct { ++ void *desc; ++ void *q; ++ } hfwd; ++}; ++ ++struct airoha_gdm_port { ++ struct airoha_qdma *qdma; ++ struct net_device *dev; ++ int id; ++ ++ struct airoha_hw_stats stats; ++ ++ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); ++ ++ /* qos stats counters */ ++ u64 cpu_tx_packets; ++ u64 fwd_tx_packets; ++}; ++ ++struct airoha_eth { ++ struct device *dev; ++ ++ unsigned long state; ++ void __iomem *fe_regs; ++ ++ struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; ++ struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; ++ ++ struct net_device *napi_dev; ++ ++ struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; ++ struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; ++}; ++ ++static u32 airoha_rr(void __iomem *base, u32 offset) ++{ ++ return readl(base + offset); ++} ++ ++static void airoha_wr(void __iomem *base, u32 offset, u32 val) ++{ ++ writel(val, base + offset); ++} ++ ++static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) ++{ ++ val |= (airoha_rr(base, offset) & ~mask); ++ airoha_wr(base, offset, val); ++ ++ return val; ++} ++ ++#define airoha_fe_rr(eth, offset) \ ++ airoha_rr((eth)->fe_regs, (offset)) ++#define airoha_fe_wr(eth, offset, val) \ ++ airoha_wr((eth)->fe_regs, (offset), (val)) ++#define airoha_fe_rmw(eth, offset, mask, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) ++#define airoha_fe_set(eth, offset, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), 0, (val)) ++#define airoha_fe_clear(eth, offset, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), (val), 0) ++ ++#define airoha_qdma_rr(qdma, offset) \ ++ airoha_rr((qdma)->regs, (offset)) ++#define airoha_qdma_wr(qdma, offset, val) \ ++ airoha_wr((qdma)->regs, (offset), (val)) ++#define airoha_qdma_rmw(qdma, offset, mask, val) \ ++ airoha_rmw((qdma)->regs, (offset), (mask), (val)) ++#define airoha_qdma_set(qdma, offset, val) \ ++ airoha_rmw((qdma)->regs, (offset), 0, (val)) ++#define airoha_qdma_clear(qdma, offset, val) \ ++ airoha_rmw((qdma)->regs, (offset), (val), 0) ++ ++static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index, ++ u32 clear, u32 set) ++{ ++ unsigned long flags; ++ ++ if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask))) ++ return; ++ ++ spin_lock_irqsave(&qdma->irq_lock, flags); ++ ++ qdma->irqmask[index] &= ~clear; ++ qdma->irqmask[index] |= set; ++ airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]); ++ /* Read irq_enable register in order to guarantee the update above ++ * completes in the spinlock critical section. ++ */ ++ airoha_qdma_rr(qdma, REG_INT_ENABLE(index)); ++ ++ spin_unlock_irqrestore(&qdma->irq_lock, flags); ++} ++ ++static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index, ++ u32 mask) ++{ ++ airoha_qdma_set_irqmask(qdma, index, 0, mask); ++} ++ ++static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index, ++ u32 mask) ++{ ++ airoha_qdma_set_irqmask(qdma, index, mask, 0); ++} ++ ++static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) ++{ ++ /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. ++ * GDM{2,3,4} can be used as wan port connected to an external ++ * phy module. ++ */ ++ return port->id == 1; ++} ++ ++static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ u32 val, reg; ++ ++ reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H ++ : REG_FE_WAN_MAC_H; ++ val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; ++ airoha_fe_wr(eth, reg, val); ++ ++ val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); ++ airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); ++} ++ ++static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, ++ u32 val) ++{ ++ airoha_fe_rmw(eth, addr, GDM_OCFQ_MASK, ++ FIELD_PREP(GDM_OCFQ_MASK, val)); ++ airoha_fe_rmw(eth, addr, GDM_MCFQ_MASK, ++ FIELD_PREP(GDM_MCFQ_MASK, val)); ++ airoha_fe_rmw(eth, addr, GDM_BCFQ_MASK, ++ FIELD_PREP(GDM_BCFQ_MASK, val)); ++ airoha_fe_rmw(eth, addr, GDM_UCFQ_MASK, ++ FIELD_PREP(GDM_UCFQ_MASK, val)); ++} ++ ++static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable) ++{ ++ u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP; ++ u32 vip_port, cfg_addr; ++ ++ switch (port) { ++ case XSI_PCIE0_PORT: ++ vip_port = XSI_PCIE0_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(3); ++ break; ++ case XSI_PCIE1_PORT: ++ vip_port = XSI_PCIE1_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(3); ++ break; ++ case XSI_USB_PORT: ++ vip_port = XSI_USB_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(4); ++ break; ++ case XSI_ETH_PORT: ++ vip_port = XSI_ETH_VIP_PORT_MASK; ++ cfg_addr = REG_GDM_FWD_CFG(4); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (enable) { ++ airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); ++ airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); ++ } else { ++ airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, vip_port); ++ airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port); ++ } ++ ++ airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val); ++ ++ return 0; ++} ++ ++static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable) ++{ ++ const int port_list[] = { ++ XSI_PCIE0_PORT, ++ XSI_PCIE1_PORT, ++ XSI_USB_PORT, ++ XSI_ETH_PORT ++ }; ++ int i, err; ++ ++ for (i = 0; i < ARRAY_SIZE(port_list); i++) { ++ err = airoha_set_gdm_port(eth, port_list[i], enable); ++ if (err) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ for (i--; i >= 0; i--) ++ airoha_set_gdm_port(eth, port_list[i], false); ++ ++ return err; ++} ++ ++static void airoha_fe_maccr_init(struct airoha_eth *eth) ++{ ++ int p; ++ ++ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(p), ++ GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | ++ GDM_DROP_CRC_ERR); ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p), ++ FE_PSE_PORT_CDM1); ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, 4004)); ++ } ++ ++ airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, ++ FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); ++ ++ airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD); ++} ++ ++static void airoha_fe_vip_setup(struct airoha_eth *eth) ++{ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(3), ETH_P_PPP_DISC); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(3), PATN_FCPU_EN_MASK | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(4), PPP_LCP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(4), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(6), PPP_IPCP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(6), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(7), PPP_CHAP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(7), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ /* BOOTP (0x43) */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(8), 0x43); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(8), ++ PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ /* BOOTP (0x44) */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(9), 0x44); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(9), ++ PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ /* ISAKMP */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(10), 0x1f401f4); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(10), ++ PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(11), PPP_IPV6CP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(11), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ /* DHCPv6 */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(12), 0x2220223); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(12), ++ PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | ++ FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(19), PPP_PAP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(19), ++ PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | ++ PATN_EN_MASK); ++ ++ /* ETH->ETH_P_1905 (0x893a) */ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(20), 0x893a); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(20), ++ PATN_FCPU_EN_MASK | PATN_EN_MASK); ++ ++ airoha_fe_wr(eth, REG_FE_VIP_PATN(21), ETH_P_LLDP); ++ airoha_fe_wr(eth, REG_FE_VIP_EN(21), ++ PATN_FCPU_EN_MASK | PATN_EN_MASK); ++} ++ ++static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth, ++ u32 port, u32 queue) ++{ ++ u32 val; ++ ++ airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, ++ PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK, ++ FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | ++ FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue)); ++ val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL); ++ ++ return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val); ++} ++ ++static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, ++ u32 port, u32 queue, u32 val) ++{ ++ airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_VAL, PSE_CFG_OQ_RSV_MASK, ++ FIELD_PREP(PSE_CFG_OQ_RSV_MASK, val)); ++ airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, ++ PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK | ++ PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK, ++ FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | ++ FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue) | ++ PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK); ++} ++ ++static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth) ++{ ++ u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); ++ ++ return FIELD_GET(PSE_ALLRSV_MASK, val); ++} ++ ++static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, ++ u32 port, u32 queue, u32 val) ++{ ++ u32 orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue); ++ u32 tmp, all_rsv, fq_limit; ++ ++ airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val); ++ ++ /* modify all rsv */ ++ all_rsv = airoha_fe_get_pse_all_rsv(eth); ++ all_rsv += (val - orig_val); ++ airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK, ++ FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); ++ ++ /* modify hthd */ ++ tmp = airoha_fe_rr(eth, PSE_FQ_CFG); ++ fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp); ++ tmp = fq_limit - all_rsv - 0x20; ++ airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, ++ PSE_SHARE_USED_HTHD_MASK, ++ FIELD_PREP(PSE_SHARE_USED_HTHD_MASK, tmp)); ++ ++ tmp = fq_limit - all_rsv - 0x100; ++ airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, ++ PSE_SHARE_USED_MTHD_MASK, ++ FIELD_PREP(PSE_SHARE_USED_MTHD_MASK, tmp)); ++ tmp = (3 * tmp) >> 2; ++ airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, ++ PSE_SHARE_USED_LTHD_MASK, ++ FIELD_PREP(PSE_SHARE_USED_LTHD_MASK, tmp)); ++ ++ return 0; ++} ++ ++static void airoha_fe_pse_ports_init(struct airoha_eth *eth) ++{ ++ const u32 pse_port_num_queues[] = { ++ [FE_PSE_PORT_CDM1] = 6, ++ [FE_PSE_PORT_GDM1] = 6, ++ [FE_PSE_PORT_GDM2] = 32, ++ [FE_PSE_PORT_GDM3] = 6, ++ [FE_PSE_PORT_PPE1] = 4, ++ [FE_PSE_PORT_CDM2] = 6, ++ [FE_PSE_PORT_CDM3] = 8, ++ [FE_PSE_PORT_CDM4] = 10, ++ [FE_PSE_PORT_PPE2] = 4, ++ [FE_PSE_PORT_GDM4] = 2, ++ [FE_PSE_PORT_CDM5] = 2, ++ }; ++ u32 all_rsv; ++ int q; ++ ++ all_rsv = airoha_fe_get_pse_all_rsv(eth); ++ /* hw misses PPE2 oq rsv */ ++ all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]; ++ airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv); ++ ++ /* CMD1 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* GMD1 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* GMD2 */ ++ for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0); ++ /* GMD3 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* PPE1 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { ++ if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, ++ PSE_QUEUE_RSV_PAGES); ++ else ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 0); ++ } ++ /* CDM2 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM2]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM2, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* CDM3 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM3] - 1; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM3, q, 0); ++ /* CDM4 */ ++ for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* PPE2 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) { ++ if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, ++ PSE_QUEUE_RSV_PAGES); ++ else ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0); ++ } ++ /* GMD4 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q, ++ PSE_QUEUE_RSV_PAGES); ++ /* CDM5 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM5]; q++) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM5, q, ++ PSE_QUEUE_RSV_PAGES); ++} ++ ++static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_FE_MC_MAX_VLAN_TABLE; i++) { ++ int err, j; ++ u32 val; ++ ++ airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); ++ ++ val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | ++ MC_VLAN_CFG_TABLE_SEL_MASK | MC_VLAN_CFG_RW_MASK; ++ airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); ++ err = read_poll_timeout(airoha_fe_rr, val, ++ val & MC_VLAN_CFG_CMD_DONE_MASK, ++ USEC_PER_MSEC, 5 * USEC_PER_MSEC, ++ false, eth, REG_MC_VLAN_CFG); ++ if (err) ++ return err; ++ ++ for (j = 0; j < AIROHA_FE_MC_MAX_VLAN_PORT; j++) { ++ airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); ++ ++ val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | ++ FIELD_PREP(MC_VLAN_CFG_PORT_ID_MASK, j) | ++ MC_VLAN_CFG_RW_MASK; ++ airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); ++ err = read_poll_timeout(airoha_fe_rr, val, ++ val & MC_VLAN_CFG_CMD_DONE_MASK, ++ USEC_PER_MSEC, ++ 5 * USEC_PER_MSEC, false, eth, ++ REG_MC_VLAN_CFG); ++ if (err) ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth) ++{ ++ /* CDM1_CRSN_QSEL */ ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), ++ CDM_CRSN_QSEL_Q6)); ++ airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2), ++ CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), ++ FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), ++ CDM_CRSN_QSEL_Q1)); ++ /* CDM2_CRSN_QSEL */ ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), ++ CDM_CRSN_QSEL_Q1)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), ++ CDM_CRSN_QSEL_Q6)); ++ airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2), ++ CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), ++ FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), ++ CDM_CRSN_QSEL_Q1)); ++} ++ ++static int airoha_fe_init(struct airoha_eth *eth) ++{ ++ airoha_fe_maccr_init(eth); ++ ++ /* PSE IQ reserve */ ++ airoha_fe_rmw(eth, REG_PSE_IQ_REV1, PSE_IQ_RES1_P2_MASK, ++ FIELD_PREP(PSE_IQ_RES1_P2_MASK, 0x10)); ++ airoha_fe_rmw(eth, REG_PSE_IQ_REV2, ++ PSE_IQ_RES2_P5_MASK | PSE_IQ_RES2_P4_MASK, ++ FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) | ++ FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34)); ++ ++ /* enable FE copy engine for MC/KA/DPI */ ++ airoha_fe_wr(eth, REG_FE_PCE_CFG, ++ PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK); ++ /* set vip queue selection to ring 1 */ ++ airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK, ++ FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4)); ++ airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK, ++ FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4)); ++ /* set GDM4 source interface offset to 8 */ ++ airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET, ++ GDM4_SPORT_OFF2_MASK | ++ GDM4_SPORT_OFF1_MASK | ++ GDM4_SPORT_OFF0_MASK, ++ FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) | ++ FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) | ++ FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8)); ++ ++ /* set PSE Page as 128B */ ++ airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG, ++ FE_DMA_GLO_L2_SPACE_MASK | FE_DMA_GLO_PG_SZ_MASK, ++ FIELD_PREP(FE_DMA_GLO_L2_SPACE_MASK, 2) | ++ FE_DMA_GLO_PG_SZ_MASK); ++ airoha_fe_wr(eth, REG_FE_RST_GLO_CFG, ++ FE_RST_CORE_MASK | FE_RST_GDM3_MBI_ARB_MASK | ++ FE_RST_GDM4_MBI_ARB_MASK); ++ usleep_range(1000, 2000); ++ ++ /* connect RxRing1 and RxRing15 to PSE Port0 OQ-1 ++ * connect other rings to PSE Port0 OQ-0 ++ */ ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP0, BIT(4)); ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP1, BIT(28)); ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP2, BIT(4)); ++ airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP3, BIT(28)); ++ ++ airoha_fe_vip_setup(eth); ++ airoha_fe_pse_ports_init(eth); ++ ++ airoha_fe_set(eth, REG_GDM_MISC_CFG, ++ GDM2_RDM_ACK_WAIT_PREF_MASK | ++ GDM2_CHN_VLD_MODE_MASK); ++ airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, ++ FIELD_PREP(CDM2_OAM_QSEL_MASK, 15)); ++ ++ /* init fragment and assemble Force Port */ ++ /* NPU Core-3, NPU Bridge Channel-3 */ ++ airoha_fe_rmw(eth, REG_IP_FRAG_FP, ++ IP_FRAGMENT_PORT_MASK | IP_FRAGMENT_NBQ_MASK, ++ FIELD_PREP(IP_FRAGMENT_PORT_MASK, 6) | ++ FIELD_PREP(IP_FRAGMENT_NBQ_MASK, 3)); ++ /* QDMA LAN, RX Ring-22 */ ++ airoha_fe_rmw(eth, REG_IP_FRAG_FP, ++ IP_ASSEMBLE_PORT_MASK | IP_ASSEMBLE_NBQ_MASK, ++ FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | ++ FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); ++ ++ airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK); ++ ++ airoha_fe_crsn_qsel_init(eth); ++ ++ airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK); ++ airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK); ++ ++ /* default aging mode for mbi unlock issue */ ++ airoha_fe_rmw(eth, REG_GDM2_CHN_RLS, ++ MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK, ++ FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) | ++ FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3)); ++ ++ /* disable IFC by default */ ++ airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); ++ ++ /* enable 1:N vlan action, init vlan table */ ++ airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); ++ ++ return airoha_fe_mc_vlan_clear(eth); ++} ++ ++static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) ++{ ++ enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ int qid = q - &qdma->q_rx[0]; ++ int nframes = 0; ++ ++ while (q->queued < q->ndesc - 1) { ++ struct airoha_queue_entry *e = &q->entry[q->head]; ++ struct airoha_qdma_desc *desc = &q->desc[q->head]; ++ struct page *page; ++ int offset; ++ u32 val; ++ ++ page = page_pool_dev_alloc_frag(q->page_pool, &offset, ++ q->buf_size); ++ if (!page) ++ break; ++ ++ q->head = (q->head + 1) % q->ndesc; ++ q->queued++; ++ nframes++; ++ ++ e->buf = page_address(page) + offset; ++ e->dma_addr = page_pool_get_dma_addr(page) + offset; ++ e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); ++ ++ dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len, ++ dir); ++ ++ val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); ++ WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); ++ WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr)); ++ val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, q->head); ++ WRITE_ONCE(desc->data, cpu_to_le32(val)); ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ WRITE_ONCE(desc->msg2, 0); ++ WRITE_ONCE(desc->msg3, 0); ++ ++ airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), ++ RX_RING_CPU_IDX_MASK, ++ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); ++ } ++ ++ return nframes; ++} ++ ++static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, ++ struct airoha_qdma_desc *desc) ++{ ++ u32 port, sport, msg1 = le32_to_cpu(desc->msg1); ++ ++ sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); ++ switch (sport) { ++ case 0x10 ... 0x13: ++ port = 0; ++ break; ++ case 0x2 ... 0x4: ++ port = sport - 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; ++} ++ ++static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) ++{ ++ enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ int qid = q - &qdma->q_rx[0]; ++ int done = 0; ++ ++ while (done < budget) { ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ struct airoha_qdma_desc *desc = &q->desc[q->tail]; ++ dma_addr_t dma_addr = le32_to_cpu(desc->addr); ++ u32 desc_ctrl = le32_to_cpu(desc->ctrl); ++ struct sk_buff *skb; ++ int len, p; ++ ++ if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) ++ break; ++ ++ if (!dma_addr) ++ break; ++ ++ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); ++ if (!len) ++ break; ++ ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ ++ dma_sync_single_for_cpu(eth->dev, dma_addr, ++ SKB_WITH_OVERHEAD(q->buf_size), dir); ++ ++ p = airoha_qdma_get_gdm_port(eth, desc); ++ if (p < 0 || !eth->ports[p]) { ++ page_pool_put_full_page(q->page_pool, ++ virt_to_head_page(e->buf), ++ true); ++ continue; ++ } ++ ++ skb = napi_build_skb(e->buf, q->buf_size); ++ if (!skb) { ++ page_pool_put_full_page(q->page_pool, ++ virt_to_head_page(e->buf), ++ true); ++ break; ++ } ++ ++ skb_reserve(skb, 2); ++ __skb_put(skb, len); ++ skb_mark_for_recycle(skb); ++ skb->dev = eth->ports[p]->dev; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb_record_rx_queue(skb, qid); ++ napi_gro_receive(&q->napi, skb); ++ ++ done++; ++ } ++ airoha_qdma_fill_rx_queue(q); ++ ++ return done; ++} ++ ++static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) ++{ ++ struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); ++ int cur, done = 0; ++ ++ do { ++ cur = airoha_qdma_rx_process(q, budget - done); ++ done += cur; ++ } while (cur && done < budget); ++ ++ if (done < budget && napi_complete(napi)) ++ airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1, ++ RX_DONE_INT_MASK); ++ ++ return done; ++} ++ ++static int airoha_qdma_init_rx_queue(struct airoha_queue *q, ++ struct airoha_qdma *qdma, int ndesc) ++{ ++ const struct page_pool_params pp_params = { ++ .order = 0, ++ .pool_size = 256, ++ .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, ++ .dma_dir = DMA_FROM_DEVICE, ++ .max_len = PAGE_SIZE, ++ .nid = NUMA_NO_NODE, ++ .dev = qdma->eth->dev, ++ .napi = &q->napi, ++ }; ++ struct airoha_eth *eth = qdma->eth; ++ int qid = q - &qdma->q_rx[0], thr; ++ dma_addr_t dma_addr; ++ ++ q->buf_size = PAGE_SIZE / 2; ++ q->qdma = qdma; ++ ++ q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry), ++ GFP_KERNEL); ++ if (!q->entry) ++ return -ENOMEM; ++ ++ q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc), ++ &dma_addr, GFP_KERNEL); ++ if (!q->desc) ++ return -ENOMEM; ++ ++ q->page_pool = page_pool_create(&pp_params); ++ if (IS_ERR(q->page_pool)) { ++ int err = PTR_ERR(q->page_pool); ++ ++ q->page_pool = NULL; ++ return err; ++ } ++ ++ q->ndesc = ndesc; ++ netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); ++ ++ airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); ++ airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), ++ RX_RING_SIZE_MASK, ++ FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); ++ ++ thr = clamp(ndesc >> 3, 1, 32); ++ airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, ++ FIELD_PREP(RX_RING_THR_MASK, thr)); ++ airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, ++ FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); ++ ++ airoha_qdma_fill_rx_queue(q); ++ ++ return 0; ++} ++ ++static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) ++{ ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ int qid = q - &qdma->q_rx[0]; ++ ++ while (q->queued) { ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ struct airoha_qdma_desc *desc = &q->desc[q->tail]; ++ struct page *page = virt_to_head_page(e->buf); ++ ++ dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, ++ page_pool_get_dma_dir(q->page_pool)); ++ page_pool_put_full_page(q->page_pool, page, false); ++ /* Reset DMA descriptor */ ++ WRITE_ONCE(desc->ctrl, 0); ++ WRITE_ONCE(desc->addr, 0); ++ WRITE_ONCE(desc->data, 0); ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ WRITE_ONCE(desc->msg2, 0); ++ WRITE_ONCE(desc->msg3, 0); ++ ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ } ++ ++ q->head = q->tail; ++ /* Set RX_DMA_IDX to RX_CPU_IDX to notify the hw the QDMA RX ring is ++ * empty. ++ */ ++ airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, ++ FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); ++ airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, ++ FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail)); ++} ++ ++static int airoha_qdma_init_rx(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ int err; ++ ++ if (!(RX_DONE_INT_MASK & BIT(i))) { ++ /* rx-queue not binded to irq */ ++ continue; ++ } ++ ++ err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma, ++ RX_DSCP_NUM(i)); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) ++{ ++ struct airoha_tx_irq_queue *irq_q; ++ int id, done = 0, irq_queued; ++ struct airoha_qdma *qdma; ++ struct airoha_eth *eth; ++ u32 status, head; ++ ++ irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); ++ qdma = irq_q->qdma; ++ id = irq_q - &qdma->q_tx_irq[0]; ++ eth = qdma->eth; ++ ++ status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(id)); ++ head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); ++ head = head % irq_q->size; ++ irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); ++ ++ while (irq_queued > 0 && done < budget) { ++ u32 qid, val = irq_q->q[head]; ++ struct airoha_qdma_desc *desc; ++ struct airoha_queue_entry *e; ++ struct airoha_queue *q; ++ u32 index, desc_ctrl; ++ struct sk_buff *skb; ++ ++ if (val == 0xff) ++ break; ++ ++ irq_q->q[head] = 0xff; /* mark as done */ ++ head = (head + 1) % irq_q->size; ++ irq_queued--; ++ done++; ++ ++ qid = FIELD_GET(IRQ_RING_IDX_MASK, val); ++ if (qid >= ARRAY_SIZE(qdma->q_tx)) ++ continue; ++ ++ q = &qdma->q_tx[qid]; ++ if (!q->ndesc) ++ continue; ++ ++ index = FIELD_GET(IRQ_DESC_IDX_MASK, val); ++ if (index >= q->ndesc) ++ continue; ++ ++ spin_lock_bh(&q->lock); ++ ++ if (!q->queued) ++ goto unlock; ++ ++ desc = &q->desc[index]; ++ desc_ctrl = le32_to_cpu(desc->ctrl); ++ ++ if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && ++ !(desc_ctrl & QDMA_DESC_DROP_MASK)) ++ goto unlock; ++ ++ e = &q->entry[index]; ++ skb = e->skb; ++ ++ dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, ++ DMA_TO_DEVICE); ++ memset(e, 0, sizeof(*e)); ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ q->queued--; ++ ++ /* completion ring can report out-of-order indexes if hw QoS ++ * is enabled and packets with different priority are queued ++ * to same DMA ring. Take into account possible out-of-order ++ * reports incrementing DMA ring tail pointer ++ */ ++ while (q->tail != q->head && !q->entry[q->tail].dma_addr) ++ q->tail = (q->tail + 1) % q->ndesc; ++ ++ if (skb) { ++ u16 queue = skb_get_queue_mapping(skb); ++ struct netdev_queue *txq; ++ ++ txq = netdev_get_tx_queue(skb->dev, queue); ++ netdev_tx_completed_queue(txq, 1, skb->len); ++ if (netif_tx_queue_stopped(txq) && ++ q->ndesc - q->queued >= q->free_thr) ++ netif_tx_wake_queue(txq); ++ ++ dev_kfree_skb_any(skb); ++ } ++unlock: ++ spin_unlock_bh(&q->lock); ++ } ++ ++ if (done) { ++ int i, len = done >> 7; ++ ++ for (i = 0; i < len; i++) ++ airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), ++ IRQ_CLEAR_LEN_MASK, 0x80); ++ airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), ++ IRQ_CLEAR_LEN_MASK, (done & 0x7f)); ++ } ++ ++ if (done < budget && napi_complete(napi)) ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, ++ TX_DONE_INT_MASK(id)); ++ ++ return done; ++} ++ ++static int airoha_qdma_init_tx_queue(struct airoha_queue *q, ++ struct airoha_qdma *qdma, int size) ++{ ++ struct airoha_eth *eth = qdma->eth; ++ int i, qid = q - &qdma->q_tx[0]; ++ dma_addr_t dma_addr; ++ ++ spin_lock_init(&q->lock); ++ q->ndesc = size; ++ q->qdma = qdma; ++ q->free_thr = 1 + MAX_SKB_FRAGS; ++ ++ q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), ++ GFP_KERNEL); ++ if (!q->entry) ++ return -ENOMEM; ++ ++ q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), ++ &dma_addr, GFP_KERNEL); ++ if (!q->desc) ++ return -ENOMEM; ++ ++ for (i = 0; i < q->ndesc; i++) { ++ u32 val; ++ ++ val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); ++ WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); ++ } ++ ++ /* xmit ring drop default setting */ ++ airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), ++ TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK); ++ ++ airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); ++ airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); ++ airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, ++ FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, ++ struct airoha_qdma *qdma, int size) ++{ ++ int id = irq_q - &qdma->q_tx_irq[0]; ++ struct airoha_eth *eth = qdma->eth; ++ dma_addr_t dma_addr; ++ ++ netif_napi_add_tx(eth->napi_dev, &irq_q->napi, ++ airoha_qdma_tx_napi_poll); ++ irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), ++ &dma_addr, GFP_KERNEL); ++ if (!irq_q->q) ++ return -ENOMEM; ++ ++ memset(irq_q->q, 0xff, size * sizeof(u32)); ++ irq_q->size = size; ++ irq_q->qdma = qdma; ++ ++ airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); ++ airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, ++ FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); ++ airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK, ++ FIELD_PREP(TX_IRQ_THR_MASK, 1)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_init_tx(struct airoha_qdma *qdma) ++{ ++ int i, err; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { ++ err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma, ++ IRQ_QUEUE_LEN(i)); ++ if (err) ++ return err; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma, ++ TX_DSCP_NUM); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) ++{ ++ struct airoha_eth *eth = q->qdma->eth; ++ ++ spin_lock_bh(&q->lock); ++ while (q->queued) { ++ struct airoha_queue_entry *e = &q->entry[q->tail]; ++ ++ dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, ++ DMA_TO_DEVICE); ++ dev_kfree_skb_any(e->skb); ++ e->skb = NULL; ++ ++ q->tail = (q->tail + 1) % q->ndesc; ++ q->queued--; ++ } ++ spin_unlock_bh(&q->lock); ++} ++ ++static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) ++{ ++ struct airoha_eth *eth = qdma->eth; ++ dma_addr_t dma_addr; ++ u32 status; ++ int size; ++ ++ size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); ++ qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL); ++ if (!qdma->hfwd.desc) ++ return -ENOMEM; ++ ++ airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); ++ ++ size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; ++ qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL); ++ if (!qdma->hfwd.q) ++ return -ENOMEM; ++ ++ airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); ++ ++ airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, ++ HW_FWD_DSCP_PAYLOAD_SIZE_MASK, ++ FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); ++ airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, ++ FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); ++ airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, ++ LMGR_INIT_START | LMGR_SRAM_MODE_MASK | ++ HW_FWD_DESC_NUM_MASK, ++ FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | ++ LMGR_INIT_START); ++ ++ return read_poll_timeout(airoha_qdma_rr, status, ++ !(status & LMGR_INIT_START), USEC_PER_MSEC, ++ 30 * USEC_PER_MSEC, true, qdma, ++ REG_LMGR_INIT_CFG); ++} ++ ++static void airoha_qdma_init_qos(struct airoha_qdma *qdma) ++{ ++ airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); ++ airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); ++ ++ airoha_qdma_clear(qdma, REG_PSE_BUF_USAGE_CFG, ++ PSE_BUF_ESTIMATE_EN_MASK); ++ ++ airoha_qdma_set(qdma, REG_EGRESS_RATE_METER_CFG, ++ EGRESS_RATE_METER_EN_MASK | ++ EGRESS_RATE_METER_EQ_RATE_EN_MASK); ++ /* 2047us x 31 = 63.457ms */ ++ airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, ++ EGRESS_RATE_METER_WINDOW_SZ_MASK, ++ FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f)); ++ airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, ++ EGRESS_RATE_METER_TIMESLICE_MASK, ++ FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff)); ++ ++ /* ratelimit init */ ++ airoha_qdma_set(qdma, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK); ++ /* fast-tick 25us */ ++ airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK, ++ FIELD_PREP(GLB_FAST_TICK_MASK, 25)); ++ airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40)); ++ ++ airoha_qdma_set(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK); ++ airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK, ++ FIELD_PREP(EGRESS_FAST_TICK_MASK, 25)); ++ airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, ++ EGRESS_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40)); ++ ++ airoha_qdma_set(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK); ++ airoha_qdma_clear(qdma, REG_INGRESS_TRTCM_CFG, ++ INGRESS_TRTCM_MODE_MASK); ++ airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK, ++ FIELD_PREP(INGRESS_FAST_TICK_MASK, 125)); ++ airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, ++ INGRESS_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8)); ++ ++ airoha_qdma_set(qdma, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK); ++ airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK, ++ FIELD_PREP(SLA_FAST_TICK_MASK, 25)); ++ airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK, ++ FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); ++} ++ ++static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_NUM_QOS_CHANNELS; i++) { ++ /* Tx-cpu transferred count */ ++ airoha_qdma_wr(qdma, REG_CNTR_VAL(i << 1), 0); ++ airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), ++ CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | ++ CNTR_ALL_DSCP_RING_EN_MASK | ++ FIELD_PREP(CNTR_CHAN_MASK, i)); ++ /* Tx-fwd transferred count */ ++ airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); ++ airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), ++ CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | ++ CNTR_ALL_DSCP_RING_EN_MASK | ++ FIELD_PREP(CNTR_SRC_MASK, 1) | ++ FIELD_PREP(CNTR_CHAN_MASK, i)); ++ } ++} ++ ++static int airoha_qdma_hw_init(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ /* clear pending irqs */ ++ for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) ++ airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); ++ ++ /* setup irqs */ ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK); ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK); ++ airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK); ++ ++ /* setup irq binding */ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) ++ continue; ++ ++ if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i)) ++ airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(i), ++ TX_RING_IRQ_BLOCKING_CFG_MASK); ++ else ++ airoha_qdma_clear(qdma, REG_TX_RING_BLOCKING(i), ++ TX_RING_IRQ_BLOCKING_CFG_MASK); ++ } ++ ++ airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_RX_2B_OFFSET_MASK | ++ FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | ++ GLOBAL_CFG_CPU_TXR_RR_MASK | ++ GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | ++ GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK | ++ GLOBAL_CFG_MULTICAST_EN_MASK | ++ GLOBAL_CFG_IRQ0_EN_MASK | GLOBAL_CFG_IRQ1_EN_MASK | ++ GLOBAL_CFG_TX_WB_DONE_MASK | ++ FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2)); ++ ++ airoha_qdma_init_qos(qdma); ++ ++ /* disable qdma rx delay interrupt */ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), ++ RX_DELAY_INT_MASK); ++ } ++ ++ airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG, ++ TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN); ++ airoha_qdma_init_qos_stats(qdma); ++ ++ return 0; ++} ++ ++static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) ++{ ++ struct airoha_qdma *qdma = dev_instance; ++ u32 intr[ARRAY_SIZE(qdma->irqmask)]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) { ++ intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); ++ intr[i] &= qdma->irqmask[i]; ++ airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); ++ } ++ ++ if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) ++ return IRQ_NONE; ++ ++ if (intr[1] & RX_DONE_INT_MASK) { ++ airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1, ++ RX_DONE_INT_MASK); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ if (intr[1] & BIT(i)) ++ napi_schedule(&qdma->q_rx[i].napi); ++ } ++ } ++ ++ if (intr[0] & INT_TX_MASK) { ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { ++ if (!(intr[0] & TX_DONE_INT_MASK(i))) ++ continue; ++ ++ airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0, ++ TX_DONE_INT_MASK(i)); ++ napi_schedule(&qdma->q_tx_irq[i].napi); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int airoha_qdma_init(struct platform_device *pdev, ++ struct airoha_eth *eth, ++ struct airoha_qdma *qdma) ++{ ++ int err, id = qdma - ð->qdma[0]; ++ const char *res; ++ ++ spin_lock_init(&qdma->irq_lock); ++ qdma->eth = eth; ++ ++ res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id); ++ if (!res) ++ return -ENOMEM; ++ ++ qdma->regs = devm_platform_ioremap_resource_byname(pdev, res); ++ if (IS_ERR(qdma->regs)) ++ return dev_err_probe(eth->dev, PTR_ERR(qdma->regs), ++ "failed to iomap qdma%d regs\n", id); ++ ++ qdma->irq = platform_get_irq(pdev, 4 * id); ++ if (qdma->irq < 0) ++ return qdma->irq; ++ ++ err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler, ++ IRQF_SHARED, KBUILD_MODNAME, qdma); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_init_rx(qdma); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_init_tx(qdma); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_init_hfwd_queues(qdma); ++ if (err) ++ return err; ++ ++ return airoha_qdma_hw_init(qdma); ++} ++ ++static int airoha_hw_init(struct platform_device *pdev, ++ struct airoha_eth *eth) ++{ ++ int err, i; ++ ++ /* disable xsi */ ++ err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), ++ eth->xsi_rsts); ++ if (err) ++ return err; ++ ++ err = reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts); ++ if (err) ++ return err; ++ ++ msleep(20); ++ err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); ++ if (err) ++ return err; ++ ++ msleep(20); ++ err = airoha_fe_init(eth); ++ if (err) ++ return err; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { ++ err = airoha_qdma_init(pdev, eth, ð->qdma[i]); ++ if (err) ++ return err; ++ } ++ ++ set_bit(DEV_STATE_INITIALIZED, ð->state); ++ ++ return 0; ++} ++ ++static void airoha_hw_cleanup(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ netif_napi_del(&qdma->q_rx[i].napi); ++ airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]); ++ if (qdma->q_rx[i].page_pool) ++ page_pool_destroy(qdma->q_rx[i].page_pool); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) ++ netif_napi_del(&qdma->q_tx_irq[i].napi); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) ++ continue; ++ ++ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); ++ } ++} ++ ++static void airoha_qdma_start_napi(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) ++ napi_enable(&qdma->q_tx_irq[i].napi); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ napi_enable(&qdma->q_rx[i].napi); ++ } ++} ++ ++static void airoha_qdma_stop_napi(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) ++ napi_disable(&qdma->q_tx_irq[i].napi); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ napi_disable(&qdma->q_rx[i].napi); ++ } ++} ++ ++static void airoha_update_hw_stats(struct airoha_gdm_port *port) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ u32 val, i = 0; ++ ++ spin_lock(&port->stats.lock); ++ u64_stats_update_begin(&port->stats.syncp); ++ ++ /* TX */ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); ++ port->stats.tx_ok_pkts += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); ++ port->stats.tx_ok_pkts += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); ++ port->stats.tx_ok_bytes += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); ++ port->stats.tx_ok_bytes += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); ++ port->stats.tx_drops += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); ++ port->stats.tx_broadcast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); ++ port->stats.tx_multicast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); ++ port->stats.tx_len[i] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); ++ port->stats.tx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); ++ port->stats.tx_len[i++] += val; ++ ++ /* RX */ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); ++ port->stats.rx_ok_pkts += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); ++ port->stats.rx_ok_pkts += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); ++ port->stats.rx_ok_bytes += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); ++ port->stats.rx_ok_bytes += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); ++ port->stats.rx_drops += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); ++ port->stats.rx_broadcast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); ++ port->stats.rx_multicast += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); ++ port->stats.rx_errors += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); ++ port->stats.rx_crc_error += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); ++ port->stats.rx_over_errors += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); ++ port->stats.rx_fragment += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); ++ port->stats.rx_jabber += val; ++ ++ i = 0; ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); ++ port->stats.rx_len[i] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); ++ port->stats.rx_len[i] += ((u64)val << 32); ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); ++ port->stats.rx_len[i++] += val; ++ ++ /* reset mib counters */ ++ airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id), ++ FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); ++ ++ u64_stats_update_end(&port->stats.syncp); ++ spin_unlock(&port->stats.lock); ++} ++ ++static int airoha_dev_open(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_qdma *qdma = port->qdma; ++ int err; ++ ++ netif_tx_start_all_queues(dev); ++ err = airoha_set_gdm_ports(qdma->eth, true); ++ if (err) ++ return err; ++ ++ if (netdev_uses_dsa(dev)) ++ airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), ++ GDM_STAG_EN_MASK); ++ else ++ airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), ++ GDM_STAG_EN_MASK); ++ ++ airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_TX_DMA_EN_MASK | ++ GLOBAL_CFG_RX_DMA_EN_MASK); ++ ++ return 0; ++} ++ ++static int airoha_dev_stop(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_qdma *qdma = port->qdma; ++ int i, err; ++ ++ netif_tx_disable(dev); ++ err = airoha_set_gdm_ports(qdma->eth, false); ++ if (err) ++ return err; ++ ++ airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_TX_DMA_EN_MASK | ++ GLOBAL_CFG_RX_DMA_EN_MASK); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) ++ continue; ++ ++ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); ++ netdev_tx_reset_subqueue(dev, i); ++ } ++ ++ return 0; ++} ++ ++static int airoha_dev_set_macaddr(struct net_device *dev, void *p) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ int err; ++ ++ err = eth_mac_addr(dev, p); ++ if (err) ++ return err; ++ ++ airoha_set_macaddr(port, dev->dev_addr); ++ ++ return 0; ++} ++ ++static int airoha_dev_init(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ ++ airoha_set_macaddr(port, dev->dev_addr); ++ ++ return 0; ++} ++ ++static void airoha_dev_get_stats64(struct net_device *dev, ++ struct rtnl_link_stats64 *storage) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ unsigned int start; ++ ++ airoha_update_hw_stats(port); ++ do { ++ start = u64_stats_fetch_begin(&port->stats.syncp); ++ storage->rx_packets = port->stats.rx_ok_pkts; ++ storage->tx_packets = port->stats.tx_ok_pkts; ++ storage->rx_bytes = port->stats.rx_ok_bytes; ++ storage->tx_bytes = port->stats.tx_ok_bytes; ++ storage->multicast = port->stats.rx_multicast; ++ storage->rx_errors = port->stats.rx_errors; ++ storage->rx_dropped = port->stats.rx_drops; ++ storage->tx_dropped = port->stats.tx_drops; ++ storage->rx_crc_errors = port->stats.rx_crc_error; ++ storage->rx_over_errors = port->stats.rx_over_errors; ++ } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++} ++ ++static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, ++ struct net_device *sb_dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ int queue, channel; ++ ++ /* For dsa device select QoS channel according to the dsa user port ++ * index, rely on port id otherwise. Select QoS queue based on the ++ * skb priority. ++ */ ++ channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; ++ channel = channel % AIROHA_NUM_QOS_CHANNELS; ++ queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ ++ queue = channel * AIROHA_NUM_QOS_QUEUES + queue; ++ ++ return queue < dev->num_tx_queues ? queue : 0; ++} ++ ++static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags; ++ u32 msg0, msg1, len = skb_headlen(skb); ++ struct airoha_qdma *qdma = port->qdma; ++ struct netdev_queue *txq; ++ struct airoha_queue *q; ++ void *data = skb->data; ++ int i, qid; ++ u16 index; ++ u8 fport; ++ ++ qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); ++ msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, ++ qid / AIROHA_NUM_QOS_QUEUES) | ++ FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK, ++ qid % AIROHA_NUM_QOS_QUEUES); ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) | ++ FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) | ++ FIELD_PREP(QDMA_ETH_TXMSG_ICO_MASK, 1); ++ ++ /* TSO: fill MSS info in tcp checksum field */ ++ if (skb_is_gso(skb)) { ++ if (skb_cow_head(skb, 0)) ++ goto error; ++ ++ if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | ++ SKB_GSO_TCPV6)) { ++ __be16 csum = cpu_to_be16(skb_shinfo(skb)->gso_size); ++ ++ tcp_hdr(skb)->check = (__force __sum16)csum; ++ msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1); ++ } ++ } ++ ++ fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; ++ msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | ++ FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); ++ ++ q = &qdma->q_tx[qid]; ++ if (WARN_ON_ONCE(!q->ndesc)) ++ goto error; ++ ++ spin_lock_bh(&q->lock); ++ ++ txq = netdev_get_tx_queue(dev, qid); ++ if (q->queued + nr_frags > q->ndesc) { ++ /* not enough space in the queue */ ++ netif_tx_stop_queue(txq); ++ spin_unlock_bh(&q->lock); ++ return NETDEV_TX_BUSY; ++ } ++ ++ index = q->head; ++ for (i = 0; i < nr_frags; i++) { ++ struct airoha_qdma_desc *desc = &q->desc[index]; ++ struct airoha_queue_entry *e = &q->entry[index]; ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ dma_addr_t addr; ++ u32 val; ++ ++ addr = dma_map_single(dev->dev.parent, data, len, ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(dev->dev.parent, addr))) ++ goto error_unmap; ++ ++ index = (index + 1) % q->ndesc; ++ ++ val = FIELD_PREP(QDMA_DESC_LEN_MASK, len); ++ if (i < nr_frags - 1) ++ val |= FIELD_PREP(QDMA_DESC_MORE_MASK, 1); ++ WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); ++ WRITE_ONCE(desc->addr, cpu_to_le32(addr)); ++ val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index); ++ WRITE_ONCE(desc->data, cpu_to_le32(val)); ++ WRITE_ONCE(desc->msg0, cpu_to_le32(msg0)); ++ WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); ++ WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); ++ ++ e->skb = i ? NULL : skb; ++ e->dma_addr = addr; ++ e->dma_len = len; ++ ++ data = skb_frag_address(frag); ++ len = skb_frag_size(frag); ++ } ++ ++ q->head = index; ++ q->queued += i; ++ ++ skb_tx_timestamp(skb); ++ netdev_tx_sent_queue(txq, skb->len); ++ ++ if (netif_xmit_stopped(txq) || !netdev_xmit_more()) ++ airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), ++ TX_RING_CPU_IDX_MASK, ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); ++ ++ if (q->ndesc - q->queued < q->free_thr) ++ netif_tx_stop_queue(txq); ++ ++ spin_unlock_bh(&q->lock); ++ ++ return NETDEV_TX_OK; ++ ++error_unmap: ++ for (i--; i >= 0; i--) { ++ index = (q->head + i) % q->ndesc; ++ dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr, ++ q->entry[index].dma_len, DMA_TO_DEVICE); ++ } ++ ++ spin_unlock_bh(&q->lock); ++error: ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ ++ return NETDEV_TX_OK; ++} ++ ++static void airoha_ethtool_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; ++ ++ strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); ++ strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); ++} ++ ++static void airoha_ethtool_get_mac_stats(struct net_device *dev, ++ struct ethtool_eth_mac_stats *stats) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ unsigned int start; ++ ++ airoha_update_hw_stats(port); ++ do { ++ start = u64_stats_fetch_begin(&port->stats.syncp); ++ stats->MulticastFramesXmittedOK = port->stats.tx_multicast; ++ stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; ++ stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; ++ } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++} ++ ++static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { ++ { 0, 64 }, ++ { 65, 127 }, ++ { 128, 255 }, ++ { 256, 511 }, ++ { 512, 1023 }, ++ { 1024, 1518 }, ++ { 1519, 10239 }, ++ {}, ++}; ++ ++static void ++airoha_ethtool_get_rmon_stats(struct net_device *dev, ++ struct ethtool_rmon_stats *stats, ++ const struct ethtool_rmon_hist_range **ranges) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_hw_stats *hw_stats = &port->stats; ++ unsigned int start; ++ ++ BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != ++ ARRAY_SIZE(hw_stats->tx_len) + 1); ++ BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != ++ ARRAY_SIZE(hw_stats->rx_len) + 1); ++ ++ *ranges = airoha_ethtool_rmon_ranges; ++ airoha_update_hw_stats(port); ++ do { ++ int i; ++ ++ start = u64_stats_fetch_begin(&port->stats.syncp); ++ stats->fragments = hw_stats->rx_fragment; ++ stats->jabbers = hw_stats->rx_jabber; ++ for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1; ++ i++) { ++ stats->hist[i] = hw_stats->rx_len[i]; ++ stats->hist_tx[i] = hw_stats->tx_len[i]; ++ } ++ } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++} ++ ++static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port, ++ int channel, enum tx_sched_mode mode, ++ const u16 *weights, u8 n_weights) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_NUM_TX_RING; i++) ++ airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel), ++ TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); ++ ++ for (i = 0; i < n_weights; i++) { ++ u32 status; ++ int err; ++ ++ airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG, ++ TWRR_RW_CMD_MASK | ++ FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | ++ FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | ++ FIELD_PREP(TWRR_VALUE_MASK, weights[i])); ++ err = read_poll_timeout(airoha_qdma_rr, status, ++ status & TWRR_RW_CMD_DONE, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, ++ true, port->qdma, ++ REG_TXWRR_WEIGHT_CFG); ++ if (err) ++ return err; ++ } ++ ++ airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), ++ CHAN_QOS_MODE_MASK(channel), ++ mode << __ffs(CHAN_QOS_MODE_MASK(channel))); ++ ++ return 0; ++} ++ ++static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port, ++ int channel) ++{ ++ static const u16 w[AIROHA_NUM_QOS_QUEUES] = {}; ++ ++ return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w, ++ ARRAY_SIZE(w)); ++} ++ ++static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port, ++ int channel, ++ struct tc_ets_qopt_offload *opt) ++{ ++ struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; ++ enum tx_sched_mode mode = TC_SCH_SP; ++ u16 w[AIROHA_NUM_QOS_QUEUES] = {}; ++ int i, nstrict = 0, nwrr, qidx; ++ ++ if (p->bands > AIROHA_NUM_QOS_QUEUES) ++ return -EINVAL; ++ ++ for (i = 0; i < p->bands; i++) { ++ if (!p->quanta[i]) ++ nstrict++; ++ } ++ ++ /* this configuration is not supported by the hw */ ++ if (nstrict == AIROHA_NUM_QOS_QUEUES - 1) ++ return -EINVAL; ++ ++ /* EN7581 SoC supports fixed QoS band priority where WRR queues have ++ * lowest priorities with respect to SP ones. ++ * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn ++ */ ++ nwrr = p->bands - nstrict; ++ qidx = nstrict && nwrr ? nstrict : 0; ++ for (i = 1; i <= p->bands; i++) { ++ if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx) ++ return -EINVAL; ++ ++ qidx = i == nwrr ? 0 : qidx + 1; ++ } ++ ++ for (i = 0; i < nwrr; i++) ++ w[i] = p->weights[nstrict + i]; ++ ++ if (!nstrict) ++ mode = TC_SCH_WRR8; ++ else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1) ++ mode = nstrict + 1; ++ ++ return airoha_qdma_set_chan_tx_sched(port, channel, mode, w, ++ ARRAY_SIZE(w)); ++} ++ ++static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port, ++ int channel, ++ struct tc_ets_qopt_offload *opt) ++{ ++ u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, ++ REG_CNTR_VAL(channel << 1)); ++ u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, ++ REG_CNTR_VAL((channel << 1) + 1)); ++ u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + ++ (fwd_tx_packets - port->fwd_tx_packets); ++ _bstats_update(opt->stats.bstats, 0, tx_packets); ++ ++ port->cpu_tx_packets = cpu_tx_packets; ++ port->fwd_tx_packets = fwd_tx_packets; ++ ++ return 0; ++} ++ ++static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, ++ struct tc_ets_qopt_offload *opt) ++{ ++ int channel = TC_H_MAJ(opt->handle) >> 16; ++ ++ if (opt->parent == TC_H_ROOT) ++ return -EINVAL; ++ ++ switch (opt->command) { ++ case TC_ETS_REPLACE: ++ return airoha_qdma_set_tx_ets_sched(port, channel, opt); ++ case TC_ETS_DESTROY: ++ /* PRIO is default qdisc scheduler */ ++ return airoha_qdma_set_tx_prio_sched(port, channel); ++ case TC_ETS_STATS: ++ return airoha_qdma_get_tx_ets_stats(port, channel, opt); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, ++ u32 addr, enum trtcm_param_type param, ++ enum trtcm_mode_type mode, ++ u32 *val_low, u32 *val_high) ++{ ++ u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); ++ u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | ++ FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | ++ FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ if (read_poll_timeout(airoha_qdma_rr, val, ++ val & TRTCM_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, ++ qdma, REG_TRTCM_CFG_PARAM(addr))) ++ return -ETIMEDOUT; ++ ++ *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); ++ if (val_high) ++ *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel, ++ u32 addr, enum trtcm_param_type param, ++ enum trtcm_mode_type mode, u32 val) ++{ ++ u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); ++ u32 config = TRTCM_PARAM_RW_MASK | ++ FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | ++ FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | ++ FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ ++ return read_poll_timeout(airoha_qdma_rr, val, ++ val & TRTCM_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, ++ qdma, REG_TRTCM_CFG_PARAM(addr)); ++} ++ ++static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel, ++ u32 addr, enum trtcm_mode_type mode, ++ bool enable, u32 enable_mask) ++{ ++ u32 val; ++ ++ if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, ++ mode, &val, NULL)) ++ return -EINVAL; ++ ++ val = enable ? val | enable_mask : val & ~enable_mask; ++ ++ return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, ++ mode, val); ++} ++ ++static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma, ++ int channel, u32 addr, ++ enum trtcm_mode_type mode, ++ u32 rate_val, u32 bucket_size) ++{ ++ u32 val, config, tick, unit, rate, rate_frac; ++ int err; ++ ++ if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, ++ mode, &config, NULL)) ++ return -EINVAL; ++ ++ val = airoha_qdma_rr(qdma, addr); ++ tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); ++ if (config & TRTCM_TICK_SEL) ++ tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); ++ if (!tick) ++ return -EINVAL; ++ ++ unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; ++ if (!unit) ++ return -EINVAL; ++ ++ rate = rate_val / unit; ++ rate_frac = rate_val % unit; ++ rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; ++ rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | ++ FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); ++ ++ err = airoha_qdma_set_trtcm_param(qdma, channel, addr, ++ TRTCM_TOKEN_RATE_MODE, mode, rate); ++ if (err) ++ return err; ++ ++ val = max_t(u32, bucket_size, MIN_TOKEN_SIZE); ++ val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); ++ ++ return airoha_qdma_set_trtcm_param(qdma, channel, addr, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, ++ mode, val); ++} ++ ++static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port, ++ int channel, u32 rate, ++ u32 bucket_size) ++{ ++ int i, err; ++ ++ for (i = 0; i <= TRTCM_PEAK_MODE; i++) { ++ err = airoha_qdma_set_trtcm_config(port->qdma, channel, ++ REG_EGRESS_TRTCM_CFG, i, ++ !!rate, TRTCM_METER_MODE); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel, ++ REG_EGRESS_TRTCM_CFG, ++ i, rate, bucket_size); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ ++ struct net_device *dev = port->dev; ++ int num_tx_queues = dev->real_num_tx_queues; ++ int err; ++ ++ if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { ++ NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); ++ return -EINVAL; ++ } ++ ++ err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum); ++ if (err) { ++ NL_SET_ERR_MSG_MOD(opt->extack, ++ "failed configuring htb offload"); ++ return err; ++ } ++ ++ if (opt->command == TC_HTB_NODE_MODIFY) ++ return 0; ++ ++ err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); ++ if (err) { ++ airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum); ++ NL_SET_ERR_MSG_MOD(opt->extack, ++ "failed setting real_num_tx_queues"); ++ return err; ++ } ++ ++ set_bit(channel, port->qos_sq_bmap); ++ opt->qid = AIROHA_NUM_TX_RING + channel; ++ ++ return 0; ++} ++ ++static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue) ++{ ++ struct net_device *dev = port->dev; ++ ++ netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); ++ airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0); ++ clear_bit(queue, port->qos_sq_bmap); ++} ++ ++static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ ++ if (!test_bit(channel, port->qos_sq_bmap)) { ++ NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); ++ return -EINVAL; ++ } ++ ++ airoha_tc_remove_htb_queue(port, channel); ++ ++ return 0; ++} ++ ++static int airoha_tc_htb_destroy(struct airoha_gdm_port *port) ++{ ++ int q; ++ ++ for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) ++ airoha_tc_remove_htb_queue(port, q); ++ ++ return 0; ++} ++ ++static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ ++ if (!test_bit(channel, port->qos_sq_bmap)) { ++ NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); ++ return -EINVAL; ++ } ++ ++ opt->qid = channel; ++ ++ return 0; ++} ++ ++static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port, ++ struct tc_htb_qopt_offload *opt) ++{ ++ switch (opt->command) { ++ case TC_HTB_CREATE: ++ break; ++ case TC_HTB_DESTROY: ++ return airoha_tc_htb_destroy(port); ++ case TC_HTB_NODE_MODIFY: ++ case TC_HTB_LEAF_ALLOC_QUEUE: ++ return airoha_tc_htb_alloc_leaf_queue(port, opt); ++ case TC_HTB_LEAF_DEL: ++ case TC_HTB_LEAF_DEL_LAST: ++ case TC_HTB_LEAF_DEL_LAST_FORCE: ++ return airoha_tc_htb_delete_leaf_queue(port, opt); ++ case TC_HTB_LEAF_QUERY_QUEUE: ++ return airoha_tc_get_htb_get_leaf_queue(port, opt); ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, ++ void *type_data) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ ++ switch (type) { ++ case TC_SETUP_QDISC_ETS: ++ return airoha_tc_setup_qdisc_ets(port, type_data); ++ case TC_SETUP_QDISC_HTB: ++ return airoha_tc_setup_qdisc_htb(port, type_data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static const struct net_device_ops airoha_netdev_ops = { ++ .ndo_init = airoha_dev_init, ++ .ndo_open = airoha_dev_open, ++ .ndo_stop = airoha_dev_stop, ++ .ndo_select_queue = airoha_dev_select_queue, ++ .ndo_start_xmit = airoha_dev_xmit, ++ .ndo_get_stats64 = airoha_dev_get_stats64, ++ .ndo_set_mac_address = airoha_dev_set_macaddr, ++ .ndo_setup_tc = airoha_dev_tc_setup, ++}; ++ ++static const struct ethtool_ops airoha_ethtool_ops = { ++ .get_drvinfo = airoha_ethtool_get_drvinfo, ++ .get_eth_mac_stats = airoha_ethtool_get_mac_stats, ++ .get_rmon_stats = airoha_ethtool_get_rmon_stats, ++}; ++ ++static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np) ++{ ++ const __be32 *id_ptr = of_get_property(np, "reg", NULL); ++ struct airoha_gdm_port *port; ++ struct airoha_qdma *qdma; ++ struct net_device *dev; ++ int err, index; ++ u32 id; ++ ++ if (!id_ptr) { ++ dev_err(eth->dev, "missing gdm port id\n"); ++ return -EINVAL; ++ } ++ ++ id = be32_to_cpup(id_ptr); ++ index = id - 1; ++ ++ if (!id || id > ARRAY_SIZE(eth->ports)) { ++ dev_err(eth->dev, "invalid gdm port id: %d\n", id); ++ return -EINVAL; ++ } ++ ++ if (eth->ports[index]) { ++ dev_err(eth->dev, "duplicate gdm port id: %d\n", id); ++ return -EINVAL; ++ } ++ ++ dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), ++ AIROHA_NUM_NETDEV_TX_RINGS, ++ AIROHA_NUM_RX_RING); ++ if (!dev) { ++ dev_err(eth->dev, "alloc_etherdev failed\n"); ++ return -ENOMEM; ++ } ++ ++ qdma = ð->qdma[index % AIROHA_MAX_NUM_QDMA]; ++ dev->netdev_ops = &airoha_netdev_ops; ++ dev->ethtool_ops = &airoha_ethtool_ops; ++ dev->max_mtu = AIROHA_MAX_MTU; ++ dev->watchdog_timeo = 5 * HZ; ++ dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | ++ NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | ++ NETIF_F_SG | NETIF_F_TSO | ++ NETIF_F_HW_TC; ++ dev->features |= dev->hw_features; ++ dev->dev.of_node = np; ++ dev->irq = qdma->irq; ++ SET_NETDEV_DEV(dev, eth->dev); ++ ++ /* reserve hw queues for HTB offloading */ ++ err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING); ++ if (err) ++ return err; ++ ++ err = of_get_ethdev_address(np, dev); ++ if (err) { ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ eth_hw_addr_random(dev); ++ dev_info(eth->dev, "generated random MAC address %pM\n", ++ dev->dev_addr); ++ } ++ ++ port = netdev_priv(dev); ++ u64_stats_init(&port->stats.syncp); ++ spin_lock_init(&port->stats.lock); ++ port->qdma = qdma; ++ port->dev = dev; ++ port->id = id; ++ eth->ports[index] = port; ++ ++ return register_netdev(dev); ++} ++ ++static int airoha_probe(struct platform_device *pdev) ++{ ++ struct device_node *np; ++ struct airoha_eth *eth; ++ int i, err; ++ ++ eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL); ++ if (!eth) ++ return -ENOMEM; ++ ++ eth->dev = &pdev->dev; ++ ++ err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32)); ++ if (err) { ++ dev_err(eth->dev, "failed configuring DMA mask\n"); ++ return err; ++ } ++ ++ eth->fe_regs = devm_platform_ioremap_resource_byname(pdev, "fe"); ++ if (IS_ERR(eth->fe_regs)) ++ return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs), ++ "failed to iomap fe regs\n"); ++ ++ eth->rsts[0].id = "fe"; ++ eth->rsts[1].id = "pdma"; ++ eth->rsts[2].id = "qdma"; ++ err = devm_reset_control_bulk_get_exclusive(eth->dev, ++ ARRAY_SIZE(eth->rsts), ++ eth->rsts); ++ if (err) { ++ dev_err(eth->dev, "failed to get bulk reset lines\n"); ++ return err; ++ } ++ ++ eth->xsi_rsts[0].id = "xsi-mac"; ++ eth->xsi_rsts[1].id = "hsi0-mac"; ++ eth->xsi_rsts[2].id = "hsi1-mac"; ++ eth->xsi_rsts[3].id = "hsi-mac"; ++ eth->xsi_rsts[4].id = "xfp-mac"; ++ err = devm_reset_control_bulk_get_exclusive(eth->dev, ++ ARRAY_SIZE(eth->xsi_rsts), ++ eth->xsi_rsts); ++ if (err) { ++ dev_err(eth->dev, "failed to get bulk xsi reset lines\n"); ++ return err; ++ } ++ ++ eth->napi_dev = alloc_netdev_dummy(0); ++ if (!eth->napi_dev) ++ return -ENOMEM; ++ ++ /* Enable threaded NAPI by default */ ++ eth->napi_dev->threaded = true; ++ strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name)); ++ platform_set_drvdata(pdev, eth); ++ ++ err = airoha_hw_init(pdev, eth); ++ if (err) ++ goto error_hw_cleanup; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_qdma_start_napi(ð->qdma[i]); ++ ++ for_each_child_of_node(pdev->dev.of_node, np) { ++ if (!of_device_is_compatible(np, "airoha,eth-mac")) ++ continue; ++ ++ if (!of_device_is_available(np)) ++ continue; ++ ++ err = airoha_alloc_gdm_port(eth, np); ++ if (err) { ++ of_node_put(np); ++ goto error_napi_stop; ++ } ++ } ++ ++ return 0; ++ ++error_napi_stop: ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_qdma_stop_napi(ð->qdma[i]); ++error_hw_cleanup: ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_hw_cleanup(ð->qdma[i]); ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ ++ if (port && port->dev->reg_state == NETREG_REGISTERED) ++ unregister_netdev(port->dev); ++ } ++ free_netdev(eth->napi_dev); ++ platform_set_drvdata(pdev, NULL); ++ ++ return err; ++} ++ ++static void airoha_remove(struct platform_device *pdev) ++{ ++ struct airoha_eth *eth = platform_get_drvdata(pdev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { ++ airoha_qdma_stop_napi(ð->qdma[i]); ++ airoha_hw_cleanup(ð->qdma[i]); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ ++ if (!port) ++ continue; ++ ++ unregister_netdev(port->dev); ++ } ++ free_netdev(eth->napi_dev); ++ ++ platform_set_drvdata(pdev, NULL); ++} ++ ++static const struct of_device_id of_airoha_match[] = { ++ { .compatible = "airoha,en7581-eth" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, of_airoha_match); ++ ++static struct platform_driver airoha_driver = { ++ .probe = airoha_probe, ++ .remove_new = airoha_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = of_airoha_match, ++ }, ++}; ++module_platform_driver(airoha_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_DESCRIPTION("Ethernet driver for Airoha SoC"); +--- a/drivers/net/ethernet/mediatek/airoha_eth.c ++++ /dev/null +@@ -1,3378 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0-only +-/* +- * Copyright (c) 2024 AIROHA Inc +- * Author: Lorenzo Bianconi +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define AIROHA_MAX_NUM_GDM_PORTS 1 +-#define AIROHA_MAX_NUM_QDMA 2 +-#define AIROHA_MAX_NUM_RSTS 3 +-#define AIROHA_MAX_NUM_XSI_RSTS 5 +-#define AIROHA_MAX_MTU 2000 +-#define AIROHA_MAX_PACKET_SIZE 2048 +-#define AIROHA_NUM_QOS_CHANNELS 4 +-#define AIROHA_NUM_QOS_QUEUES 8 +-#define AIROHA_NUM_TX_RING 32 +-#define AIROHA_NUM_RX_RING 32 +-#define AIROHA_NUM_NETDEV_TX_RINGS (AIROHA_NUM_TX_RING + \ +- AIROHA_NUM_QOS_CHANNELS) +-#define AIROHA_FE_MC_MAX_VLAN_TABLE 64 +-#define AIROHA_FE_MC_MAX_VLAN_PORT 16 +-#define AIROHA_NUM_TX_IRQ 2 +-#define HW_DSCP_NUM 2048 +-#define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) +-#define TX_DSCP_NUM 1024 +-#define RX_DSCP_NUM(_n) \ +- ((_n) == 2 ? 128 : \ +- (_n) == 11 ? 128 : \ +- (_n) == 15 ? 128 : \ +- (_n) == 0 ? 1024 : 16) +- +-#define PSE_RSV_PAGES 128 +-#define PSE_QUEUE_RSV_PAGES 64 +- +-#define QDMA_METER_IDX(_n) ((_n) & 0xff) +-#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) +- +-/* FE */ +-#define PSE_BASE 0x0100 +-#define CSR_IFC_BASE 0x0200 +-#define CDM1_BASE 0x0400 +-#define GDM1_BASE 0x0500 +-#define PPE1_BASE 0x0c00 +- +-#define CDM2_BASE 0x1400 +-#define GDM2_BASE 0x1500 +- +-#define GDM3_BASE 0x1100 +-#define GDM4_BASE 0x2500 +- +-#define GDM_BASE(_n) \ +- ((_n) == 4 ? GDM4_BASE : \ +- (_n) == 3 ? GDM3_BASE : \ +- (_n) == 2 ? GDM2_BASE : GDM1_BASE) +- +-#define REG_FE_DMA_GLO_CFG 0x0000 +-#define FE_DMA_GLO_L2_SPACE_MASK GENMASK(7, 4) +-#define FE_DMA_GLO_PG_SZ_MASK BIT(3) +- +-#define REG_FE_RST_GLO_CFG 0x0004 +-#define FE_RST_GDM4_MBI_ARB_MASK BIT(3) +-#define FE_RST_GDM3_MBI_ARB_MASK BIT(2) +-#define FE_RST_CORE_MASK BIT(0) +- +-#define REG_FE_WAN_MAC_H 0x0030 +-#define REG_FE_LAN_MAC_H 0x0040 +- +-#define REG_FE_MAC_LMIN(_n) ((_n) + 0x04) +-#define REG_FE_MAC_LMAX(_n) ((_n) + 0x08) +- +-#define REG_FE_CDM1_OQ_MAP0 0x0050 +-#define REG_FE_CDM1_OQ_MAP1 0x0054 +-#define REG_FE_CDM1_OQ_MAP2 0x0058 +-#define REG_FE_CDM1_OQ_MAP3 0x005c +- +-#define REG_FE_PCE_CFG 0x0070 +-#define PCE_DPI_EN_MASK BIT(2) +-#define PCE_KA_EN_MASK BIT(1) +-#define PCE_MC_EN_MASK BIT(0) +- +-#define REG_FE_PSE_QUEUE_CFG_WR 0x0080 +-#define PSE_CFG_PORT_ID_MASK GENMASK(27, 24) +-#define PSE_CFG_QUEUE_ID_MASK GENMASK(20, 16) +-#define PSE_CFG_WR_EN_MASK BIT(8) +-#define PSE_CFG_OQRSV_SEL_MASK BIT(0) +- +-#define REG_FE_PSE_QUEUE_CFG_VAL 0x0084 +-#define PSE_CFG_OQ_RSV_MASK GENMASK(13, 0) +- +-#define PSE_FQ_CFG 0x008c +-#define PSE_FQ_LIMIT_MASK GENMASK(14, 0) +- +-#define REG_FE_PSE_BUF_SET 0x0090 +-#define PSE_SHARE_USED_LTHD_MASK GENMASK(31, 16) +-#define PSE_ALLRSV_MASK GENMASK(14, 0) +- +-#define REG_PSE_SHARE_USED_THD 0x0094 +-#define PSE_SHARE_USED_MTHD_MASK GENMASK(31, 16) +-#define PSE_SHARE_USED_HTHD_MASK GENMASK(15, 0) +- +-#define REG_GDM_MISC_CFG 0x0148 +-#define GDM2_RDM_ACK_WAIT_PREF_MASK BIT(9) +-#define GDM2_CHN_VLD_MODE_MASK BIT(5) +- +-#define REG_FE_CSR_IFC_CFG CSR_IFC_BASE +-#define FE_IFC_EN_MASK BIT(0) +- +-#define REG_FE_VIP_PORT_EN 0x01f0 +-#define REG_FE_IFC_PORT_EN 0x01f4 +- +-#define REG_PSE_IQ_REV1 (PSE_BASE + 0x08) +-#define PSE_IQ_RES1_P2_MASK GENMASK(23, 16) +- +-#define REG_PSE_IQ_REV2 (PSE_BASE + 0x0c) +-#define PSE_IQ_RES2_P5_MASK GENMASK(15, 8) +-#define PSE_IQ_RES2_P4_MASK GENMASK(7, 0) +- +-#define REG_FE_VIP_EN(_n) (0x0300 + ((_n) << 3)) +-#define PATN_FCPU_EN_MASK BIT(7) +-#define PATN_SWP_EN_MASK BIT(6) +-#define PATN_DP_EN_MASK BIT(5) +-#define PATN_SP_EN_MASK BIT(4) +-#define PATN_TYPE_MASK GENMASK(3, 1) +-#define PATN_EN_MASK BIT(0) +- +-#define REG_FE_VIP_PATN(_n) (0x0304 + ((_n) << 3)) +-#define PATN_DP_MASK GENMASK(31, 16) +-#define PATN_SP_MASK GENMASK(15, 0) +- +-#define REG_CDM1_VLAN_CTRL CDM1_BASE +-#define CDM1_VLAN_MASK GENMASK(31, 16) +- +-#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08) +-#define CDM1_VIP_QSEL_MASK GENMASK(24, 20) +- +-#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2)) +-#define CDM1_CRSN_QSEL_REASON_MASK(_n) \ +- GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) +- +-#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08) +-#define CDM2_OAM_QSEL_MASK GENMASK(31, 27) +-#define CDM2_VIP_QSEL_MASK GENMASK(24, 20) +- +-#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2)) +-#define CDM2_CRSN_QSEL_REASON_MASK(_n) \ +- GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) +- +-#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) +-#define GDM_DROP_CRC_ERR BIT(23) +-#define GDM_IP4_CKSUM BIT(22) +-#define GDM_TCP_CKSUM BIT(21) +-#define GDM_UDP_CKSUM BIT(20) +-#define GDM_UCFQ_MASK GENMASK(15, 12) +-#define GDM_BCFQ_MASK GENMASK(11, 8) +-#define GDM_MCFQ_MASK GENMASK(7, 4) +-#define GDM_OCFQ_MASK GENMASK(3, 0) +- +-#define REG_GDM_INGRESS_CFG(_n) (GDM_BASE(_n) + 0x10) +-#define GDM_INGRESS_FC_EN_MASK BIT(1) +-#define GDM_STAG_EN_MASK BIT(0) +- +-#define REG_GDM_LEN_CFG(_n) (GDM_BASE(_n) + 0x14) +-#define GDM_SHORT_LEN_MASK GENMASK(13, 0) +-#define GDM_LONG_LEN_MASK GENMASK(29, 16) +- +-#define REG_FE_CPORT_CFG (GDM1_BASE + 0x40) +-#define FE_CPORT_PAD BIT(26) +-#define FE_CPORT_PORT_XFC_MASK BIT(25) +-#define FE_CPORT_QUEUE_XFC_MASK BIT(24) +- +-#define REG_FE_GDM_MIB_CLEAR(_n) (GDM_BASE(_n) + 0xf0) +-#define FE_GDM_MIB_RX_CLEAR_MASK BIT(1) +-#define FE_GDM_MIB_TX_CLEAR_MASK BIT(0) +- +-#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4) +-#define FE_STRICT_RFC2819_MODE_MASK BIT(31) +-#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17) +-#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16) +-#define FE_TX_MIB_ID_MASK GENMASK(15, 8) +-#define FE_RX_MIB_ID_MASK GENMASK(7, 0) +- +-#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x104) +-#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x10c) +-#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x110) +-#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x114) +-#define REG_FE_GDM_TX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x118) +-#define REG_FE_GDM_TX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x11c) +-#define REG_FE_GDM_TX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x120) +-#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x124) +-#define REG_FE_GDM_TX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x128) +-#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x12c) +-#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x130) +-#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x134) +-#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x138) +-#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x13c) +-#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x140) +- +-#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x148) +-#define REG_FE_GDM_RX_FC_DROP_CNT(_n) (GDM_BASE(_n) + 0x14c) +-#define REG_FE_GDM_RX_RC_DROP_CNT(_n) (GDM_BASE(_n) + 0x150) +-#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n) (GDM_BASE(_n) + 0x154) +-#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n) (GDM_BASE(_n) + 0x158) +-#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x15c) +-#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x160) +-#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x164) +-#define REG_FE_GDM_RX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x168) +-#define REG_FE_GDM_RX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x16c) +-#define REG_FE_GDM_RX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x170) +-#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n) (GDM_BASE(_n) + 0x174) +-#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n) (GDM_BASE(_n) + 0x178) +-#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n) (GDM_BASE(_n) + 0x17c) +-#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x180) +-#define REG_FE_GDM_RX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x184) +-#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x188) +-#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x18c) +-#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x190) +-#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x194) +-#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) +-#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) +- +-#define REG_PPE1_TB_HASH_CFG (PPE1_BASE + 0x250) +-#define PPE1_SRAM_TABLE_EN_MASK BIT(0) +-#define PPE1_SRAM_HASH1_EN_MASK BIT(8) +-#define PPE1_DRAM_TABLE_EN_MASK BIT(16) +-#define PPE1_DRAM_HASH1_EN_MASK BIT(24) +- +-#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) +-#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) +-#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) +-#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) +- +-#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) +-#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) +-#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) +-#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) +-#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) +-#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) +-#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) +-#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) +-#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) +-#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) +-#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) +-#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) +-#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) +-#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) +-#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) +-#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) +- +-#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) +-#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) +-#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) +- +-#define REG_GDM3_FWD_CFG GDM3_BASE +-#define GDM3_PAD_EN_MASK BIT(28) +- +-#define REG_GDM4_FWD_CFG GDM4_BASE +-#define GDM4_PAD_EN_MASK BIT(28) +-#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8) +- +-#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x23c) +-#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16) +-#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12) +-#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8) +- +-#define REG_IP_FRAG_FP 0x2010 +-#define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21) +-#define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16) +-#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) +-#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) +- +-#define REG_MC_VLAN_EN 0x2100 +-#define MC_VLAN_EN_MASK BIT(0) +- +-#define REG_MC_VLAN_CFG 0x2104 +-#define MC_VLAN_CFG_CMD_DONE_MASK BIT(31) +-#define MC_VLAN_CFG_TABLE_ID_MASK GENMASK(21, 16) +-#define MC_VLAN_CFG_PORT_ID_MASK GENMASK(11, 8) +-#define MC_VLAN_CFG_TABLE_SEL_MASK BIT(4) +-#define MC_VLAN_CFG_RW_MASK BIT(0) +- +-#define REG_MC_VLAN_DATA 0x2108 +- +-#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 +- +-/* QDMA */ +-#define REG_QDMA_GLOBAL_CFG 0x0004 +-#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) +-#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) +-#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) +-#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) +-#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) +-#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) +-#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) +-#define GLOBAL_CFG_RESET_MASK BIT(23) +-#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) +-#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) +-#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) +-#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) +-#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) +-#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) +-#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) +-#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) +-#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) +-#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) +-#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) +-#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) +-#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) +-#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) +-#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) +- +-#define REG_FWD_DSCP_BASE 0x0010 +-#define REG_FWD_BUF_BASE 0x0014 +- +-#define REG_HW_FWD_DSCP_CFG 0x0018 +-#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) +-#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) +-#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) +- +-#define REG_INT_STATUS(_n) \ +- (((_n) == 4) ? 0x0730 : \ +- ((_n) == 3) ? 0x0724 : \ +- ((_n) == 2) ? 0x0720 : \ +- ((_n) == 1) ? 0x0024 : 0x0020) +- +-#define REG_INT_ENABLE(_n) \ +- (((_n) == 4) ? 0x0750 : \ +- ((_n) == 3) ? 0x0744 : \ +- ((_n) == 2) ? 0x0740 : \ +- ((_n) == 1) ? 0x002c : 0x0028) +- +-/* QDMA_CSR_INT_ENABLE1 */ +-#define RX15_COHERENT_INT_MASK BIT(31) +-#define RX14_COHERENT_INT_MASK BIT(30) +-#define RX13_COHERENT_INT_MASK BIT(29) +-#define RX12_COHERENT_INT_MASK BIT(28) +-#define RX11_COHERENT_INT_MASK BIT(27) +-#define RX10_COHERENT_INT_MASK BIT(26) +-#define RX9_COHERENT_INT_MASK BIT(25) +-#define RX8_COHERENT_INT_MASK BIT(24) +-#define RX7_COHERENT_INT_MASK BIT(23) +-#define RX6_COHERENT_INT_MASK BIT(22) +-#define RX5_COHERENT_INT_MASK BIT(21) +-#define RX4_COHERENT_INT_MASK BIT(20) +-#define RX3_COHERENT_INT_MASK BIT(19) +-#define RX2_COHERENT_INT_MASK BIT(18) +-#define RX1_COHERENT_INT_MASK BIT(17) +-#define RX0_COHERENT_INT_MASK BIT(16) +-#define TX7_COHERENT_INT_MASK BIT(15) +-#define TX6_COHERENT_INT_MASK BIT(14) +-#define TX5_COHERENT_INT_MASK BIT(13) +-#define TX4_COHERENT_INT_MASK BIT(12) +-#define TX3_COHERENT_INT_MASK BIT(11) +-#define TX2_COHERENT_INT_MASK BIT(10) +-#define TX1_COHERENT_INT_MASK BIT(9) +-#define TX0_COHERENT_INT_MASK BIT(8) +-#define CNT_OVER_FLOW_INT_MASK BIT(7) +-#define IRQ1_FULL_INT_MASK BIT(5) +-#define IRQ1_INT_MASK BIT(4) +-#define HWFWD_DSCP_LOW_INT_MASK BIT(3) +-#define HWFWD_DSCP_EMPTY_INT_MASK BIT(2) +-#define IRQ0_FULL_INT_MASK BIT(1) +-#define IRQ0_INT_MASK BIT(0) +- +-#define TX_DONE_INT_MASK(_n) \ +- ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ +- : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) +- +-#define INT_TX_MASK \ +- (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ +- IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) +- +-#define INT_IDX0_MASK \ +- (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ +- TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ +- TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ +- TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ +- RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ +- RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ +- RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ +- RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ +- RX15_COHERENT_INT_MASK | INT_TX_MASK) +- +-/* QDMA_CSR_INT_ENABLE2 */ +-#define RX15_NO_CPU_DSCP_INT_MASK BIT(31) +-#define RX14_NO_CPU_DSCP_INT_MASK BIT(30) +-#define RX13_NO_CPU_DSCP_INT_MASK BIT(29) +-#define RX12_NO_CPU_DSCP_INT_MASK BIT(28) +-#define RX11_NO_CPU_DSCP_INT_MASK BIT(27) +-#define RX10_NO_CPU_DSCP_INT_MASK BIT(26) +-#define RX9_NO_CPU_DSCP_INT_MASK BIT(25) +-#define RX8_NO_CPU_DSCP_INT_MASK BIT(24) +-#define RX7_NO_CPU_DSCP_INT_MASK BIT(23) +-#define RX6_NO_CPU_DSCP_INT_MASK BIT(22) +-#define RX5_NO_CPU_DSCP_INT_MASK BIT(21) +-#define RX4_NO_CPU_DSCP_INT_MASK BIT(20) +-#define RX3_NO_CPU_DSCP_INT_MASK BIT(19) +-#define RX2_NO_CPU_DSCP_INT_MASK BIT(18) +-#define RX1_NO_CPU_DSCP_INT_MASK BIT(17) +-#define RX0_NO_CPU_DSCP_INT_MASK BIT(16) +-#define RX15_DONE_INT_MASK BIT(15) +-#define RX14_DONE_INT_MASK BIT(14) +-#define RX13_DONE_INT_MASK BIT(13) +-#define RX12_DONE_INT_MASK BIT(12) +-#define RX11_DONE_INT_MASK BIT(11) +-#define RX10_DONE_INT_MASK BIT(10) +-#define RX9_DONE_INT_MASK BIT(9) +-#define RX8_DONE_INT_MASK BIT(8) +-#define RX7_DONE_INT_MASK BIT(7) +-#define RX6_DONE_INT_MASK BIT(6) +-#define RX5_DONE_INT_MASK BIT(5) +-#define RX4_DONE_INT_MASK BIT(4) +-#define RX3_DONE_INT_MASK BIT(3) +-#define RX2_DONE_INT_MASK BIT(2) +-#define RX1_DONE_INT_MASK BIT(1) +-#define RX0_DONE_INT_MASK BIT(0) +- +-#define RX_DONE_INT_MASK \ +- (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ +- RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ +- RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ +- RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ +- RX15_DONE_INT_MASK) +-#define INT_IDX1_MASK \ +- (RX_DONE_INT_MASK | \ +- RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ +- RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ +- RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ +- RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ +- RX15_NO_CPU_DSCP_INT_MASK) +- +-/* QDMA_CSR_INT_ENABLE5 */ +-#define TX31_COHERENT_INT_MASK BIT(31) +-#define TX30_COHERENT_INT_MASK BIT(30) +-#define TX29_COHERENT_INT_MASK BIT(29) +-#define TX28_COHERENT_INT_MASK BIT(28) +-#define TX27_COHERENT_INT_MASK BIT(27) +-#define TX26_COHERENT_INT_MASK BIT(26) +-#define TX25_COHERENT_INT_MASK BIT(25) +-#define TX24_COHERENT_INT_MASK BIT(24) +-#define TX23_COHERENT_INT_MASK BIT(23) +-#define TX22_COHERENT_INT_MASK BIT(22) +-#define TX21_COHERENT_INT_MASK BIT(21) +-#define TX20_COHERENT_INT_MASK BIT(20) +-#define TX19_COHERENT_INT_MASK BIT(19) +-#define TX18_COHERENT_INT_MASK BIT(18) +-#define TX17_COHERENT_INT_MASK BIT(17) +-#define TX16_COHERENT_INT_MASK BIT(16) +-#define TX15_COHERENT_INT_MASK BIT(15) +-#define TX14_COHERENT_INT_MASK BIT(14) +-#define TX13_COHERENT_INT_MASK BIT(13) +-#define TX12_COHERENT_INT_MASK BIT(12) +-#define TX11_COHERENT_INT_MASK BIT(11) +-#define TX10_COHERENT_INT_MASK BIT(10) +-#define TX9_COHERENT_INT_MASK BIT(9) +-#define TX8_COHERENT_INT_MASK BIT(8) +- +-#define INT_IDX4_MASK \ +- (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ +- TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ +- TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ +- TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ +- TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ +- TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ +- TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ +- TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ +- TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ +- TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ +- TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ +- TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) +- +-#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) +- +-#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) +-#define TX_IRQ_THR_MASK GENMASK(27, 16) +-#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) +- +-#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) +-#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) +- +-#define REG_IRQ_STATUS(_n) ((_n) ? 0x0068 : 0x005c) +-#define IRQ_ENTRY_LEN_MASK GENMASK(27, 16) +-#define IRQ_HEAD_IDX_MASK GENMASK(11, 0) +- +-#define REG_TX_RING_BASE(_n) \ +- (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) +- +-#define REG_TX_RING_BLOCKING(_n) \ +- (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5)) +- +-#define TX_RING_IRQ_BLOCKING_MAP_MASK BIT(6) +-#define TX_RING_IRQ_BLOCKING_CFG_MASK BIT(4) +-#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK BIT(2) +-#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK BIT(1) +-#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK BIT(0) +- +-#define REG_TX_CPU_IDX(_n) \ +- (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) +- +-#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) +- +-#define REG_TX_DMA_IDX(_n) \ +- (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) +- +-#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) +- +-#define IRQ_RING_IDX_MASK GENMASK(20, 16) +-#define IRQ_DESC_IDX_MASK GENMASK(15, 0) +- +-#define REG_RX_RING_BASE(_n) \ +- (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) +- +-#define REG_RX_RING_SIZE(_n) \ +- (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) +- +-#define RX_RING_THR_MASK GENMASK(31, 16) +-#define RX_RING_SIZE_MASK GENMASK(15, 0) +- +-#define REG_RX_CPU_IDX(_n) \ +- (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) +- +-#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) +- +-#define REG_RX_DMA_IDX(_n) \ +- (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) +- +-#define REG_RX_DELAY_INT_IDX(_n) \ +- (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) +- +-#define RX_DELAY_INT_MASK GENMASK(15, 0) +- +-#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) +- +-#define REG_INGRESS_TRTCM_CFG 0x0070 +-#define INGRESS_TRTCM_EN_MASK BIT(31) +-#define INGRESS_TRTCM_MODE_MASK BIT(30) +-#define INGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define INGRESS_FAST_TICK_MASK GENMASK(15, 0) +- +-#define REG_QUEUE_CLOSE_CFG(_n) (0x00a0 + ((_n) & 0xfc)) +-#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) BIT((_m) + (((_n) & 0x3) << 3)) +- +-#define REG_TXQ_DIS_CFG_BASE(_n) ((_n) ? 0x20a0 : 0x00a0) +-#define REG_TXQ_DIS_CFG(_n, _m) (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2) +- +-#define REG_CNTR_CFG(_n) (0x0400 + ((_n) << 3)) +-#define CNTR_EN_MASK BIT(31) +-#define CNTR_ALL_CHAN_EN_MASK BIT(30) +-#define CNTR_ALL_QUEUE_EN_MASK BIT(29) +-#define CNTR_ALL_DSCP_RING_EN_MASK BIT(28) +-#define CNTR_SRC_MASK GENMASK(27, 24) +-#define CNTR_DSCP_RING_MASK GENMASK(20, 16) +-#define CNTR_CHAN_MASK GENMASK(7, 3) +-#define CNTR_QUEUE_MASK GENMASK(2, 0) +- +-#define REG_CNTR_VAL(_n) (0x0404 + ((_n) << 3)) +- +-#define REG_LMGR_INIT_CFG 0x1000 +-#define LMGR_INIT_START BIT(31) +-#define LMGR_SRAM_MODE_MASK BIT(30) +-#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) +-#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) +- +-#define REG_FWD_DSCP_LOW_THR 0x1004 +-#define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) +- +-#define REG_EGRESS_RATE_METER_CFG 0x100c +-#define EGRESS_RATE_METER_EN_MASK BIT(31) +-#define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) +-#define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) +-#define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) +- +-#define REG_EGRESS_TRTCM_CFG 0x1010 +-#define EGRESS_TRTCM_EN_MASK BIT(31) +-#define EGRESS_TRTCM_MODE_MASK BIT(30) +-#define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define EGRESS_FAST_TICK_MASK GENMASK(15, 0) +- +-#define TRTCM_PARAM_RW_MASK BIT(31) +-#define TRTCM_PARAM_RW_DONE_MASK BIT(30) +-#define TRTCM_PARAM_TYPE_MASK GENMASK(29, 28) +-#define TRTCM_METER_GROUP_MASK GENMASK(27, 26) +-#define TRTCM_PARAM_INDEX_MASK GENMASK(23, 17) +-#define TRTCM_PARAM_RATE_TYPE_MASK BIT(16) +- +-#define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4) +-#define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) +-#define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) +- +-#define REG_TXWRR_MODE_CFG 0x1020 +-#define TWRR_WEIGHT_SCALE_MASK BIT(31) +-#define TWRR_WEIGHT_BASE_MASK BIT(3) +- +-#define REG_TXWRR_WEIGHT_CFG 0x1024 +-#define TWRR_RW_CMD_MASK BIT(31) +-#define TWRR_RW_CMD_DONE BIT(30) +-#define TWRR_CHAN_IDX_MASK GENMASK(23, 19) +-#define TWRR_QUEUE_IDX_MASK GENMASK(18, 16) +-#define TWRR_VALUE_MASK GENMASK(15, 0) +- +-#define REG_PSE_BUF_USAGE_CFG 0x1028 +-#define PSE_BUF_ESTIMATE_EN_MASK BIT(29) +- +-#define REG_CHAN_QOS_MODE(_n) (0x1040 + ((_n) << 2)) +-#define CHAN_QOS_MODE_MASK(_n) GENMASK(2 + ((_n) << 2), (_n) << 2) +- +-#define REG_GLB_TRTCM_CFG 0x1080 +-#define GLB_TRTCM_EN_MASK BIT(31) +-#define GLB_TRTCM_MODE_MASK BIT(30) +-#define GLB_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define GLB_FAST_TICK_MASK GENMASK(15, 0) +- +-#define REG_TXQ_CNGST_CFG 0x10a0 +-#define TXQ_CNGST_DROP_EN BIT(31) +-#define TXQ_CNGST_DEI_DROP_EN BIT(30) +- +-#define REG_SLA_TRTCM_CFG 0x1150 +-#define SLA_TRTCM_EN_MASK BIT(31) +-#define SLA_TRTCM_MODE_MASK BIT(30) +-#define SLA_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define SLA_FAST_TICK_MASK GENMASK(15, 0) +- +-/* CTRL */ +-#define QDMA_DESC_DONE_MASK BIT(31) +-#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ +-#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ +-#define QDMA_DESC_DEI_MASK BIT(25) +-#define QDMA_DESC_NO_DROP_MASK BIT(24) +-#define QDMA_DESC_LEN_MASK GENMASK(15, 0) +-/* DATA */ +-#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) +-/* TX MSG0 */ +-#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) +-#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) +-#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) +-#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) +-#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) +-#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) +-#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) +-#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) +-#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) +-#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) +-/* TX MSG1 */ +-#define QDMA_ETH_TXMSG_NO_DROP BIT(31) +-#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ +-#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) +-#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) +-#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) +-#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) +-#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) +-#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ +-#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ +- +-/* RX MSG1 */ +-#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) +-#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) +-#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) +-#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) +-#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) +-#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) +-#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) +-#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) +-#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) +- +-struct airoha_qdma_desc { +- __le32 rsv; +- __le32 ctrl; +- __le32 addr; +- __le32 data; +- __le32 msg0; +- __le32 msg1; +- __le32 msg2; +- __le32 msg3; +-}; +- +-/* CTRL0 */ +-#define QDMA_FWD_DESC_CTX_MASK BIT(31) +-#define QDMA_FWD_DESC_RING_MASK GENMASK(30, 28) +-#define QDMA_FWD_DESC_IDX_MASK GENMASK(27, 16) +-#define QDMA_FWD_DESC_LEN_MASK GENMASK(15, 0) +-/* CTRL1 */ +-#define QDMA_FWD_DESC_FIRST_IDX_MASK GENMASK(15, 0) +-/* CTRL2 */ +-#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK GENMASK(2, 0) +- +-struct airoha_qdma_fwd_desc { +- __le32 addr; +- __le32 ctrl0; +- __le32 ctrl1; +- __le32 ctrl2; +- __le32 msg0; +- __le32 msg1; +- __le32 rsv0; +- __le32 rsv1; +-}; +- +-enum { +- QDMA_INT_REG_IDX0, +- QDMA_INT_REG_IDX1, +- QDMA_INT_REG_IDX2, +- QDMA_INT_REG_IDX3, +- QDMA_INT_REG_IDX4, +- QDMA_INT_REG_MAX +-}; +- +-enum { +- XSI_PCIE0_PORT, +- XSI_PCIE1_PORT, +- XSI_USB_PORT, +- XSI_AE_PORT, +- XSI_ETH_PORT, +-}; +- +-enum { +- XSI_PCIE0_VIP_PORT_MASK = BIT(22), +- XSI_PCIE1_VIP_PORT_MASK = BIT(23), +- XSI_USB_VIP_PORT_MASK = BIT(25), +- XSI_ETH_VIP_PORT_MASK = BIT(24), +-}; +- +-enum { +- DEV_STATE_INITIALIZED, +-}; +- +-enum { +- CDM_CRSN_QSEL_Q1 = 1, +- CDM_CRSN_QSEL_Q5 = 5, +- CDM_CRSN_QSEL_Q6 = 6, +- CDM_CRSN_QSEL_Q15 = 15, +-}; +- +-enum { +- CRSN_08 = 0x8, +- CRSN_21 = 0x15, /* KA */ +- CRSN_22 = 0x16, /* hit bind and force route to CPU */ +- CRSN_24 = 0x18, +- CRSN_25 = 0x19, +-}; +- +-enum { +- FE_PSE_PORT_CDM1, +- FE_PSE_PORT_GDM1, +- FE_PSE_PORT_GDM2, +- FE_PSE_PORT_GDM3, +- FE_PSE_PORT_PPE1, +- FE_PSE_PORT_CDM2, +- FE_PSE_PORT_CDM3, +- FE_PSE_PORT_CDM4, +- FE_PSE_PORT_PPE2, +- FE_PSE_PORT_GDM4, +- FE_PSE_PORT_CDM5, +- FE_PSE_PORT_DROP = 0xf, +-}; +- +-enum tx_sched_mode { +- TC_SCH_WRR8, +- TC_SCH_SP, +- TC_SCH_WRR7, +- TC_SCH_WRR6, +- TC_SCH_WRR5, +- TC_SCH_WRR4, +- TC_SCH_WRR3, +- TC_SCH_WRR2, +-}; +- +-enum trtcm_param_type { +- TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ +- TRTCM_TOKEN_RATE_MODE, +- TRTCM_BUCKETSIZE_SHIFT_MODE, +- TRTCM_BUCKET_COUNTER_MODE, +-}; +- +-enum trtcm_mode_type { +- TRTCM_COMMIT_MODE, +- TRTCM_PEAK_MODE, +-}; +- +-enum trtcm_param { +- TRTCM_TICK_SEL = BIT(0), +- TRTCM_PKT_MODE = BIT(1), +- TRTCM_METER_MODE = BIT(2), +-}; +- +-#define MIN_TOKEN_SIZE 4096 +-#define MAX_TOKEN_SIZE_OFFSET 17 +-#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6) +-#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) +- +-struct airoha_queue_entry { +- union { +- void *buf; +- struct sk_buff *skb; +- }; +- dma_addr_t dma_addr; +- u16 dma_len; +-}; +- +-struct airoha_queue { +- struct airoha_qdma *qdma; +- +- /* protect concurrent queue accesses */ +- spinlock_t lock; +- struct airoha_queue_entry *entry; +- struct airoha_qdma_desc *desc; +- u16 head; +- u16 tail; +- +- int queued; +- int ndesc; +- int free_thr; +- int buf_size; +- +- struct napi_struct napi; +- struct page_pool *page_pool; +-}; +- +-struct airoha_tx_irq_queue { +- struct airoha_qdma *qdma; +- +- struct napi_struct napi; +- +- int size; +- u32 *q; +-}; +- +-struct airoha_hw_stats { +- /* protect concurrent hw_stats accesses */ +- spinlock_t lock; +- struct u64_stats_sync syncp; +- +- /* get_stats64 */ +- u64 rx_ok_pkts; +- u64 tx_ok_pkts; +- u64 rx_ok_bytes; +- u64 tx_ok_bytes; +- u64 rx_multicast; +- u64 rx_errors; +- u64 rx_drops; +- u64 tx_drops; +- u64 rx_crc_error; +- u64 rx_over_errors; +- /* ethtool stats */ +- u64 tx_broadcast; +- u64 tx_multicast; +- u64 tx_len[7]; +- u64 rx_broadcast; +- u64 rx_fragment; +- u64 rx_jabber; +- u64 rx_len[7]; +-}; +- +-struct airoha_qdma { +- struct airoha_eth *eth; +- void __iomem *regs; +- +- /* protect concurrent irqmask accesses */ +- spinlock_t irq_lock; +- u32 irqmask[QDMA_INT_REG_MAX]; +- int irq; +- +- struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; +- +- struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; +- struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; +- +- /* descriptor and packet buffers for qdma hw forward */ +- struct { +- void *desc; +- void *q; +- } hfwd; +-}; +- +-struct airoha_gdm_port { +- struct airoha_qdma *qdma; +- struct net_device *dev; +- int id; +- +- struct airoha_hw_stats stats; +- +- DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); +- +- /* qos stats counters */ +- u64 cpu_tx_packets; +- u64 fwd_tx_packets; +-}; +- +-struct airoha_eth { +- struct device *dev; +- +- unsigned long state; +- void __iomem *fe_regs; +- +- struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; +- struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; +- +- struct net_device *napi_dev; +- +- struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; +- struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; +-}; +- +-static u32 airoha_rr(void __iomem *base, u32 offset) +-{ +- return readl(base + offset); +-} +- +-static void airoha_wr(void __iomem *base, u32 offset, u32 val) +-{ +- writel(val, base + offset); +-} +- +-static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) +-{ +- val |= (airoha_rr(base, offset) & ~mask); +- airoha_wr(base, offset, val); +- +- return val; +-} +- +-#define airoha_fe_rr(eth, offset) \ +- airoha_rr((eth)->fe_regs, (offset)) +-#define airoha_fe_wr(eth, offset, val) \ +- airoha_wr((eth)->fe_regs, (offset), (val)) +-#define airoha_fe_rmw(eth, offset, mask, val) \ +- airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) +-#define airoha_fe_set(eth, offset, val) \ +- airoha_rmw((eth)->fe_regs, (offset), 0, (val)) +-#define airoha_fe_clear(eth, offset, val) \ +- airoha_rmw((eth)->fe_regs, (offset), (val), 0) +- +-#define airoha_qdma_rr(qdma, offset) \ +- airoha_rr((qdma)->regs, (offset)) +-#define airoha_qdma_wr(qdma, offset, val) \ +- airoha_wr((qdma)->regs, (offset), (val)) +-#define airoha_qdma_rmw(qdma, offset, mask, val) \ +- airoha_rmw((qdma)->regs, (offset), (mask), (val)) +-#define airoha_qdma_set(qdma, offset, val) \ +- airoha_rmw((qdma)->regs, (offset), 0, (val)) +-#define airoha_qdma_clear(qdma, offset, val) \ +- airoha_rmw((qdma)->regs, (offset), (val), 0) +- +-static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index, +- u32 clear, u32 set) +-{ +- unsigned long flags; +- +- if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask))) +- return; +- +- spin_lock_irqsave(&qdma->irq_lock, flags); +- +- qdma->irqmask[index] &= ~clear; +- qdma->irqmask[index] |= set; +- airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]); +- /* Read irq_enable register in order to guarantee the update above +- * completes in the spinlock critical section. +- */ +- airoha_qdma_rr(qdma, REG_INT_ENABLE(index)); +- +- spin_unlock_irqrestore(&qdma->irq_lock, flags); +-} +- +-static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index, +- u32 mask) +-{ +- airoha_qdma_set_irqmask(qdma, index, 0, mask); +-} +- +-static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index, +- u32 mask) +-{ +- airoha_qdma_set_irqmask(qdma, index, mask, 0); +-} +- +-static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) +-{ +- /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. +- * GDM{2,3,4} can be used as wan port connected to an external +- * phy module. +- */ +- return port->id == 1; +-} +- +-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) +-{ +- struct airoha_eth *eth = port->qdma->eth; +- u32 val, reg; +- +- reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H +- : REG_FE_WAN_MAC_H; +- val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; +- airoha_fe_wr(eth, reg, val); +- +- val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; +- airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); +- airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); +-} +- +-static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, +- u32 val) +-{ +- airoha_fe_rmw(eth, addr, GDM_OCFQ_MASK, +- FIELD_PREP(GDM_OCFQ_MASK, val)); +- airoha_fe_rmw(eth, addr, GDM_MCFQ_MASK, +- FIELD_PREP(GDM_MCFQ_MASK, val)); +- airoha_fe_rmw(eth, addr, GDM_BCFQ_MASK, +- FIELD_PREP(GDM_BCFQ_MASK, val)); +- airoha_fe_rmw(eth, addr, GDM_UCFQ_MASK, +- FIELD_PREP(GDM_UCFQ_MASK, val)); +-} +- +-static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable) +-{ +- u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP; +- u32 vip_port, cfg_addr; +- +- switch (port) { +- case XSI_PCIE0_PORT: +- vip_port = XSI_PCIE0_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(3); +- break; +- case XSI_PCIE1_PORT: +- vip_port = XSI_PCIE1_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(3); +- break; +- case XSI_USB_PORT: +- vip_port = XSI_USB_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(4); +- break; +- case XSI_ETH_PORT: +- vip_port = XSI_ETH_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(4); +- break; +- default: +- return -EINVAL; +- } +- +- if (enable) { +- airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); +- airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); +- } else { +- airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, vip_port); +- airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port); +- } +- +- airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val); +- +- return 0; +-} +- +-static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable) +-{ +- const int port_list[] = { +- XSI_PCIE0_PORT, +- XSI_PCIE1_PORT, +- XSI_USB_PORT, +- XSI_ETH_PORT +- }; +- int i, err; +- +- for (i = 0; i < ARRAY_SIZE(port_list); i++) { +- err = airoha_set_gdm_port(eth, port_list[i], enable); +- if (err) +- goto error; +- } +- +- return 0; +- +-error: +- for (i--; i >= 0; i--) +- airoha_set_gdm_port(eth, port_list[i], false); +- +- return err; +-} +- +-static void airoha_fe_maccr_init(struct airoha_eth *eth) +-{ +- int p; +- +- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { +- airoha_fe_set(eth, REG_GDM_FWD_CFG(p), +- GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | +- GDM_DROP_CRC_ERR); +- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p), +- FE_PSE_PORT_CDM1); +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), +- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +- FIELD_PREP(GDM_LONG_LEN_MASK, 4004)); +- } +- +- airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, +- FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); +- +- airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD); +-} +- +-static void airoha_fe_vip_setup(struct airoha_eth *eth) +-{ +- airoha_fe_wr(eth, REG_FE_VIP_PATN(3), ETH_P_PPP_DISC); +- airoha_fe_wr(eth, REG_FE_VIP_EN(3), PATN_FCPU_EN_MASK | PATN_EN_MASK); +- +- airoha_fe_wr(eth, REG_FE_VIP_PATN(4), PPP_LCP); +- airoha_fe_wr(eth, REG_FE_VIP_EN(4), +- PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | +- PATN_EN_MASK); +- +- airoha_fe_wr(eth, REG_FE_VIP_PATN(6), PPP_IPCP); +- airoha_fe_wr(eth, REG_FE_VIP_EN(6), +- PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | +- PATN_EN_MASK); +- +- airoha_fe_wr(eth, REG_FE_VIP_PATN(7), PPP_CHAP); +- airoha_fe_wr(eth, REG_FE_VIP_EN(7), +- PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | +- PATN_EN_MASK); +- +- /* BOOTP (0x43) */ +- airoha_fe_wr(eth, REG_FE_VIP_PATN(8), 0x43); +- airoha_fe_wr(eth, REG_FE_VIP_EN(8), +- PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | +- FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); +- +- /* BOOTP (0x44) */ +- airoha_fe_wr(eth, REG_FE_VIP_PATN(9), 0x44); +- airoha_fe_wr(eth, REG_FE_VIP_EN(9), +- PATN_FCPU_EN_MASK | PATN_SP_EN_MASK | +- FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); +- +- /* ISAKMP */ +- airoha_fe_wr(eth, REG_FE_VIP_PATN(10), 0x1f401f4); +- airoha_fe_wr(eth, REG_FE_VIP_EN(10), +- PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | +- FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); +- +- airoha_fe_wr(eth, REG_FE_VIP_PATN(11), PPP_IPV6CP); +- airoha_fe_wr(eth, REG_FE_VIP_EN(11), +- PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | +- PATN_EN_MASK); +- +- /* DHCPv6 */ +- airoha_fe_wr(eth, REG_FE_VIP_PATN(12), 0x2220223); +- airoha_fe_wr(eth, REG_FE_VIP_EN(12), +- PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK | +- FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK); +- +- airoha_fe_wr(eth, REG_FE_VIP_PATN(19), PPP_PAP); +- airoha_fe_wr(eth, REG_FE_VIP_EN(19), +- PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) | +- PATN_EN_MASK); +- +- /* ETH->ETH_P_1905 (0x893a) */ +- airoha_fe_wr(eth, REG_FE_VIP_PATN(20), 0x893a); +- airoha_fe_wr(eth, REG_FE_VIP_EN(20), +- PATN_FCPU_EN_MASK | PATN_EN_MASK); +- +- airoha_fe_wr(eth, REG_FE_VIP_PATN(21), ETH_P_LLDP); +- airoha_fe_wr(eth, REG_FE_VIP_EN(21), +- PATN_FCPU_EN_MASK | PATN_EN_MASK); +-} +- +-static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth, +- u32 port, u32 queue) +-{ +- u32 val; +- +- airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, +- PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK, +- FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | +- FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue)); +- val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL); +- +- return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val); +-} +- +-static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, +- u32 port, u32 queue, u32 val) +-{ +- airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_VAL, PSE_CFG_OQ_RSV_MASK, +- FIELD_PREP(PSE_CFG_OQ_RSV_MASK, val)); +- airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, +- PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK | +- PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK, +- FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | +- FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue) | +- PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK); +-} +- +-static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth) +-{ +- u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); +- +- return FIELD_GET(PSE_ALLRSV_MASK, val); +-} +- +-static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, +- u32 port, u32 queue, u32 val) +-{ +- u32 orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue); +- u32 tmp, all_rsv, fq_limit; +- +- airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val); +- +- /* modify all rsv */ +- all_rsv = airoha_fe_get_pse_all_rsv(eth); +- all_rsv += (val - orig_val); +- airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK, +- FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); +- +- /* modify hthd */ +- tmp = airoha_fe_rr(eth, PSE_FQ_CFG); +- fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp); +- tmp = fq_limit - all_rsv - 0x20; +- airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, +- PSE_SHARE_USED_HTHD_MASK, +- FIELD_PREP(PSE_SHARE_USED_HTHD_MASK, tmp)); +- +- tmp = fq_limit - all_rsv - 0x100; +- airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, +- PSE_SHARE_USED_MTHD_MASK, +- FIELD_PREP(PSE_SHARE_USED_MTHD_MASK, tmp)); +- tmp = (3 * tmp) >> 2; +- airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, +- PSE_SHARE_USED_LTHD_MASK, +- FIELD_PREP(PSE_SHARE_USED_LTHD_MASK, tmp)); +- +- return 0; +-} +- +-static void airoha_fe_pse_ports_init(struct airoha_eth *eth) +-{ +- const u32 pse_port_num_queues[] = { +- [FE_PSE_PORT_CDM1] = 6, +- [FE_PSE_PORT_GDM1] = 6, +- [FE_PSE_PORT_GDM2] = 32, +- [FE_PSE_PORT_GDM3] = 6, +- [FE_PSE_PORT_PPE1] = 4, +- [FE_PSE_PORT_CDM2] = 6, +- [FE_PSE_PORT_CDM3] = 8, +- [FE_PSE_PORT_CDM4] = 10, +- [FE_PSE_PORT_PPE2] = 4, +- [FE_PSE_PORT_GDM4] = 2, +- [FE_PSE_PORT_CDM5] = 2, +- }; +- u32 all_rsv; +- int q; +- +- all_rsv = airoha_fe_get_pse_all_rsv(eth); +- /* hw misses PPE2 oq rsv */ +- all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]; +- airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv); +- +- /* CMD1 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q, +- PSE_QUEUE_RSV_PAGES); +- /* GMD1 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q, +- PSE_QUEUE_RSV_PAGES); +- /* GMD2 */ +- for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0); +- /* GMD3 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q, +- PSE_QUEUE_RSV_PAGES); +- /* PPE1 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { +- if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, +- PSE_QUEUE_RSV_PAGES); +- else +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 0); +- } +- /* CDM2 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM2]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM2, q, +- PSE_QUEUE_RSV_PAGES); +- /* CDM3 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM3] - 1; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM3, q, 0); +- /* CDM4 */ +- for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q, +- PSE_QUEUE_RSV_PAGES); +- /* PPE2 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) { +- if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, +- PSE_QUEUE_RSV_PAGES); +- else +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0); +- } +- /* GMD4 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q, +- PSE_QUEUE_RSV_PAGES); +- /* CDM5 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM5]; q++) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM5, q, +- PSE_QUEUE_RSV_PAGES); +-} +- +-static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth) +-{ +- int i; +- +- for (i = 0; i < AIROHA_FE_MC_MAX_VLAN_TABLE; i++) { +- int err, j; +- u32 val; +- +- airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); +- +- val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | +- MC_VLAN_CFG_TABLE_SEL_MASK | MC_VLAN_CFG_RW_MASK; +- airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); +- err = read_poll_timeout(airoha_fe_rr, val, +- val & MC_VLAN_CFG_CMD_DONE_MASK, +- USEC_PER_MSEC, 5 * USEC_PER_MSEC, +- false, eth, REG_MC_VLAN_CFG); +- if (err) +- return err; +- +- for (j = 0; j < AIROHA_FE_MC_MAX_VLAN_PORT; j++) { +- airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0); +- +- val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) | +- FIELD_PREP(MC_VLAN_CFG_PORT_ID_MASK, j) | +- MC_VLAN_CFG_RW_MASK; +- airoha_fe_wr(eth, REG_MC_VLAN_CFG, val); +- err = read_poll_timeout(airoha_fe_rr, val, +- val & MC_VLAN_CFG_CMD_DONE_MASK, +- USEC_PER_MSEC, +- 5 * USEC_PER_MSEC, false, eth, +- REG_MC_VLAN_CFG); +- if (err) +- return err; +- } +- } +- +- return 0; +-} +- +-static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth) +-{ +- /* CDM1_CRSN_QSEL */ +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), +- CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), +- CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), +- CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), +- CDM_CRSN_QSEL_Q6)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), +- CDM_CRSN_QSEL_Q1)); +- /* CDM2_CRSN_QSEL */ +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), +- CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), +- CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), +- CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), +- CDM_CRSN_QSEL_Q6)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), +- CDM_CRSN_QSEL_Q1)); +-} +- +-static int airoha_fe_init(struct airoha_eth *eth) +-{ +- airoha_fe_maccr_init(eth); +- +- /* PSE IQ reserve */ +- airoha_fe_rmw(eth, REG_PSE_IQ_REV1, PSE_IQ_RES1_P2_MASK, +- FIELD_PREP(PSE_IQ_RES1_P2_MASK, 0x10)); +- airoha_fe_rmw(eth, REG_PSE_IQ_REV2, +- PSE_IQ_RES2_P5_MASK | PSE_IQ_RES2_P4_MASK, +- FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) | +- FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34)); +- +- /* enable FE copy engine for MC/KA/DPI */ +- airoha_fe_wr(eth, REG_FE_PCE_CFG, +- PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK); +- /* set vip queue selection to ring 1 */ +- airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK, +- FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4)); +- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK, +- FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4)); +- /* set GDM4 source interface offset to 8 */ +- airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET, +- GDM4_SPORT_OFF2_MASK | +- GDM4_SPORT_OFF1_MASK | +- GDM4_SPORT_OFF0_MASK, +- FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) | +- FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) | +- FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8)); +- +- /* set PSE Page as 128B */ +- airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG, +- FE_DMA_GLO_L2_SPACE_MASK | FE_DMA_GLO_PG_SZ_MASK, +- FIELD_PREP(FE_DMA_GLO_L2_SPACE_MASK, 2) | +- FE_DMA_GLO_PG_SZ_MASK); +- airoha_fe_wr(eth, REG_FE_RST_GLO_CFG, +- FE_RST_CORE_MASK | FE_RST_GDM3_MBI_ARB_MASK | +- FE_RST_GDM4_MBI_ARB_MASK); +- usleep_range(1000, 2000); +- +- /* connect RxRing1 and RxRing15 to PSE Port0 OQ-1 +- * connect other rings to PSE Port0 OQ-0 +- */ +- airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP0, BIT(4)); +- airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP1, BIT(28)); +- airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP2, BIT(4)); +- airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP3, BIT(28)); +- +- airoha_fe_vip_setup(eth); +- airoha_fe_pse_ports_init(eth); +- +- airoha_fe_set(eth, REG_GDM_MISC_CFG, +- GDM2_RDM_ACK_WAIT_PREF_MASK | +- GDM2_CHN_VLD_MODE_MASK); +- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, +- FIELD_PREP(CDM2_OAM_QSEL_MASK, 15)); +- +- /* init fragment and assemble Force Port */ +- /* NPU Core-3, NPU Bridge Channel-3 */ +- airoha_fe_rmw(eth, REG_IP_FRAG_FP, +- IP_FRAGMENT_PORT_MASK | IP_FRAGMENT_NBQ_MASK, +- FIELD_PREP(IP_FRAGMENT_PORT_MASK, 6) | +- FIELD_PREP(IP_FRAGMENT_NBQ_MASK, 3)); +- /* QDMA LAN, RX Ring-22 */ +- airoha_fe_rmw(eth, REG_IP_FRAG_FP, +- IP_ASSEMBLE_PORT_MASK | IP_ASSEMBLE_NBQ_MASK, +- FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | +- FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); +- +- airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK); +- airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK); +- +- airoha_fe_crsn_qsel_init(eth); +- +- airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK); +- airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK); +- +- /* default aging mode for mbi unlock issue */ +- airoha_fe_rmw(eth, REG_GDM2_CHN_RLS, +- MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK, +- FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) | +- FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3)); +- +- /* disable IFC by default */ +- airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); +- +- /* enable 1:N vlan action, init vlan table */ +- airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); +- +- return airoha_fe_mc_vlan_clear(eth); +-} +- +-static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) +-{ +- enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); +- struct airoha_qdma *qdma = q->qdma; +- struct airoha_eth *eth = qdma->eth; +- int qid = q - &qdma->q_rx[0]; +- int nframes = 0; +- +- while (q->queued < q->ndesc - 1) { +- struct airoha_queue_entry *e = &q->entry[q->head]; +- struct airoha_qdma_desc *desc = &q->desc[q->head]; +- struct page *page; +- int offset; +- u32 val; +- +- page = page_pool_dev_alloc_frag(q->page_pool, &offset, +- q->buf_size); +- if (!page) +- break; +- +- q->head = (q->head + 1) % q->ndesc; +- q->queued++; +- nframes++; +- +- e->buf = page_address(page) + offset; +- e->dma_addr = page_pool_get_dma_addr(page) + offset; +- e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); +- +- dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len, +- dir); +- +- val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); +- WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); +- WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr)); +- val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, q->head); +- WRITE_ONCE(desc->data, cpu_to_le32(val)); +- WRITE_ONCE(desc->msg0, 0); +- WRITE_ONCE(desc->msg1, 0); +- WRITE_ONCE(desc->msg2, 0); +- WRITE_ONCE(desc->msg3, 0); +- +- airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), +- RX_RING_CPU_IDX_MASK, +- FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); +- } +- +- return nframes; +-} +- +-static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, +- struct airoha_qdma_desc *desc) +-{ +- u32 port, sport, msg1 = le32_to_cpu(desc->msg1); +- +- sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); +- switch (sport) { +- case 0x10 ... 0x13: +- port = 0; +- break; +- case 0x2 ... 0x4: +- port = sport - 1; +- break; +- default: +- return -EINVAL; +- } +- +- return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; +-} +- +-static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) +-{ +- enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); +- struct airoha_qdma *qdma = q->qdma; +- struct airoha_eth *eth = qdma->eth; +- int qid = q - &qdma->q_rx[0]; +- int done = 0; +- +- while (done < budget) { +- struct airoha_queue_entry *e = &q->entry[q->tail]; +- struct airoha_qdma_desc *desc = &q->desc[q->tail]; +- dma_addr_t dma_addr = le32_to_cpu(desc->addr); +- u32 desc_ctrl = le32_to_cpu(desc->ctrl); +- struct sk_buff *skb; +- int len, p; +- +- if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) +- break; +- +- if (!dma_addr) +- break; +- +- len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); +- if (!len) +- break; +- +- q->tail = (q->tail + 1) % q->ndesc; +- q->queued--; +- +- dma_sync_single_for_cpu(eth->dev, dma_addr, +- SKB_WITH_OVERHEAD(q->buf_size), dir); +- +- p = airoha_qdma_get_gdm_port(eth, desc); +- if (p < 0 || !eth->ports[p]) { +- page_pool_put_full_page(q->page_pool, +- virt_to_head_page(e->buf), +- true); +- continue; +- } +- +- skb = napi_build_skb(e->buf, q->buf_size); +- if (!skb) { +- page_pool_put_full_page(q->page_pool, +- virt_to_head_page(e->buf), +- true); +- break; +- } +- +- skb_reserve(skb, 2); +- __skb_put(skb, len); +- skb_mark_for_recycle(skb); +- skb->dev = eth->ports[p]->dev; +- skb->protocol = eth_type_trans(skb, skb->dev); +- skb->ip_summed = CHECKSUM_UNNECESSARY; +- skb_record_rx_queue(skb, qid); +- napi_gro_receive(&q->napi, skb); +- +- done++; +- } +- airoha_qdma_fill_rx_queue(q); +- +- return done; +-} +- +-static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) +-{ +- struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); +- int cur, done = 0; +- +- do { +- cur = airoha_qdma_rx_process(q, budget - done); +- done += cur; +- } while (cur && done < budget); +- +- if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1, +- RX_DONE_INT_MASK); +- +- return done; +-} +- +-static int airoha_qdma_init_rx_queue(struct airoha_queue *q, +- struct airoha_qdma *qdma, int ndesc) +-{ +- const struct page_pool_params pp_params = { +- .order = 0, +- .pool_size = 256, +- .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, +- .dma_dir = DMA_FROM_DEVICE, +- .max_len = PAGE_SIZE, +- .nid = NUMA_NO_NODE, +- .dev = qdma->eth->dev, +- .napi = &q->napi, +- }; +- struct airoha_eth *eth = qdma->eth; +- int qid = q - &qdma->q_rx[0], thr; +- dma_addr_t dma_addr; +- +- q->buf_size = PAGE_SIZE / 2; +- q->qdma = qdma; +- +- q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry), +- GFP_KERNEL); +- if (!q->entry) +- return -ENOMEM; +- +- q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc), +- &dma_addr, GFP_KERNEL); +- if (!q->desc) +- return -ENOMEM; +- +- q->page_pool = page_pool_create(&pp_params); +- if (IS_ERR(q->page_pool)) { +- int err = PTR_ERR(q->page_pool); +- +- q->page_pool = NULL; +- return err; +- } +- +- q->ndesc = ndesc; +- netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); +- +- airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); +- airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), +- RX_RING_SIZE_MASK, +- FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); +- +- thr = clamp(ndesc >> 3, 1, 32); +- airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, +- FIELD_PREP(RX_RING_THR_MASK, thr)); +- airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, +- FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); +- +- airoha_qdma_fill_rx_queue(q); +- +- return 0; +-} +- +-static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q) +-{ +- struct airoha_qdma *qdma = q->qdma; +- struct airoha_eth *eth = qdma->eth; +- int qid = q - &qdma->q_rx[0]; +- +- while (q->queued) { +- struct airoha_queue_entry *e = &q->entry[q->tail]; +- struct airoha_qdma_desc *desc = &q->desc[q->tail]; +- struct page *page = virt_to_head_page(e->buf); +- +- dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, +- page_pool_get_dma_dir(q->page_pool)); +- page_pool_put_full_page(q->page_pool, page, false); +- /* Reset DMA descriptor */ +- WRITE_ONCE(desc->ctrl, 0); +- WRITE_ONCE(desc->addr, 0); +- WRITE_ONCE(desc->data, 0); +- WRITE_ONCE(desc->msg0, 0); +- WRITE_ONCE(desc->msg1, 0); +- WRITE_ONCE(desc->msg2, 0); +- WRITE_ONCE(desc->msg3, 0); +- +- q->tail = (q->tail + 1) % q->ndesc; +- q->queued--; +- } +- +- q->head = q->tail; +- /* Set RX_DMA_IDX to RX_CPU_IDX to notify the hw the QDMA RX ring is +- * empty. +- */ +- airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, +- FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); +- airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, +- FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail)); +-} +- +-static int airoha_qdma_init_rx(struct airoha_qdma *qdma) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- int err; +- +- if (!(RX_DONE_INT_MASK & BIT(i))) { +- /* rx-queue not binded to irq */ +- continue; +- } +- +- err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma, +- RX_DSCP_NUM(i)); +- if (err) +- return err; +- } +- +- return 0; +-} +- +-static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) +-{ +- struct airoha_tx_irq_queue *irq_q; +- int id, done = 0, irq_queued; +- struct airoha_qdma *qdma; +- struct airoha_eth *eth; +- u32 status, head; +- +- irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); +- qdma = irq_q->qdma; +- id = irq_q - &qdma->q_tx_irq[0]; +- eth = qdma->eth; +- +- status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(id)); +- head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); +- head = head % irq_q->size; +- irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); +- +- while (irq_queued > 0 && done < budget) { +- u32 qid, val = irq_q->q[head]; +- struct airoha_qdma_desc *desc; +- struct airoha_queue_entry *e; +- struct airoha_queue *q; +- u32 index, desc_ctrl; +- struct sk_buff *skb; +- +- if (val == 0xff) +- break; +- +- irq_q->q[head] = 0xff; /* mark as done */ +- head = (head + 1) % irq_q->size; +- irq_queued--; +- done++; +- +- qid = FIELD_GET(IRQ_RING_IDX_MASK, val); +- if (qid >= ARRAY_SIZE(qdma->q_tx)) +- continue; +- +- q = &qdma->q_tx[qid]; +- if (!q->ndesc) +- continue; +- +- index = FIELD_GET(IRQ_DESC_IDX_MASK, val); +- if (index >= q->ndesc) +- continue; +- +- spin_lock_bh(&q->lock); +- +- if (!q->queued) +- goto unlock; +- +- desc = &q->desc[index]; +- desc_ctrl = le32_to_cpu(desc->ctrl); +- +- if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && +- !(desc_ctrl & QDMA_DESC_DROP_MASK)) +- goto unlock; +- +- e = &q->entry[index]; +- skb = e->skb; +- +- dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, +- DMA_TO_DEVICE); +- memset(e, 0, sizeof(*e)); +- WRITE_ONCE(desc->msg0, 0); +- WRITE_ONCE(desc->msg1, 0); +- q->queued--; +- +- /* completion ring can report out-of-order indexes if hw QoS +- * is enabled and packets with different priority are queued +- * to same DMA ring. Take into account possible out-of-order +- * reports incrementing DMA ring tail pointer +- */ +- while (q->tail != q->head && !q->entry[q->tail].dma_addr) +- q->tail = (q->tail + 1) % q->ndesc; +- +- if (skb) { +- u16 queue = skb_get_queue_mapping(skb); +- struct netdev_queue *txq; +- +- txq = netdev_get_tx_queue(skb->dev, queue); +- netdev_tx_completed_queue(txq, 1, skb->len); +- if (netif_tx_queue_stopped(txq) && +- q->ndesc - q->queued >= q->free_thr) +- netif_tx_wake_queue(txq); +- +- dev_kfree_skb_any(skb); +- } +-unlock: +- spin_unlock_bh(&q->lock); +- } +- +- if (done) { +- int i, len = done >> 7; +- +- for (i = 0; i < len; i++) +- airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), +- IRQ_CLEAR_LEN_MASK, 0x80); +- airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id), +- IRQ_CLEAR_LEN_MASK, (done & 0x7f)); +- } +- +- if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, +- TX_DONE_INT_MASK(id)); +- +- return done; +-} +- +-static int airoha_qdma_init_tx_queue(struct airoha_queue *q, +- struct airoha_qdma *qdma, int size) +-{ +- struct airoha_eth *eth = qdma->eth; +- int i, qid = q - &qdma->q_tx[0]; +- dma_addr_t dma_addr; +- +- spin_lock_init(&q->lock); +- q->ndesc = size; +- q->qdma = qdma; +- q->free_thr = 1 + MAX_SKB_FRAGS; +- +- q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), +- GFP_KERNEL); +- if (!q->entry) +- return -ENOMEM; +- +- q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), +- &dma_addr, GFP_KERNEL); +- if (!q->desc) +- return -ENOMEM; +- +- for (i = 0; i < q->ndesc; i++) { +- u32 val; +- +- val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); +- WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); +- } +- +- /* xmit ring drop default setting */ +- airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), +- TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK); +- +- airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); +- airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, +- FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); +- airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, +- FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); +- +- return 0; +-} +- +-static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, +- struct airoha_qdma *qdma, int size) +-{ +- int id = irq_q - &qdma->q_tx_irq[0]; +- struct airoha_eth *eth = qdma->eth; +- dma_addr_t dma_addr; +- +- netif_napi_add_tx(eth->napi_dev, &irq_q->napi, +- airoha_qdma_tx_napi_poll); +- irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), +- &dma_addr, GFP_KERNEL); +- if (!irq_q->q) +- return -ENOMEM; +- +- memset(irq_q->q, 0xff, size * sizeof(u32)); +- irq_q->size = size; +- irq_q->qdma = qdma; +- +- airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); +- airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, +- FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); +- airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK, +- FIELD_PREP(TX_IRQ_THR_MASK, 1)); +- +- return 0; +-} +- +-static int airoha_qdma_init_tx(struct airoha_qdma *qdma) +-{ +- int i, err; +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { +- err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma, +- IRQ_QUEUE_LEN(i)); +- if (err) +- return err; +- } +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma, +- TX_DSCP_NUM); +- if (err) +- return err; +- } +- +- return 0; +-} +- +-static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) +-{ +- struct airoha_eth *eth = q->qdma->eth; +- +- spin_lock_bh(&q->lock); +- while (q->queued) { +- struct airoha_queue_entry *e = &q->entry[q->tail]; +- +- dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, +- DMA_TO_DEVICE); +- dev_kfree_skb_any(e->skb); +- e->skb = NULL; +- +- q->tail = (q->tail + 1) % q->ndesc; +- q->queued--; +- } +- spin_unlock_bh(&q->lock); +-} +- +-static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) +-{ +- struct airoha_eth *eth = qdma->eth; +- dma_addr_t dma_addr; +- u32 status; +- int size; +- +- size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); +- qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!qdma->hfwd.desc) +- return -ENOMEM; +- +- airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); +- +- size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; +- qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!qdma->hfwd.q) +- return -ENOMEM; +- +- airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); +- +- airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, +- HW_FWD_DSCP_PAYLOAD_SIZE_MASK, +- FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); +- airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, +- FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); +- airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, +- LMGR_INIT_START | LMGR_SRAM_MODE_MASK | +- HW_FWD_DESC_NUM_MASK, +- FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | +- LMGR_INIT_START); +- +- return read_poll_timeout(airoha_qdma_rr, status, +- !(status & LMGR_INIT_START), USEC_PER_MSEC, +- 30 * USEC_PER_MSEC, true, qdma, +- REG_LMGR_INIT_CFG); +-} +- +-static void airoha_qdma_init_qos(struct airoha_qdma *qdma) +-{ +- airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK); +- airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK); +- +- airoha_qdma_clear(qdma, REG_PSE_BUF_USAGE_CFG, +- PSE_BUF_ESTIMATE_EN_MASK); +- +- airoha_qdma_set(qdma, REG_EGRESS_RATE_METER_CFG, +- EGRESS_RATE_METER_EN_MASK | +- EGRESS_RATE_METER_EQ_RATE_EN_MASK); +- /* 2047us x 31 = 63.457ms */ +- airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, +- EGRESS_RATE_METER_WINDOW_SZ_MASK, +- FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f)); +- airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG, +- EGRESS_RATE_METER_TIMESLICE_MASK, +- FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff)); +- +- /* ratelimit init */ +- airoha_qdma_set(qdma, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK); +- /* fast-tick 25us */ +- airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK, +- FIELD_PREP(GLB_FAST_TICK_MASK, 25)); +- airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK, +- FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40)); +- +- airoha_qdma_set(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK); +- airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK, +- FIELD_PREP(EGRESS_FAST_TICK_MASK, 25)); +- airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, +- EGRESS_SLOW_TICK_RATIO_MASK, +- FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40)); +- +- airoha_qdma_set(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK); +- airoha_qdma_clear(qdma, REG_INGRESS_TRTCM_CFG, +- INGRESS_TRTCM_MODE_MASK); +- airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK, +- FIELD_PREP(INGRESS_FAST_TICK_MASK, 125)); +- airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, +- INGRESS_SLOW_TICK_RATIO_MASK, +- FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8)); +- +- airoha_qdma_set(qdma, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK); +- airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK, +- FIELD_PREP(SLA_FAST_TICK_MASK, 25)); +- airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK, +- FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40)); +-} +- +-static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) +-{ +- int i; +- +- for (i = 0; i < AIROHA_NUM_QOS_CHANNELS; i++) { +- /* Tx-cpu transferred count */ +- airoha_qdma_wr(qdma, REG_CNTR_VAL(i << 1), 0); +- airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), +- CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | +- CNTR_ALL_DSCP_RING_EN_MASK | +- FIELD_PREP(CNTR_CHAN_MASK, i)); +- /* Tx-fwd transferred count */ +- airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); +- airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), +- CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | +- CNTR_ALL_DSCP_RING_EN_MASK | +- FIELD_PREP(CNTR_SRC_MASK, 1) | +- FIELD_PREP(CNTR_CHAN_MASK, i)); +- } +-} +- +-static int airoha_qdma_hw_init(struct airoha_qdma *qdma) +-{ +- int i; +- +- /* clear pending irqs */ +- for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) +- airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); +- +- /* setup irqs */ +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK); +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK); +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK); +- +- /* setup irq binding */ +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- if (!qdma->q_tx[i].ndesc) +- continue; +- +- if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i)) +- airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(i), +- TX_RING_IRQ_BLOCKING_CFG_MASK); +- else +- airoha_qdma_clear(qdma, REG_TX_RING_BLOCKING(i), +- TX_RING_IRQ_BLOCKING_CFG_MASK); +- } +- +- airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, +- GLOBAL_CFG_RX_2B_OFFSET_MASK | +- FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | +- GLOBAL_CFG_CPU_TXR_RR_MASK | +- GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | +- GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK | +- GLOBAL_CFG_MULTICAST_EN_MASK | +- GLOBAL_CFG_IRQ0_EN_MASK | GLOBAL_CFG_IRQ1_EN_MASK | +- GLOBAL_CFG_TX_WB_DONE_MASK | +- FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2)); +- +- airoha_qdma_init_qos(qdma); +- +- /* disable qdma rx delay interrupt */ +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; +- +- airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), +- RX_DELAY_INT_MASK); +- } +- +- airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG, +- TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN); +- airoha_qdma_init_qos_stats(qdma); +- +- return 0; +-} +- +-static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) +-{ +- struct airoha_qdma *qdma = dev_instance; +- u32 intr[ARRAY_SIZE(qdma->irqmask)]; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) { +- intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); +- intr[i] &= qdma->irqmask[i]; +- airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); +- } +- +- if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) +- return IRQ_NONE; +- +- if (intr[1] & RX_DONE_INT_MASK) { +- airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1, +- RX_DONE_INT_MASK); +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; +- +- if (intr[1] & BIT(i)) +- napi_schedule(&qdma->q_rx[i].napi); +- } +- } +- +- if (intr[0] & INT_TX_MASK) { +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { +- if (!(intr[0] & TX_DONE_INT_MASK(i))) +- continue; +- +- airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0, +- TX_DONE_INT_MASK(i)); +- napi_schedule(&qdma->q_tx_irq[i].napi); +- } +- } +- +- return IRQ_HANDLED; +-} +- +-static int airoha_qdma_init(struct platform_device *pdev, +- struct airoha_eth *eth, +- struct airoha_qdma *qdma) +-{ +- int err, id = qdma - ð->qdma[0]; +- const char *res; +- +- spin_lock_init(&qdma->irq_lock); +- qdma->eth = eth; +- +- res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id); +- if (!res) +- return -ENOMEM; +- +- qdma->regs = devm_platform_ioremap_resource_byname(pdev, res); +- if (IS_ERR(qdma->regs)) +- return dev_err_probe(eth->dev, PTR_ERR(qdma->regs), +- "failed to iomap qdma%d regs\n", id); +- +- qdma->irq = platform_get_irq(pdev, 4 * id); +- if (qdma->irq < 0) +- return qdma->irq; +- +- err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler, +- IRQF_SHARED, KBUILD_MODNAME, qdma); +- if (err) +- return err; +- +- err = airoha_qdma_init_rx(qdma); +- if (err) +- return err; +- +- err = airoha_qdma_init_tx(qdma); +- if (err) +- return err; +- +- err = airoha_qdma_init_hfwd_queues(qdma); +- if (err) +- return err; +- +- return airoha_qdma_hw_init(qdma); +-} +- +-static int airoha_hw_init(struct platform_device *pdev, +- struct airoha_eth *eth) +-{ +- int err, i; +- +- /* disable xsi */ +- err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), +- eth->xsi_rsts); +- if (err) +- return err; +- +- err = reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts); +- if (err) +- return err; +- +- msleep(20); +- err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); +- if (err) +- return err; +- +- msleep(20); +- err = airoha_fe_init(eth); +- if (err) +- return err; +- +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { +- err = airoha_qdma_init(pdev, eth, ð->qdma[i]); +- if (err) +- return err; +- } +- +- set_bit(DEV_STATE_INITIALIZED, ð->state); +- +- return 0; +-} +- +-static void airoha_hw_cleanup(struct airoha_qdma *qdma) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; +- +- netif_napi_del(&qdma->q_rx[i].napi); +- airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]); +- if (qdma->q_rx[i].page_pool) +- page_pool_destroy(qdma->q_rx[i].page_pool); +- } +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) +- netif_napi_del(&qdma->q_tx_irq[i].napi); +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- if (!qdma->q_tx[i].ndesc) +- continue; +- +- airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); +- } +-} +- +-static void airoha_qdma_start_napi(struct airoha_qdma *qdma) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) +- napi_enable(&qdma->q_tx_irq[i].napi); +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; +- +- napi_enable(&qdma->q_rx[i].napi); +- } +-} +- +-static void airoha_qdma_stop_napi(struct airoha_qdma *qdma) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) +- napi_disable(&qdma->q_tx_irq[i].napi); +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; +- +- napi_disable(&qdma->q_rx[i].napi); +- } +-} +- +-static void airoha_update_hw_stats(struct airoha_gdm_port *port) +-{ +- struct airoha_eth *eth = port->qdma->eth; +- u32 val, i = 0; +- +- spin_lock(&port->stats.lock); +- u64_stats_update_begin(&port->stats.syncp); +- +- /* TX */ +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); +- port->stats.tx_ok_pkts += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); +- port->stats.tx_ok_pkts += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); +- port->stats.tx_ok_bytes += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); +- port->stats.tx_ok_bytes += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); +- port->stats.tx_drops += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); +- port->stats.tx_broadcast += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); +- port->stats.tx_multicast += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); +- port->stats.tx_len[i] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); +- port->stats.tx_len[i++] += val; +- +- /* RX */ +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); +- port->stats.rx_ok_pkts += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); +- port->stats.rx_ok_pkts += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); +- port->stats.rx_ok_bytes += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); +- port->stats.rx_ok_bytes += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); +- port->stats.rx_drops += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); +- port->stats.rx_broadcast += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); +- port->stats.rx_multicast += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); +- port->stats.rx_errors += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); +- port->stats.rx_crc_error += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); +- port->stats.rx_over_errors += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); +- port->stats.rx_fragment += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); +- port->stats.rx_jabber += val; +- +- i = 0; +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); +- port->stats.rx_len[i] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; +- +- val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); +- port->stats.rx_len[i++] += val; +- +- /* reset mib counters */ +- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id), +- FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); +- +- u64_stats_update_end(&port->stats.syncp); +- spin_unlock(&port->stats.lock); +-} +- +-static int airoha_dev_open(struct net_device *dev) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_qdma *qdma = port->qdma; +- int err; +- +- netif_tx_start_all_queues(dev); +- err = airoha_set_gdm_ports(qdma->eth, true); +- if (err) +- return err; +- +- if (netdev_uses_dsa(dev)) +- airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), +- GDM_STAG_EN_MASK); +- else +- airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), +- GDM_STAG_EN_MASK); +- +- airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, +- GLOBAL_CFG_TX_DMA_EN_MASK | +- GLOBAL_CFG_RX_DMA_EN_MASK); +- +- return 0; +-} +- +-static int airoha_dev_stop(struct net_device *dev) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_qdma *qdma = port->qdma; +- int i, err; +- +- netif_tx_disable(dev); +- err = airoha_set_gdm_ports(qdma->eth, false); +- if (err) +- return err; +- +- airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, +- GLOBAL_CFG_TX_DMA_EN_MASK | +- GLOBAL_CFG_RX_DMA_EN_MASK); +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- if (!qdma->q_tx[i].ndesc) +- continue; +- +- airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); +- netdev_tx_reset_subqueue(dev, i); +- } +- +- return 0; +-} +- +-static int airoha_dev_set_macaddr(struct net_device *dev, void *p) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- int err; +- +- err = eth_mac_addr(dev, p); +- if (err) +- return err; +- +- airoha_set_macaddr(port, dev->dev_addr); +- +- return 0; +-} +- +-static int airoha_dev_init(struct net_device *dev) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- +- airoha_set_macaddr(port, dev->dev_addr); +- +- return 0; +-} +- +-static void airoha_dev_get_stats64(struct net_device *dev, +- struct rtnl_link_stats64 *storage) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- unsigned int start; +- +- airoha_update_hw_stats(port); +- do { +- start = u64_stats_fetch_begin(&port->stats.syncp); +- storage->rx_packets = port->stats.rx_ok_pkts; +- storage->tx_packets = port->stats.tx_ok_pkts; +- storage->rx_bytes = port->stats.rx_ok_bytes; +- storage->tx_bytes = port->stats.tx_ok_bytes; +- storage->multicast = port->stats.rx_multicast; +- storage->rx_errors = port->stats.rx_errors; +- storage->rx_dropped = port->stats.rx_drops; +- storage->tx_dropped = port->stats.tx_drops; +- storage->rx_crc_errors = port->stats.rx_crc_error; +- storage->rx_over_errors = port->stats.rx_over_errors; +- } while (u64_stats_fetch_retry(&port->stats.syncp, start)); +-} +- +-static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, +- struct net_device *sb_dev) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- int queue, channel; +- +- /* For dsa device select QoS channel according to the dsa user port +- * index, rely on port id otherwise. Select QoS queue based on the +- * skb priority. +- */ +- channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; +- channel = channel % AIROHA_NUM_QOS_CHANNELS; +- queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ +- queue = channel * AIROHA_NUM_QOS_QUEUES + queue; +- +- return queue < dev->num_tx_queues ? queue : 0; +-} +- +-static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, +- struct net_device *dev) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags; +- u32 msg0, msg1, len = skb_headlen(skb); +- struct airoha_qdma *qdma = port->qdma; +- struct netdev_queue *txq; +- struct airoha_queue *q; +- void *data = skb->data; +- int i, qid; +- u16 index; +- u8 fport; +- +- qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); +- msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, +- qid / AIROHA_NUM_QOS_QUEUES) | +- FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK, +- qid % AIROHA_NUM_QOS_QUEUES); +- if (skb->ip_summed == CHECKSUM_PARTIAL) +- msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) | +- FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) | +- FIELD_PREP(QDMA_ETH_TXMSG_ICO_MASK, 1); +- +- /* TSO: fill MSS info in tcp checksum field */ +- if (skb_is_gso(skb)) { +- if (skb_cow_head(skb, 0)) +- goto error; +- +- if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | +- SKB_GSO_TCPV6)) { +- __be16 csum = cpu_to_be16(skb_shinfo(skb)->gso_size); +- +- tcp_hdr(skb)->check = (__force __sum16)csum; +- msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1); +- } +- } +- +- fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; +- msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | +- FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); +- +- q = &qdma->q_tx[qid]; +- if (WARN_ON_ONCE(!q->ndesc)) +- goto error; +- +- spin_lock_bh(&q->lock); +- +- txq = netdev_get_tx_queue(dev, qid); +- if (q->queued + nr_frags > q->ndesc) { +- /* not enough space in the queue */ +- netif_tx_stop_queue(txq); +- spin_unlock_bh(&q->lock); +- return NETDEV_TX_BUSY; +- } +- +- index = q->head; +- for (i = 0; i < nr_frags; i++) { +- struct airoha_qdma_desc *desc = &q->desc[index]; +- struct airoha_queue_entry *e = &q->entry[index]; +- skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; +- dma_addr_t addr; +- u32 val; +- +- addr = dma_map_single(dev->dev.parent, data, len, +- DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(dev->dev.parent, addr))) +- goto error_unmap; +- +- index = (index + 1) % q->ndesc; +- +- val = FIELD_PREP(QDMA_DESC_LEN_MASK, len); +- if (i < nr_frags - 1) +- val |= FIELD_PREP(QDMA_DESC_MORE_MASK, 1); +- WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); +- WRITE_ONCE(desc->addr, cpu_to_le32(addr)); +- val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index); +- WRITE_ONCE(desc->data, cpu_to_le32(val)); +- WRITE_ONCE(desc->msg0, cpu_to_le32(msg0)); +- WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); +- WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); +- +- e->skb = i ? NULL : skb; +- e->dma_addr = addr; +- e->dma_len = len; +- +- data = skb_frag_address(frag); +- len = skb_frag_size(frag); +- } +- +- q->head = index; +- q->queued += i; +- +- skb_tx_timestamp(skb); +- netdev_tx_sent_queue(txq, skb->len); +- +- if (netif_xmit_stopped(txq) || !netdev_xmit_more()) +- airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), +- TX_RING_CPU_IDX_MASK, +- FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); +- +- if (q->ndesc - q->queued < q->free_thr) +- netif_tx_stop_queue(txq); +- +- spin_unlock_bh(&q->lock); +- +- return NETDEV_TX_OK; +- +-error_unmap: +- for (i--; i >= 0; i--) { +- index = (q->head + i) % q->ndesc; +- dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr, +- q->entry[index].dma_len, DMA_TO_DEVICE); +- } +- +- spin_unlock_bh(&q->lock); +-error: +- dev_kfree_skb_any(skb); +- dev->stats.tx_dropped++; +- +- return NETDEV_TX_OK; +-} +- +-static void airoha_ethtool_get_drvinfo(struct net_device *dev, +- struct ethtool_drvinfo *info) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->qdma->eth; +- +- strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); +- strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); +-} +- +-static void airoha_ethtool_get_mac_stats(struct net_device *dev, +- struct ethtool_eth_mac_stats *stats) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- unsigned int start; +- +- airoha_update_hw_stats(port); +- do { +- start = u64_stats_fetch_begin(&port->stats.syncp); +- stats->MulticastFramesXmittedOK = port->stats.tx_multicast; +- stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; +- stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; +- } while (u64_stats_fetch_retry(&port->stats.syncp, start)); +-} +- +-static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { +- { 0, 64 }, +- { 65, 127 }, +- { 128, 255 }, +- { 256, 511 }, +- { 512, 1023 }, +- { 1024, 1518 }, +- { 1519, 10239 }, +- {}, +-}; +- +-static void +-airoha_ethtool_get_rmon_stats(struct net_device *dev, +- struct ethtool_rmon_stats *stats, +- const struct ethtool_rmon_hist_range **ranges) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_hw_stats *hw_stats = &port->stats; +- unsigned int start; +- +- BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != +- ARRAY_SIZE(hw_stats->tx_len) + 1); +- BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != +- ARRAY_SIZE(hw_stats->rx_len) + 1); +- +- *ranges = airoha_ethtool_rmon_ranges; +- airoha_update_hw_stats(port); +- do { +- int i; +- +- start = u64_stats_fetch_begin(&port->stats.syncp); +- stats->fragments = hw_stats->rx_fragment; +- stats->jabbers = hw_stats->rx_jabber; +- for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1; +- i++) { +- stats->hist[i] = hw_stats->rx_len[i]; +- stats->hist_tx[i] = hw_stats->tx_len[i]; +- } +- } while (u64_stats_fetch_retry(&port->stats.syncp, start)); +-} +- +-static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port, +- int channel, enum tx_sched_mode mode, +- const u16 *weights, u8 n_weights) +-{ +- int i; +- +- for (i = 0; i < AIROHA_NUM_TX_RING; i++) +- airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel), +- TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); +- +- for (i = 0; i < n_weights; i++) { +- u32 status; +- int err; +- +- airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG, +- TWRR_RW_CMD_MASK | +- FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | +- FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | +- FIELD_PREP(TWRR_VALUE_MASK, weights[i])); +- err = read_poll_timeout(airoha_qdma_rr, status, +- status & TWRR_RW_CMD_DONE, +- USEC_PER_MSEC, 10 * USEC_PER_MSEC, +- true, port->qdma, +- REG_TXWRR_WEIGHT_CFG); +- if (err) +- return err; +- } +- +- airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), +- CHAN_QOS_MODE_MASK(channel), +- mode << __ffs(CHAN_QOS_MODE_MASK(channel))); +- +- return 0; +-} +- +-static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port, +- int channel) +-{ +- static const u16 w[AIROHA_NUM_QOS_QUEUES] = {}; +- +- return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w, +- ARRAY_SIZE(w)); +-} +- +-static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port, +- int channel, +- struct tc_ets_qopt_offload *opt) +-{ +- struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; +- enum tx_sched_mode mode = TC_SCH_SP; +- u16 w[AIROHA_NUM_QOS_QUEUES] = {}; +- int i, nstrict = 0, nwrr, qidx; +- +- if (p->bands > AIROHA_NUM_QOS_QUEUES) +- return -EINVAL; +- +- for (i = 0; i < p->bands; i++) { +- if (!p->quanta[i]) +- nstrict++; +- } +- +- /* this configuration is not supported by the hw */ +- if (nstrict == AIROHA_NUM_QOS_QUEUES - 1) +- return -EINVAL; +- +- /* EN7581 SoC supports fixed QoS band priority where WRR queues have +- * lowest priorities with respect to SP ones. +- * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn +- */ +- nwrr = p->bands - nstrict; +- qidx = nstrict && nwrr ? nstrict : 0; +- for (i = 1; i <= p->bands; i++) { +- if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx) +- return -EINVAL; +- +- qidx = i == nwrr ? 0 : qidx + 1; +- } +- +- for (i = 0; i < nwrr; i++) +- w[i] = p->weights[nstrict + i]; +- +- if (!nstrict) +- mode = TC_SCH_WRR8; +- else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1) +- mode = nstrict + 1; +- +- return airoha_qdma_set_chan_tx_sched(port, channel, mode, w, +- ARRAY_SIZE(w)); +-} +- +-static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port, +- int channel, +- struct tc_ets_qopt_offload *opt) +-{ +- u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, +- REG_CNTR_VAL(channel << 1)); +- u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, +- REG_CNTR_VAL((channel << 1) + 1)); +- u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + +- (fwd_tx_packets - port->fwd_tx_packets); +- _bstats_update(opt->stats.bstats, 0, tx_packets); +- +- port->cpu_tx_packets = cpu_tx_packets; +- port->fwd_tx_packets = fwd_tx_packets; +- +- return 0; +-} +- +-static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, +- struct tc_ets_qopt_offload *opt) +-{ +- int channel = TC_H_MAJ(opt->handle) >> 16; +- +- if (opt->parent == TC_H_ROOT) +- return -EINVAL; +- +- switch (opt->command) { +- case TC_ETS_REPLACE: +- return airoha_qdma_set_tx_ets_sched(port, channel, opt); +- case TC_ETS_DESTROY: +- /* PRIO is default qdisc scheduler */ +- return airoha_qdma_set_tx_prio_sched(port, channel); +- case TC_ETS_STATS: +- return airoha_qdma_get_tx_ets_stats(port, channel, opt); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, +- u32 addr, enum trtcm_param_type param, +- enum trtcm_mode_type mode, +- u32 *val_low, u32 *val_high) +-{ +- u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); +- u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | +- FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | +- FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | +- FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); +- +- airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); +- if (read_poll_timeout(airoha_qdma_rr, val, +- val & TRTCM_PARAM_RW_DONE_MASK, +- USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, +- qdma, REG_TRTCM_CFG_PARAM(addr))) +- return -ETIMEDOUT; +- +- *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); +- if (val_high) +- *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); +- +- return 0; +-} +- +-static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel, +- u32 addr, enum trtcm_param_type param, +- enum trtcm_mode_type mode, u32 val) +-{ +- u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel); +- u32 config = TRTCM_PARAM_RW_MASK | +- FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) | +- FIELD_PREP(TRTCM_METER_GROUP_MASK, group) | +- FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) | +- FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode); +- +- airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); +- airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); +- +- return read_poll_timeout(airoha_qdma_rr, val, +- val & TRTCM_PARAM_RW_DONE_MASK, +- USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, +- qdma, REG_TRTCM_CFG_PARAM(addr)); +-} +- +-static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel, +- u32 addr, enum trtcm_mode_type mode, +- bool enable, u32 enable_mask) +-{ +- u32 val; +- +- if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, +- mode, &val, NULL)) +- return -EINVAL; +- +- val = enable ? val | enable_mask : val & ~enable_mask; +- +- return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, +- mode, val); +-} +- +-static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma, +- int channel, u32 addr, +- enum trtcm_mode_type mode, +- u32 rate_val, u32 bucket_size) +-{ +- u32 val, config, tick, unit, rate, rate_frac; +- int err; +- +- if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE, +- mode, &config, NULL)) +- return -EINVAL; +- +- val = airoha_qdma_rr(qdma, addr); +- tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); +- if (config & TRTCM_TICK_SEL) +- tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); +- if (!tick) +- return -EINVAL; +- +- unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; +- if (!unit) +- return -EINVAL; +- +- rate = rate_val / unit; +- rate_frac = rate_val % unit; +- rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; +- rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | +- FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); +- +- err = airoha_qdma_set_trtcm_param(qdma, channel, addr, +- TRTCM_TOKEN_RATE_MODE, mode, rate); +- if (err) +- return err; +- +- val = max_t(u32, bucket_size, MIN_TOKEN_SIZE); +- val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); +- +- return airoha_qdma_set_trtcm_param(qdma, channel, addr, +- TRTCM_BUCKETSIZE_SHIFT_MODE, +- mode, val); +-} +- +-static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port, +- int channel, u32 rate, +- u32 bucket_size) +-{ +- int i, err; +- +- for (i = 0; i <= TRTCM_PEAK_MODE; i++) { +- err = airoha_qdma_set_trtcm_config(port->qdma, channel, +- REG_EGRESS_TRTCM_CFG, i, +- !!rate, TRTCM_METER_MODE); +- if (err) +- return err; +- +- err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel, +- REG_EGRESS_TRTCM_CFG, +- i, rate, bucket_size); +- if (err) +- return err; +- } +- +- return 0; +-} +- +-static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port, +- struct tc_htb_qopt_offload *opt) +-{ +- u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; +- u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ +- struct net_device *dev = port->dev; +- int num_tx_queues = dev->real_num_tx_queues; +- int err; +- +- if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { +- NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); +- return -EINVAL; +- } +- +- err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum); +- if (err) { +- NL_SET_ERR_MSG_MOD(opt->extack, +- "failed configuring htb offload"); +- return err; +- } +- +- if (opt->command == TC_HTB_NODE_MODIFY) +- return 0; +- +- err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); +- if (err) { +- airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum); +- NL_SET_ERR_MSG_MOD(opt->extack, +- "failed setting real_num_tx_queues"); +- return err; +- } +- +- set_bit(channel, port->qos_sq_bmap); +- opt->qid = AIROHA_NUM_TX_RING + channel; +- +- return 0; +-} +- +-static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue) +-{ +- struct net_device *dev = port->dev; +- +- netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); +- airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0); +- clear_bit(queue, port->qos_sq_bmap); +-} +- +-static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port, +- struct tc_htb_qopt_offload *opt) +-{ +- u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; +- +- if (!test_bit(channel, port->qos_sq_bmap)) { +- NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); +- return -EINVAL; +- } +- +- airoha_tc_remove_htb_queue(port, channel); +- +- return 0; +-} +- +-static int airoha_tc_htb_destroy(struct airoha_gdm_port *port) +-{ +- int q; +- +- for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) +- airoha_tc_remove_htb_queue(port, q); +- +- return 0; +-} +- +-static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port, +- struct tc_htb_qopt_offload *opt) +-{ +- u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; +- +- if (!test_bit(channel, port->qos_sq_bmap)) { +- NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); +- return -EINVAL; +- } +- +- opt->qid = channel; +- +- return 0; +-} +- +-static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port, +- struct tc_htb_qopt_offload *opt) +-{ +- switch (opt->command) { +- case TC_HTB_CREATE: +- break; +- case TC_HTB_DESTROY: +- return airoha_tc_htb_destroy(port); +- case TC_HTB_NODE_MODIFY: +- case TC_HTB_LEAF_ALLOC_QUEUE: +- return airoha_tc_htb_alloc_leaf_queue(port, opt); +- case TC_HTB_LEAF_DEL: +- case TC_HTB_LEAF_DEL_LAST: +- case TC_HTB_LEAF_DEL_LAST_FORCE: +- return airoha_tc_htb_delete_leaf_queue(port, opt); +- case TC_HTB_LEAF_QUERY_QUEUE: +- return airoha_tc_get_htb_get_leaf_queue(port, opt); +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +- +-static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, +- void *type_data) +-{ +- struct airoha_gdm_port *port = netdev_priv(dev); +- +- switch (type) { +- case TC_SETUP_QDISC_ETS: +- return airoha_tc_setup_qdisc_ets(port, type_data); +- case TC_SETUP_QDISC_HTB: +- return airoha_tc_setup_qdisc_htb(port, type_data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static const struct net_device_ops airoha_netdev_ops = { +- .ndo_init = airoha_dev_init, +- .ndo_open = airoha_dev_open, +- .ndo_stop = airoha_dev_stop, +- .ndo_select_queue = airoha_dev_select_queue, +- .ndo_start_xmit = airoha_dev_xmit, +- .ndo_get_stats64 = airoha_dev_get_stats64, +- .ndo_set_mac_address = airoha_dev_set_macaddr, +- .ndo_setup_tc = airoha_dev_tc_setup, +-}; +- +-static const struct ethtool_ops airoha_ethtool_ops = { +- .get_drvinfo = airoha_ethtool_get_drvinfo, +- .get_eth_mac_stats = airoha_ethtool_get_mac_stats, +- .get_rmon_stats = airoha_ethtool_get_rmon_stats, +-}; +- +-static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np) +-{ +- const __be32 *id_ptr = of_get_property(np, "reg", NULL); +- struct airoha_gdm_port *port; +- struct airoha_qdma *qdma; +- struct net_device *dev; +- int err, index; +- u32 id; +- +- if (!id_ptr) { +- dev_err(eth->dev, "missing gdm port id\n"); +- return -EINVAL; +- } +- +- id = be32_to_cpup(id_ptr); +- index = id - 1; +- +- if (!id || id > ARRAY_SIZE(eth->ports)) { +- dev_err(eth->dev, "invalid gdm port id: %d\n", id); +- return -EINVAL; +- } +- +- if (eth->ports[index]) { +- dev_err(eth->dev, "duplicate gdm port id: %d\n", id); +- return -EINVAL; +- } +- +- dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), +- AIROHA_NUM_NETDEV_TX_RINGS, +- AIROHA_NUM_RX_RING); +- if (!dev) { +- dev_err(eth->dev, "alloc_etherdev failed\n"); +- return -ENOMEM; +- } +- +- qdma = ð->qdma[index % AIROHA_MAX_NUM_QDMA]; +- dev->netdev_ops = &airoha_netdev_ops; +- dev->ethtool_ops = &airoha_ethtool_ops; +- dev->max_mtu = AIROHA_MAX_MTU; +- dev->watchdog_timeo = 5 * HZ; +- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | +- NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | +- NETIF_F_SG | NETIF_F_TSO | +- NETIF_F_HW_TC; +- dev->features |= dev->hw_features; +- dev->dev.of_node = np; +- dev->irq = qdma->irq; +- SET_NETDEV_DEV(dev, eth->dev); +- +- /* reserve hw queues for HTB offloading */ +- err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING); +- if (err) +- return err; +- +- err = of_get_ethdev_address(np, dev); +- if (err) { +- if (err == -EPROBE_DEFER) +- return err; +- +- eth_hw_addr_random(dev); +- dev_info(eth->dev, "generated random MAC address %pM\n", +- dev->dev_addr); +- } +- +- port = netdev_priv(dev); +- u64_stats_init(&port->stats.syncp); +- spin_lock_init(&port->stats.lock); +- port->qdma = qdma; +- port->dev = dev; +- port->id = id; +- eth->ports[index] = port; +- +- return register_netdev(dev); +-} +- +-static int airoha_probe(struct platform_device *pdev) +-{ +- struct device_node *np; +- struct airoha_eth *eth; +- int i, err; +- +- eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL); +- if (!eth) +- return -ENOMEM; +- +- eth->dev = &pdev->dev; +- +- err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32)); +- if (err) { +- dev_err(eth->dev, "failed configuring DMA mask\n"); +- return err; +- } +- +- eth->fe_regs = devm_platform_ioremap_resource_byname(pdev, "fe"); +- if (IS_ERR(eth->fe_regs)) +- return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs), +- "failed to iomap fe regs\n"); +- +- eth->rsts[0].id = "fe"; +- eth->rsts[1].id = "pdma"; +- eth->rsts[2].id = "qdma"; +- err = devm_reset_control_bulk_get_exclusive(eth->dev, +- ARRAY_SIZE(eth->rsts), +- eth->rsts); +- if (err) { +- dev_err(eth->dev, "failed to get bulk reset lines\n"); +- return err; +- } +- +- eth->xsi_rsts[0].id = "xsi-mac"; +- eth->xsi_rsts[1].id = "hsi0-mac"; +- eth->xsi_rsts[2].id = "hsi1-mac"; +- eth->xsi_rsts[3].id = "hsi-mac"; +- eth->xsi_rsts[4].id = "xfp-mac"; +- err = devm_reset_control_bulk_get_exclusive(eth->dev, +- ARRAY_SIZE(eth->xsi_rsts), +- eth->xsi_rsts); +- if (err) { +- dev_err(eth->dev, "failed to get bulk xsi reset lines\n"); +- return err; +- } +- +- eth->napi_dev = alloc_netdev_dummy(0); +- if (!eth->napi_dev) +- return -ENOMEM; +- +- /* Enable threaded NAPI by default */ +- eth->napi_dev->threaded = true; +- strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name)); +- platform_set_drvdata(pdev, eth); +- +- err = airoha_hw_init(pdev, eth); +- if (err) +- goto error_hw_cleanup; +- +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) +- airoha_qdma_start_napi(ð->qdma[i]); +- +- for_each_child_of_node(pdev->dev.of_node, np) { +- if (!of_device_is_compatible(np, "airoha,eth-mac")) +- continue; +- +- if (!of_device_is_available(np)) +- continue; +- +- err = airoha_alloc_gdm_port(eth, np); +- if (err) { +- of_node_put(np); +- goto error_napi_stop; +- } +- } +- +- return 0; +- +-error_napi_stop: +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) +- airoha_qdma_stop_napi(ð->qdma[i]); +-error_hw_cleanup: +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) +- airoha_hw_cleanup(ð->qdma[i]); +- +- for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { +- struct airoha_gdm_port *port = eth->ports[i]; +- +- if (port && port->dev->reg_state == NETREG_REGISTERED) +- unregister_netdev(port->dev); +- } +- free_netdev(eth->napi_dev); +- platform_set_drvdata(pdev, NULL); +- +- return err; +-} +- +-static void airoha_remove(struct platform_device *pdev) +-{ +- struct airoha_eth *eth = platform_get_drvdata(pdev); +- int i; +- +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { +- airoha_qdma_stop_napi(ð->qdma[i]); +- airoha_hw_cleanup(ð->qdma[i]); +- } +- +- for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { +- struct airoha_gdm_port *port = eth->ports[i]; +- +- if (!port) +- continue; +- +- unregister_netdev(port->dev); +- } +- free_netdev(eth->napi_dev); +- +- platform_set_drvdata(pdev, NULL); +-} +- +-static const struct of_device_id of_airoha_match[] = { +- { .compatible = "airoha,en7581-eth" }, +- { /* sentinel */ } +-}; +-MODULE_DEVICE_TABLE(of, of_airoha_match); +- +-static struct platform_driver airoha_driver = { +- .probe = airoha_probe, +- .remove_new = airoha_remove, +- .driver = { +- .name = KBUILD_MODNAME, +- .of_match_table = of_airoha_match, +- }, +-}; +-module_platform_driver(airoha_driver); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Lorenzo Bianconi "); +-MODULE_DESCRIPTION("Ethernet driver for Airoha SoC"); diff --git a/target/linux/airoha/patches-6.12/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch b/target/linux/airoha/patches-6.12/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch new file mode 100644 index 00000000000..85391281a25 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch @@ -0,0 +1,538 @@ +From b38f4ff0ceacd6ce8d333a8dc90f405a040968d3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:10 +0100 +Subject: [PATCH 02/15] net: airoha: Move definitions in airoha_eth.h + +Move common airoha_eth definitions in airoha_eth.h in order to reuse +them for Packet Processor Engine (PPE) codebase. +PPE module is used to enable support for flowtable hw offloading in +airoha_eth driver. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 240 +--------------------- + drivers/net/ethernet/airoha/airoha_eth.h | 251 +++++++++++++++++++++++ + 2 files changed, 252 insertions(+), 239 deletions(-) + create mode 100644 drivers/net/ethernet/airoha/airoha_eth.h + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -3,14 +3,9 @@ + * Copyright (c) 2024 AIROHA Inc + * Author: Lorenzo Bianconi + */ +-#include +-#include +-#include +-#include + #include + #include + #include +-#include + #include + #include + #include +@@ -18,35 +13,7 @@ + #include + #include + +-#define AIROHA_MAX_NUM_GDM_PORTS 1 +-#define AIROHA_MAX_NUM_QDMA 2 +-#define AIROHA_MAX_NUM_RSTS 3 +-#define AIROHA_MAX_NUM_XSI_RSTS 5 +-#define AIROHA_MAX_MTU 2000 +-#define AIROHA_MAX_PACKET_SIZE 2048 +-#define AIROHA_NUM_QOS_CHANNELS 4 +-#define AIROHA_NUM_QOS_QUEUES 8 +-#define AIROHA_NUM_TX_RING 32 +-#define AIROHA_NUM_RX_RING 32 +-#define AIROHA_NUM_NETDEV_TX_RINGS (AIROHA_NUM_TX_RING + \ +- AIROHA_NUM_QOS_CHANNELS) +-#define AIROHA_FE_MC_MAX_VLAN_TABLE 64 +-#define AIROHA_FE_MC_MAX_VLAN_PORT 16 +-#define AIROHA_NUM_TX_IRQ 2 +-#define HW_DSCP_NUM 2048 +-#define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) +-#define TX_DSCP_NUM 1024 +-#define RX_DSCP_NUM(_n) \ +- ((_n) == 2 ? 128 : \ +- (_n) == 11 ? 128 : \ +- (_n) == 15 ? 128 : \ +- (_n) == 0 ? 1024 : 16) +- +-#define PSE_RSV_PAGES 128 +-#define PSE_QUEUE_RSV_PAGES 64 +- +-#define QDMA_METER_IDX(_n) ((_n) & 0xff) +-#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) ++#include "airoha_eth.h" + + /* FE */ + #define PSE_BASE 0x0100 +@@ -706,211 +673,6 @@ struct airoha_qdma_fwd_desc { + __le32 rsv1; + }; + +-enum { +- QDMA_INT_REG_IDX0, +- QDMA_INT_REG_IDX1, +- QDMA_INT_REG_IDX2, +- QDMA_INT_REG_IDX3, +- QDMA_INT_REG_IDX4, +- QDMA_INT_REG_MAX +-}; +- +-enum { +- XSI_PCIE0_PORT, +- XSI_PCIE1_PORT, +- XSI_USB_PORT, +- XSI_AE_PORT, +- XSI_ETH_PORT, +-}; +- +-enum { +- XSI_PCIE0_VIP_PORT_MASK = BIT(22), +- XSI_PCIE1_VIP_PORT_MASK = BIT(23), +- XSI_USB_VIP_PORT_MASK = BIT(25), +- XSI_ETH_VIP_PORT_MASK = BIT(24), +-}; +- +-enum { +- DEV_STATE_INITIALIZED, +-}; +- +-enum { +- CDM_CRSN_QSEL_Q1 = 1, +- CDM_CRSN_QSEL_Q5 = 5, +- CDM_CRSN_QSEL_Q6 = 6, +- CDM_CRSN_QSEL_Q15 = 15, +-}; +- +-enum { +- CRSN_08 = 0x8, +- CRSN_21 = 0x15, /* KA */ +- CRSN_22 = 0x16, /* hit bind and force route to CPU */ +- CRSN_24 = 0x18, +- CRSN_25 = 0x19, +-}; +- +-enum { +- FE_PSE_PORT_CDM1, +- FE_PSE_PORT_GDM1, +- FE_PSE_PORT_GDM2, +- FE_PSE_PORT_GDM3, +- FE_PSE_PORT_PPE1, +- FE_PSE_PORT_CDM2, +- FE_PSE_PORT_CDM3, +- FE_PSE_PORT_CDM4, +- FE_PSE_PORT_PPE2, +- FE_PSE_PORT_GDM4, +- FE_PSE_PORT_CDM5, +- FE_PSE_PORT_DROP = 0xf, +-}; +- +-enum tx_sched_mode { +- TC_SCH_WRR8, +- TC_SCH_SP, +- TC_SCH_WRR7, +- TC_SCH_WRR6, +- TC_SCH_WRR5, +- TC_SCH_WRR4, +- TC_SCH_WRR3, +- TC_SCH_WRR2, +-}; +- +-enum trtcm_param_type { +- TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ +- TRTCM_TOKEN_RATE_MODE, +- TRTCM_BUCKETSIZE_SHIFT_MODE, +- TRTCM_BUCKET_COUNTER_MODE, +-}; +- +-enum trtcm_mode_type { +- TRTCM_COMMIT_MODE, +- TRTCM_PEAK_MODE, +-}; +- +-enum trtcm_param { +- TRTCM_TICK_SEL = BIT(0), +- TRTCM_PKT_MODE = BIT(1), +- TRTCM_METER_MODE = BIT(2), +-}; +- +-#define MIN_TOKEN_SIZE 4096 +-#define MAX_TOKEN_SIZE_OFFSET 17 +-#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6) +-#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) +- +-struct airoha_queue_entry { +- union { +- void *buf; +- struct sk_buff *skb; +- }; +- dma_addr_t dma_addr; +- u16 dma_len; +-}; +- +-struct airoha_queue { +- struct airoha_qdma *qdma; +- +- /* protect concurrent queue accesses */ +- spinlock_t lock; +- struct airoha_queue_entry *entry; +- struct airoha_qdma_desc *desc; +- u16 head; +- u16 tail; +- +- int queued; +- int ndesc; +- int free_thr; +- int buf_size; +- +- struct napi_struct napi; +- struct page_pool *page_pool; +-}; +- +-struct airoha_tx_irq_queue { +- struct airoha_qdma *qdma; +- +- struct napi_struct napi; +- +- int size; +- u32 *q; +-}; +- +-struct airoha_hw_stats { +- /* protect concurrent hw_stats accesses */ +- spinlock_t lock; +- struct u64_stats_sync syncp; +- +- /* get_stats64 */ +- u64 rx_ok_pkts; +- u64 tx_ok_pkts; +- u64 rx_ok_bytes; +- u64 tx_ok_bytes; +- u64 rx_multicast; +- u64 rx_errors; +- u64 rx_drops; +- u64 tx_drops; +- u64 rx_crc_error; +- u64 rx_over_errors; +- /* ethtool stats */ +- u64 tx_broadcast; +- u64 tx_multicast; +- u64 tx_len[7]; +- u64 rx_broadcast; +- u64 rx_fragment; +- u64 rx_jabber; +- u64 rx_len[7]; +-}; +- +-struct airoha_qdma { +- struct airoha_eth *eth; +- void __iomem *regs; +- +- /* protect concurrent irqmask accesses */ +- spinlock_t irq_lock; +- u32 irqmask[QDMA_INT_REG_MAX]; +- int irq; +- +- struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; +- +- struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; +- struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; +- +- /* descriptor and packet buffers for qdma hw forward */ +- struct { +- void *desc; +- void *q; +- } hfwd; +-}; +- +-struct airoha_gdm_port { +- struct airoha_qdma *qdma; +- struct net_device *dev; +- int id; +- +- struct airoha_hw_stats stats; +- +- DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); +- +- /* qos stats counters */ +- u64 cpu_tx_packets; +- u64 fwd_tx_packets; +-}; +- +-struct airoha_eth { +- struct device *dev; +- +- unsigned long state; +- void __iomem *fe_regs; +- +- struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; +- struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; +- +- struct net_device *napi_dev; +- +- struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; +- struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; +-}; +- + static u32 airoha_rr(void __iomem *base, u32 offset) + { + return readl(base + offset); +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -0,0 +1,251 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#ifndef AIROHA_ETH_H ++#define AIROHA_ETH_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define AIROHA_MAX_NUM_GDM_PORTS 1 ++#define AIROHA_MAX_NUM_QDMA 2 ++#define AIROHA_MAX_NUM_RSTS 3 ++#define AIROHA_MAX_NUM_XSI_RSTS 5 ++#define AIROHA_MAX_MTU 2000 ++#define AIROHA_MAX_PACKET_SIZE 2048 ++#define AIROHA_NUM_QOS_CHANNELS 4 ++#define AIROHA_NUM_QOS_QUEUES 8 ++#define AIROHA_NUM_TX_RING 32 ++#define AIROHA_NUM_RX_RING 32 ++#define AIROHA_NUM_NETDEV_TX_RINGS (AIROHA_NUM_TX_RING + \ ++ AIROHA_NUM_QOS_CHANNELS) ++#define AIROHA_FE_MC_MAX_VLAN_TABLE 64 ++#define AIROHA_FE_MC_MAX_VLAN_PORT 16 ++#define AIROHA_NUM_TX_IRQ 2 ++#define HW_DSCP_NUM 2048 ++#define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) ++#define TX_DSCP_NUM 1024 ++#define RX_DSCP_NUM(_n) \ ++ ((_n) == 2 ? 128 : \ ++ (_n) == 11 ? 128 : \ ++ (_n) == 15 ? 128 : \ ++ (_n) == 0 ? 1024 : 16) ++ ++#define PSE_RSV_PAGES 128 ++#define PSE_QUEUE_RSV_PAGES 64 ++ ++#define QDMA_METER_IDX(_n) ((_n) & 0xff) ++#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) ++ ++enum { ++ QDMA_INT_REG_IDX0, ++ QDMA_INT_REG_IDX1, ++ QDMA_INT_REG_IDX2, ++ QDMA_INT_REG_IDX3, ++ QDMA_INT_REG_IDX4, ++ QDMA_INT_REG_MAX ++}; ++ ++enum { ++ XSI_PCIE0_PORT, ++ XSI_PCIE1_PORT, ++ XSI_USB_PORT, ++ XSI_AE_PORT, ++ XSI_ETH_PORT, ++}; ++ ++enum { ++ XSI_PCIE0_VIP_PORT_MASK = BIT(22), ++ XSI_PCIE1_VIP_PORT_MASK = BIT(23), ++ XSI_USB_VIP_PORT_MASK = BIT(25), ++ XSI_ETH_VIP_PORT_MASK = BIT(24), ++}; ++ ++enum { ++ DEV_STATE_INITIALIZED, ++}; ++ ++enum { ++ CDM_CRSN_QSEL_Q1 = 1, ++ CDM_CRSN_QSEL_Q5 = 5, ++ CDM_CRSN_QSEL_Q6 = 6, ++ CDM_CRSN_QSEL_Q15 = 15, ++}; ++ ++enum { ++ CRSN_08 = 0x8, ++ CRSN_21 = 0x15, /* KA */ ++ CRSN_22 = 0x16, /* hit bind and force route to CPU */ ++ CRSN_24 = 0x18, ++ CRSN_25 = 0x19, ++}; ++ ++enum { ++ FE_PSE_PORT_CDM1, ++ FE_PSE_PORT_GDM1, ++ FE_PSE_PORT_GDM2, ++ FE_PSE_PORT_GDM3, ++ FE_PSE_PORT_PPE1, ++ FE_PSE_PORT_CDM2, ++ FE_PSE_PORT_CDM3, ++ FE_PSE_PORT_CDM4, ++ FE_PSE_PORT_PPE2, ++ FE_PSE_PORT_GDM4, ++ FE_PSE_PORT_CDM5, ++ FE_PSE_PORT_DROP = 0xf, ++}; ++ ++enum tx_sched_mode { ++ TC_SCH_WRR8, ++ TC_SCH_SP, ++ TC_SCH_WRR7, ++ TC_SCH_WRR6, ++ TC_SCH_WRR5, ++ TC_SCH_WRR4, ++ TC_SCH_WRR3, ++ TC_SCH_WRR2, ++}; ++ ++enum trtcm_param_type { ++ TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ ++ TRTCM_TOKEN_RATE_MODE, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, ++ TRTCM_BUCKET_COUNTER_MODE, ++}; ++ ++enum trtcm_mode_type { ++ TRTCM_COMMIT_MODE, ++ TRTCM_PEAK_MODE, ++}; ++ ++enum trtcm_param { ++ TRTCM_TICK_SEL = BIT(0), ++ TRTCM_PKT_MODE = BIT(1), ++ TRTCM_METER_MODE = BIT(2), ++}; ++ ++#define MIN_TOKEN_SIZE 4096 ++#define MAX_TOKEN_SIZE_OFFSET 17 ++#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6) ++#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) ++ ++struct airoha_queue_entry { ++ union { ++ void *buf; ++ struct sk_buff *skb; ++ }; ++ dma_addr_t dma_addr; ++ u16 dma_len; ++}; ++ ++struct airoha_queue { ++ struct airoha_qdma *qdma; ++ ++ /* protect concurrent queue accesses */ ++ spinlock_t lock; ++ struct airoha_queue_entry *entry; ++ struct airoha_qdma_desc *desc; ++ u16 head; ++ u16 tail; ++ ++ int queued; ++ int ndesc; ++ int free_thr; ++ int buf_size; ++ ++ struct napi_struct napi; ++ struct page_pool *page_pool; ++}; ++ ++struct airoha_tx_irq_queue { ++ struct airoha_qdma *qdma; ++ ++ struct napi_struct napi; ++ ++ int size; ++ u32 *q; ++}; ++ ++struct airoha_hw_stats { ++ /* protect concurrent hw_stats accesses */ ++ spinlock_t lock; ++ struct u64_stats_sync syncp; ++ ++ /* get_stats64 */ ++ u64 rx_ok_pkts; ++ u64 tx_ok_pkts; ++ u64 rx_ok_bytes; ++ u64 tx_ok_bytes; ++ u64 rx_multicast; ++ u64 rx_errors; ++ u64 rx_drops; ++ u64 tx_drops; ++ u64 rx_crc_error; ++ u64 rx_over_errors; ++ /* ethtool stats */ ++ u64 tx_broadcast; ++ u64 tx_multicast; ++ u64 tx_len[7]; ++ u64 rx_broadcast; ++ u64 rx_fragment; ++ u64 rx_jabber; ++ u64 rx_len[7]; ++}; ++ ++struct airoha_qdma { ++ struct airoha_eth *eth; ++ void __iomem *regs; ++ ++ /* protect concurrent irqmask accesses */ ++ spinlock_t irq_lock; ++ u32 irqmask[QDMA_INT_REG_MAX]; ++ int irq; ++ ++ struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; ++ ++ struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; ++ struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; ++ ++ /* descriptor and packet buffers for qdma hw forward */ ++ struct { ++ void *desc; ++ void *q; ++ } hfwd; ++}; ++ ++struct airoha_gdm_port { ++ struct airoha_qdma *qdma; ++ struct net_device *dev; ++ int id; ++ ++ struct airoha_hw_stats stats; ++ ++ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); ++ ++ /* qos stats counters */ ++ u64 cpu_tx_packets; ++ u64 fwd_tx_packets; ++}; ++ ++struct airoha_eth { ++ struct device *dev; ++ ++ unsigned long state; ++ void __iomem *fe_regs; ++ ++ struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; ++ struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; ++ ++ struct net_device *napi_dev; ++ ++ struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; ++ struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; ++}; ++ ++#endif /* AIROHA_ETH_H */ diff --git a/target/linux/airoha/patches-6.12/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch b/target/linux/airoha/patches-6.12/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch new file mode 100644 index 00000000000..bf24638ec91 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch @@ -0,0 +1,101 @@ +From e0758a8694fbaffdc72940774db295585e951119 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:11 +0100 +Subject: [PATCH 03/15] net: airoha: Move reg/write utility routines in + airoha_eth.h + +This is a preliminary patch to introduce flowtable hw offloading +support for airoha_eth driver. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 28 +++--------------------- + drivers/net/ethernet/airoha/airoha_eth.h | 26 ++++++++++++++++++++++ + 2 files changed, 29 insertions(+), 25 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -673,17 +673,17 @@ struct airoha_qdma_fwd_desc { + __le32 rsv1; + }; + +-static u32 airoha_rr(void __iomem *base, u32 offset) ++u32 airoha_rr(void __iomem *base, u32 offset) + { + return readl(base + offset); + } + +-static void airoha_wr(void __iomem *base, u32 offset, u32 val) ++void airoha_wr(void __iomem *base, u32 offset, u32 val) + { + writel(val, base + offset); + } + +-static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) ++u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) + { + val |= (airoha_rr(base, offset) & ~mask); + airoha_wr(base, offset, val); +@@ -691,28 +691,6 @@ static u32 airoha_rmw(void __iomem *base + return val; + } + +-#define airoha_fe_rr(eth, offset) \ +- airoha_rr((eth)->fe_regs, (offset)) +-#define airoha_fe_wr(eth, offset, val) \ +- airoha_wr((eth)->fe_regs, (offset), (val)) +-#define airoha_fe_rmw(eth, offset, mask, val) \ +- airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) +-#define airoha_fe_set(eth, offset, val) \ +- airoha_rmw((eth)->fe_regs, (offset), 0, (val)) +-#define airoha_fe_clear(eth, offset, val) \ +- airoha_rmw((eth)->fe_regs, (offset), (val), 0) +- +-#define airoha_qdma_rr(qdma, offset) \ +- airoha_rr((qdma)->regs, (offset)) +-#define airoha_qdma_wr(qdma, offset, val) \ +- airoha_wr((qdma)->regs, (offset), (val)) +-#define airoha_qdma_rmw(qdma, offset, mask, val) \ +- airoha_rmw((qdma)->regs, (offset), (mask), (val)) +-#define airoha_qdma_set(qdma, offset, val) \ +- airoha_rmw((qdma)->regs, (offset), 0, (val)) +-#define airoha_qdma_clear(qdma, offset, val) \ +- airoha_rmw((qdma)->regs, (offset), (val), 0) +- + static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index, + u32 clear, u32 set) + { +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -248,4 +248,30 @@ struct airoha_eth { + struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; + }; + ++u32 airoha_rr(void __iomem *base, u32 offset); ++void airoha_wr(void __iomem *base, u32 offset, u32 val); ++u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val); ++ ++#define airoha_fe_rr(eth, offset) \ ++ airoha_rr((eth)->fe_regs, (offset)) ++#define airoha_fe_wr(eth, offset, val) \ ++ airoha_wr((eth)->fe_regs, (offset), (val)) ++#define airoha_fe_rmw(eth, offset, mask, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) ++#define airoha_fe_set(eth, offset, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), 0, (val)) ++#define airoha_fe_clear(eth, offset, val) \ ++ airoha_rmw((eth)->fe_regs, (offset), (val), 0) ++ ++#define airoha_qdma_rr(qdma, offset) \ ++ airoha_rr((qdma)->regs, (offset)) ++#define airoha_qdma_wr(qdma, offset, val) \ ++ airoha_wr((qdma)->regs, (offset), (val)) ++#define airoha_qdma_rmw(qdma, offset, mask, val) \ ++ airoha_rmw((qdma)->regs, (offset), (mask), (val)) ++#define airoha_qdma_set(qdma, offset, val) \ ++ airoha_rmw((qdma)->regs, (offset), 0, (val)) ++#define airoha_qdma_clear(qdma, offset, val) \ ++ airoha_rmw((qdma)->regs, (offset), (val), 0) ++ + #endif /* AIROHA_ETH_H */ diff --git a/target/linux/airoha/patches-6.12/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch b/target/linux/airoha/patches-6.12/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch new file mode 100644 index 00000000000..3b2b8bfe5dc --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch @@ -0,0 +1,1361 @@ +From ec663d9a82bf4d16721f6b1fc29df4892ba6c088 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:12 +0100 +Subject: [PATCH 04/15] net: airoha: Move register definitions in airoha_regs.h + +Move common airoha_eth register definitions in airoha_regs.h in order +to reuse them for Packet Processor Engine (PPE) codebase. +PPE module is used to enable support for flowtable hw offloading in +airoha_eth driver. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 659 +-------------------- + drivers/net/ethernet/airoha/airoha_regs.h | 670 ++++++++++++++++++++++ + 2 files changed, 671 insertions(+), 658 deletions(-) + create mode 100644 drivers/net/ethernet/airoha/airoha_regs.h + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -13,666 +13,9 @@ + #include + #include + ++#include "airoha_regs.h" + #include "airoha_eth.h" + +-/* FE */ +-#define PSE_BASE 0x0100 +-#define CSR_IFC_BASE 0x0200 +-#define CDM1_BASE 0x0400 +-#define GDM1_BASE 0x0500 +-#define PPE1_BASE 0x0c00 +- +-#define CDM2_BASE 0x1400 +-#define GDM2_BASE 0x1500 +- +-#define GDM3_BASE 0x1100 +-#define GDM4_BASE 0x2500 +- +-#define GDM_BASE(_n) \ +- ((_n) == 4 ? GDM4_BASE : \ +- (_n) == 3 ? GDM3_BASE : \ +- (_n) == 2 ? GDM2_BASE : GDM1_BASE) +- +-#define REG_FE_DMA_GLO_CFG 0x0000 +-#define FE_DMA_GLO_L2_SPACE_MASK GENMASK(7, 4) +-#define FE_DMA_GLO_PG_SZ_MASK BIT(3) +- +-#define REG_FE_RST_GLO_CFG 0x0004 +-#define FE_RST_GDM4_MBI_ARB_MASK BIT(3) +-#define FE_RST_GDM3_MBI_ARB_MASK BIT(2) +-#define FE_RST_CORE_MASK BIT(0) +- +-#define REG_FE_WAN_MAC_H 0x0030 +-#define REG_FE_LAN_MAC_H 0x0040 +- +-#define REG_FE_MAC_LMIN(_n) ((_n) + 0x04) +-#define REG_FE_MAC_LMAX(_n) ((_n) + 0x08) +- +-#define REG_FE_CDM1_OQ_MAP0 0x0050 +-#define REG_FE_CDM1_OQ_MAP1 0x0054 +-#define REG_FE_CDM1_OQ_MAP2 0x0058 +-#define REG_FE_CDM1_OQ_MAP3 0x005c +- +-#define REG_FE_PCE_CFG 0x0070 +-#define PCE_DPI_EN_MASK BIT(2) +-#define PCE_KA_EN_MASK BIT(1) +-#define PCE_MC_EN_MASK BIT(0) +- +-#define REG_FE_PSE_QUEUE_CFG_WR 0x0080 +-#define PSE_CFG_PORT_ID_MASK GENMASK(27, 24) +-#define PSE_CFG_QUEUE_ID_MASK GENMASK(20, 16) +-#define PSE_CFG_WR_EN_MASK BIT(8) +-#define PSE_CFG_OQRSV_SEL_MASK BIT(0) +- +-#define REG_FE_PSE_QUEUE_CFG_VAL 0x0084 +-#define PSE_CFG_OQ_RSV_MASK GENMASK(13, 0) +- +-#define PSE_FQ_CFG 0x008c +-#define PSE_FQ_LIMIT_MASK GENMASK(14, 0) +- +-#define REG_FE_PSE_BUF_SET 0x0090 +-#define PSE_SHARE_USED_LTHD_MASK GENMASK(31, 16) +-#define PSE_ALLRSV_MASK GENMASK(14, 0) +- +-#define REG_PSE_SHARE_USED_THD 0x0094 +-#define PSE_SHARE_USED_MTHD_MASK GENMASK(31, 16) +-#define PSE_SHARE_USED_HTHD_MASK GENMASK(15, 0) +- +-#define REG_GDM_MISC_CFG 0x0148 +-#define GDM2_RDM_ACK_WAIT_PREF_MASK BIT(9) +-#define GDM2_CHN_VLD_MODE_MASK BIT(5) +- +-#define REG_FE_CSR_IFC_CFG CSR_IFC_BASE +-#define FE_IFC_EN_MASK BIT(0) +- +-#define REG_FE_VIP_PORT_EN 0x01f0 +-#define REG_FE_IFC_PORT_EN 0x01f4 +- +-#define REG_PSE_IQ_REV1 (PSE_BASE + 0x08) +-#define PSE_IQ_RES1_P2_MASK GENMASK(23, 16) +- +-#define REG_PSE_IQ_REV2 (PSE_BASE + 0x0c) +-#define PSE_IQ_RES2_P5_MASK GENMASK(15, 8) +-#define PSE_IQ_RES2_P4_MASK GENMASK(7, 0) +- +-#define REG_FE_VIP_EN(_n) (0x0300 + ((_n) << 3)) +-#define PATN_FCPU_EN_MASK BIT(7) +-#define PATN_SWP_EN_MASK BIT(6) +-#define PATN_DP_EN_MASK BIT(5) +-#define PATN_SP_EN_MASK BIT(4) +-#define PATN_TYPE_MASK GENMASK(3, 1) +-#define PATN_EN_MASK BIT(0) +- +-#define REG_FE_VIP_PATN(_n) (0x0304 + ((_n) << 3)) +-#define PATN_DP_MASK GENMASK(31, 16) +-#define PATN_SP_MASK GENMASK(15, 0) +- +-#define REG_CDM1_VLAN_CTRL CDM1_BASE +-#define CDM1_VLAN_MASK GENMASK(31, 16) +- +-#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08) +-#define CDM1_VIP_QSEL_MASK GENMASK(24, 20) +- +-#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2)) +-#define CDM1_CRSN_QSEL_REASON_MASK(_n) \ +- GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) +- +-#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08) +-#define CDM2_OAM_QSEL_MASK GENMASK(31, 27) +-#define CDM2_VIP_QSEL_MASK GENMASK(24, 20) +- +-#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2)) +-#define CDM2_CRSN_QSEL_REASON_MASK(_n) \ +- GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) +- +-#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) +-#define GDM_DROP_CRC_ERR BIT(23) +-#define GDM_IP4_CKSUM BIT(22) +-#define GDM_TCP_CKSUM BIT(21) +-#define GDM_UDP_CKSUM BIT(20) +-#define GDM_UCFQ_MASK GENMASK(15, 12) +-#define GDM_BCFQ_MASK GENMASK(11, 8) +-#define GDM_MCFQ_MASK GENMASK(7, 4) +-#define GDM_OCFQ_MASK GENMASK(3, 0) +- +-#define REG_GDM_INGRESS_CFG(_n) (GDM_BASE(_n) + 0x10) +-#define GDM_INGRESS_FC_EN_MASK BIT(1) +-#define GDM_STAG_EN_MASK BIT(0) +- +-#define REG_GDM_LEN_CFG(_n) (GDM_BASE(_n) + 0x14) +-#define GDM_SHORT_LEN_MASK GENMASK(13, 0) +-#define GDM_LONG_LEN_MASK GENMASK(29, 16) +- +-#define REG_FE_CPORT_CFG (GDM1_BASE + 0x40) +-#define FE_CPORT_PAD BIT(26) +-#define FE_CPORT_PORT_XFC_MASK BIT(25) +-#define FE_CPORT_QUEUE_XFC_MASK BIT(24) +- +-#define REG_FE_GDM_MIB_CLEAR(_n) (GDM_BASE(_n) + 0xf0) +-#define FE_GDM_MIB_RX_CLEAR_MASK BIT(1) +-#define FE_GDM_MIB_TX_CLEAR_MASK BIT(0) +- +-#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4) +-#define FE_STRICT_RFC2819_MODE_MASK BIT(31) +-#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17) +-#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16) +-#define FE_TX_MIB_ID_MASK GENMASK(15, 8) +-#define FE_RX_MIB_ID_MASK GENMASK(7, 0) +- +-#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x104) +-#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x10c) +-#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x110) +-#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x114) +-#define REG_FE_GDM_TX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x118) +-#define REG_FE_GDM_TX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x11c) +-#define REG_FE_GDM_TX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x120) +-#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x124) +-#define REG_FE_GDM_TX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x128) +-#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x12c) +-#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x130) +-#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x134) +-#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x138) +-#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x13c) +-#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x140) +- +-#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x148) +-#define REG_FE_GDM_RX_FC_DROP_CNT(_n) (GDM_BASE(_n) + 0x14c) +-#define REG_FE_GDM_RX_RC_DROP_CNT(_n) (GDM_BASE(_n) + 0x150) +-#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n) (GDM_BASE(_n) + 0x154) +-#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n) (GDM_BASE(_n) + 0x158) +-#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x15c) +-#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x160) +-#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x164) +-#define REG_FE_GDM_RX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x168) +-#define REG_FE_GDM_RX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x16c) +-#define REG_FE_GDM_RX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x170) +-#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n) (GDM_BASE(_n) + 0x174) +-#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n) (GDM_BASE(_n) + 0x178) +-#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n) (GDM_BASE(_n) + 0x17c) +-#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x180) +-#define REG_FE_GDM_RX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x184) +-#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x188) +-#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x18c) +-#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x190) +-#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x194) +-#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) +-#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) +- +-#define REG_PPE1_TB_HASH_CFG (PPE1_BASE + 0x250) +-#define PPE1_SRAM_TABLE_EN_MASK BIT(0) +-#define PPE1_SRAM_HASH1_EN_MASK BIT(8) +-#define PPE1_DRAM_TABLE_EN_MASK BIT(16) +-#define PPE1_DRAM_HASH1_EN_MASK BIT(24) +- +-#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) +-#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) +-#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) +-#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) +- +-#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) +-#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) +-#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) +-#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) +-#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) +-#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) +-#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) +-#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) +-#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) +-#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) +-#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) +-#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) +-#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) +-#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) +-#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) +-#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) +- +-#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) +-#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) +-#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) +- +-#define REG_GDM3_FWD_CFG GDM3_BASE +-#define GDM3_PAD_EN_MASK BIT(28) +- +-#define REG_GDM4_FWD_CFG GDM4_BASE +-#define GDM4_PAD_EN_MASK BIT(28) +-#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8) +- +-#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x23c) +-#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16) +-#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12) +-#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8) +- +-#define REG_IP_FRAG_FP 0x2010 +-#define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21) +-#define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16) +-#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) +-#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) +- +-#define REG_MC_VLAN_EN 0x2100 +-#define MC_VLAN_EN_MASK BIT(0) +- +-#define REG_MC_VLAN_CFG 0x2104 +-#define MC_VLAN_CFG_CMD_DONE_MASK BIT(31) +-#define MC_VLAN_CFG_TABLE_ID_MASK GENMASK(21, 16) +-#define MC_VLAN_CFG_PORT_ID_MASK GENMASK(11, 8) +-#define MC_VLAN_CFG_TABLE_SEL_MASK BIT(4) +-#define MC_VLAN_CFG_RW_MASK BIT(0) +- +-#define REG_MC_VLAN_DATA 0x2108 +- +-#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 +- +-/* QDMA */ +-#define REG_QDMA_GLOBAL_CFG 0x0004 +-#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) +-#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) +-#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) +-#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) +-#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) +-#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) +-#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) +-#define GLOBAL_CFG_RESET_MASK BIT(23) +-#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) +-#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) +-#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) +-#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) +-#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) +-#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) +-#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) +-#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) +-#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) +-#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) +-#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) +-#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) +-#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) +-#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) +-#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) +- +-#define REG_FWD_DSCP_BASE 0x0010 +-#define REG_FWD_BUF_BASE 0x0014 +- +-#define REG_HW_FWD_DSCP_CFG 0x0018 +-#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) +-#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) +-#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) +- +-#define REG_INT_STATUS(_n) \ +- (((_n) == 4) ? 0x0730 : \ +- ((_n) == 3) ? 0x0724 : \ +- ((_n) == 2) ? 0x0720 : \ +- ((_n) == 1) ? 0x0024 : 0x0020) +- +-#define REG_INT_ENABLE(_n) \ +- (((_n) == 4) ? 0x0750 : \ +- ((_n) == 3) ? 0x0744 : \ +- ((_n) == 2) ? 0x0740 : \ +- ((_n) == 1) ? 0x002c : 0x0028) +- +-/* QDMA_CSR_INT_ENABLE1 */ +-#define RX15_COHERENT_INT_MASK BIT(31) +-#define RX14_COHERENT_INT_MASK BIT(30) +-#define RX13_COHERENT_INT_MASK BIT(29) +-#define RX12_COHERENT_INT_MASK BIT(28) +-#define RX11_COHERENT_INT_MASK BIT(27) +-#define RX10_COHERENT_INT_MASK BIT(26) +-#define RX9_COHERENT_INT_MASK BIT(25) +-#define RX8_COHERENT_INT_MASK BIT(24) +-#define RX7_COHERENT_INT_MASK BIT(23) +-#define RX6_COHERENT_INT_MASK BIT(22) +-#define RX5_COHERENT_INT_MASK BIT(21) +-#define RX4_COHERENT_INT_MASK BIT(20) +-#define RX3_COHERENT_INT_MASK BIT(19) +-#define RX2_COHERENT_INT_MASK BIT(18) +-#define RX1_COHERENT_INT_MASK BIT(17) +-#define RX0_COHERENT_INT_MASK BIT(16) +-#define TX7_COHERENT_INT_MASK BIT(15) +-#define TX6_COHERENT_INT_MASK BIT(14) +-#define TX5_COHERENT_INT_MASK BIT(13) +-#define TX4_COHERENT_INT_MASK BIT(12) +-#define TX3_COHERENT_INT_MASK BIT(11) +-#define TX2_COHERENT_INT_MASK BIT(10) +-#define TX1_COHERENT_INT_MASK BIT(9) +-#define TX0_COHERENT_INT_MASK BIT(8) +-#define CNT_OVER_FLOW_INT_MASK BIT(7) +-#define IRQ1_FULL_INT_MASK BIT(5) +-#define IRQ1_INT_MASK BIT(4) +-#define HWFWD_DSCP_LOW_INT_MASK BIT(3) +-#define HWFWD_DSCP_EMPTY_INT_MASK BIT(2) +-#define IRQ0_FULL_INT_MASK BIT(1) +-#define IRQ0_INT_MASK BIT(0) +- +-#define TX_DONE_INT_MASK(_n) \ +- ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ +- : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) +- +-#define INT_TX_MASK \ +- (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ +- IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) +- +-#define INT_IDX0_MASK \ +- (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ +- TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ +- TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ +- TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ +- RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ +- RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ +- RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ +- RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ +- RX15_COHERENT_INT_MASK | INT_TX_MASK) +- +-/* QDMA_CSR_INT_ENABLE2 */ +-#define RX15_NO_CPU_DSCP_INT_MASK BIT(31) +-#define RX14_NO_CPU_DSCP_INT_MASK BIT(30) +-#define RX13_NO_CPU_DSCP_INT_MASK BIT(29) +-#define RX12_NO_CPU_DSCP_INT_MASK BIT(28) +-#define RX11_NO_CPU_DSCP_INT_MASK BIT(27) +-#define RX10_NO_CPU_DSCP_INT_MASK BIT(26) +-#define RX9_NO_CPU_DSCP_INT_MASK BIT(25) +-#define RX8_NO_CPU_DSCP_INT_MASK BIT(24) +-#define RX7_NO_CPU_DSCP_INT_MASK BIT(23) +-#define RX6_NO_CPU_DSCP_INT_MASK BIT(22) +-#define RX5_NO_CPU_DSCP_INT_MASK BIT(21) +-#define RX4_NO_CPU_DSCP_INT_MASK BIT(20) +-#define RX3_NO_CPU_DSCP_INT_MASK BIT(19) +-#define RX2_NO_CPU_DSCP_INT_MASK BIT(18) +-#define RX1_NO_CPU_DSCP_INT_MASK BIT(17) +-#define RX0_NO_CPU_DSCP_INT_MASK BIT(16) +-#define RX15_DONE_INT_MASK BIT(15) +-#define RX14_DONE_INT_MASK BIT(14) +-#define RX13_DONE_INT_MASK BIT(13) +-#define RX12_DONE_INT_MASK BIT(12) +-#define RX11_DONE_INT_MASK BIT(11) +-#define RX10_DONE_INT_MASK BIT(10) +-#define RX9_DONE_INT_MASK BIT(9) +-#define RX8_DONE_INT_MASK BIT(8) +-#define RX7_DONE_INT_MASK BIT(7) +-#define RX6_DONE_INT_MASK BIT(6) +-#define RX5_DONE_INT_MASK BIT(5) +-#define RX4_DONE_INT_MASK BIT(4) +-#define RX3_DONE_INT_MASK BIT(3) +-#define RX2_DONE_INT_MASK BIT(2) +-#define RX1_DONE_INT_MASK BIT(1) +-#define RX0_DONE_INT_MASK BIT(0) +- +-#define RX_DONE_INT_MASK \ +- (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ +- RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ +- RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ +- RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ +- RX15_DONE_INT_MASK) +-#define INT_IDX1_MASK \ +- (RX_DONE_INT_MASK | \ +- RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ +- RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ +- RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ +- RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ +- RX15_NO_CPU_DSCP_INT_MASK) +- +-/* QDMA_CSR_INT_ENABLE5 */ +-#define TX31_COHERENT_INT_MASK BIT(31) +-#define TX30_COHERENT_INT_MASK BIT(30) +-#define TX29_COHERENT_INT_MASK BIT(29) +-#define TX28_COHERENT_INT_MASK BIT(28) +-#define TX27_COHERENT_INT_MASK BIT(27) +-#define TX26_COHERENT_INT_MASK BIT(26) +-#define TX25_COHERENT_INT_MASK BIT(25) +-#define TX24_COHERENT_INT_MASK BIT(24) +-#define TX23_COHERENT_INT_MASK BIT(23) +-#define TX22_COHERENT_INT_MASK BIT(22) +-#define TX21_COHERENT_INT_MASK BIT(21) +-#define TX20_COHERENT_INT_MASK BIT(20) +-#define TX19_COHERENT_INT_MASK BIT(19) +-#define TX18_COHERENT_INT_MASK BIT(18) +-#define TX17_COHERENT_INT_MASK BIT(17) +-#define TX16_COHERENT_INT_MASK BIT(16) +-#define TX15_COHERENT_INT_MASK BIT(15) +-#define TX14_COHERENT_INT_MASK BIT(14) +-#define TX13_COHERENT_INT_MASK BIT(13) +-#define TX12_COHERENT_INT_MASK BIT(12) +-#define TX11_COHERENT_INT_MASK BIT(11) +-#define TX10_COHERENT_INT_MASK BIT(10) +-#define TX9_COHERENT_INT_MASK BIT(9) +-#define TX8_COHERENT_INT_MASK BIT(8) +- +-#define INT_IDX4_MASK \ +- (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ +- TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ +- TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ +- TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ +- TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ +- TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ +- TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ +- TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ +- TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ +- TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ +- TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ +- TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) +- +-#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) +- +-#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) +-#define TX_IRQ_THR_MASK GENMASK(27, 16) +-#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) +- +-#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) +-#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) +- +-#define REG_IRQ_STATUS(_n) ((_n) ? 0x0068 : 0x005c) +-#define IRQ_ENTRY_LEN_MASK GENMASK(27, 16) +-#define IRQ_HEAD_IDX_MASK GENMASK(11, 0) +- +-#define REG_TX_RING_BASE(_n) \ +- (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) +- +-#define REG_TX_RING_BLOCKING(_n) \ +- (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5)) +- +-#define TX_RING_IRQ_BLOCKING_MAP_MASK BIT(6) +-#define TX_RING_IRQ_BLOCKING_CFG_MASK BIT(4) +-#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK BIT(2) +-#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK BIT(1) +-#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK BIT(0) +- +-#define REG_TX_CPU_IDX(_n) \ +- (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) +- +-#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) +- +-#define REG_TX_DMA_IDX(_n) \ +- (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) +- +-#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) +- +-#define IRQ_RING_IDX_MASK GENMASK(20, 16) +-#define IRQ_DESC_IDX_MASK GENMASK(15, 0) +- +-#define REG_RX_RING_BASE(_n) \ +- (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) +- +-#define REG_RX_RING_SIZE(_n) \ +- (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) +- +-#define RX_RING_THR_MASK GENMASK(31, 16) +-#define RX_RING_SIZE_MASK GENMASK(15, 0) +- +-#define REG_RX_CPU_IDX(_n) \ +- (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) +- +-#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) +- +-#define REG_RX_DMA_IDX(_n) \ +- (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) +- +-#define REG_RX_DELAY_INT_IDX(_n) \ +- (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) +- +-#define RX_DELAY_INT_MASK GENMASK(15, 0) +- +-#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) +- +-#define REG_INGRESS_TRTCM_CFG 0x0070 +-#define INGRESS_TRTCM_EN_MASK BIT(31) +-#define INGRESS_TRTCM_MODE_MASK BIT(30) +-#define INGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define INGRESS_FAST_TICK_MASK GENMASK(15, 0) +- +-#define REG_QUEUE_CLOSE_CFG(_n) (0x00a0 + ((_n) & 0xfc)) +-#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) BIT((_m) + (((_n) & 0x3) << 3)) +- +-#define REG_TXQ_DIS_CFG_BASE(_n) ((_n) ? 0x20a0 : 0x00a0) +-#define REG_TXQ_DIS_CFG(_n, _m) (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2) +- +-#define REG_CNTR_CFG(_n) (0x0400 + ((_n) << 3)) +-#define CNTR_EN_MASK BIT(31) +-#define CNTR_ALL_CHAN_EN_MASK BIT(30) +-#define CNTR_ALL_QUEUE_EN_MASK BIT(29) +-#define CNTR_ALL_DSCP_RING_EN_MASK BIT(28) +-#define CNTR_SRC_MASK GENMASK(27, 24) +-#define CNTR_DSCP_RING_MASK GENMASK(20, 16) +-#define CNTR_CHAN_MASK GENMASK(7, 3) +-#define CNTR_QUEUE_MASK GENMASK(2, 0) +- +-#define REG_CNTR_VAL(_n) (0x0404 + ((_n) << 3)) +- +-#define REG_LMGR_INIT_CFG 0x1000 +-#define LMGR_INIT_START BIT(31) +-#define LMGR_SRAM_MODE_MASK BIT(30) +-#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) +-#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) +- +-#define REG_FWD_DSCP_LOW_THR 0x1004 +-#define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) +- +-#define REG_EGRESS_RATE_METER_CFG 0x100c +-#define EGRESS_RATE_METER_EN_MASK BIT(31) +-#define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) +-#define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) +-#define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) +- +-#define REG_EGRESS_TRTCM_CFG 0x1010 +-#define EGRESS_TRTCM_EN_MASK BIT(31) +-#define EGRESS_TRTCM_MODE_MASK BIT(30) +-#define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define EGRESS_FAST_TICK_MASK GENMASK(15, 0) +- +-#define TRTCM_PARAM_RW_MASK BIT(31) +-#define TRTCM_PARAM_RW_DONE_MASK BIT(30) +-#define TRTCM_PARAM_TYPE_MASK GENMASK(29, 28) +-#define TRTCM_METER_GROUP_MASK GENMASK(27, 26) +-#define TRTCM_PARAM_INDEX_MASK GENMASK(23, 17) +-#define TRTCM_PARAM_RATE_TYPE_MASK BIT(16) +- +-#define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4) +-#define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) +-#define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) +- +-#define REG_TXWRR_MODE_CFG 0x1020 +-#define TWRR_WEIGHT_SCALE_MASK BIT(31) +-#define TWRR_WEIGHT_BASE_MASK BIT(3) +- +-#define REG_TXWRR_WEIGHT_CFG 0x1024 +-#define TWRR_RW_CMD_MASK BIT(31) +-#define TWRR_RW_CMD_DONE BIT(30) +-#define TWRR_CHAN_IDX_MASK GENMASK(23, 19) +-#define TWRR_QUEUE_IDX_MASK GENMASK(18, 16) +-#define TWRR_VALUE_MASK GENMASK(15, 0) +- +-#define REG_PSE_BUF_USAGE_CFG 0x1028 +-#define PSE_BUF_ESTIMATE_EN_MASK BIT(29) +- +-#define REG_CHAN_QOS_MODE(_n) (0x1040 + ((_n) << 2)) +-#define CHAN_QOS_MODE_MASK(_n) GENMASK(2 + ((_n) << 2), (_n) << 2) +- +-#define REG_GLB_TRTCM_CFG 0x1080 +-#define GLB_TRTCM_EN_MASK BIT(31) +-#define GLB_TRTCM_MODE_MASK BIT(30) +-#define GLB_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define GLB_FAST_TICK_MASK GENMASK(15, 0) +- +-#define REG_TXQ_CNGST_CFG 0x10a0 +-#define TXQ_CNGST_DROP_EN BIT(31) +-#define TXQ_CNGST_DEI_DROP_EN BIT(30) +- +-#define REG_SLA_TRTCM_CFG 0x1150 +-#define SLA_TRTCM_EN_MASK BIT(31) +-#define SLA_TRTCM_MODE_MASK BIT(30) +-#define SLA_SLOW_TICK_RATIO_MASK GENMASK(29, 16) +-#define SLA_FAST_TICK_MASK GENMASK(15, 0) +- +-/* CTRL */ +-#define QDMA_DESC_DONE_MASK BIT(31) +-#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ +-#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ +-#define QDMA_DESC_DEI_MASK BIT(25) +-#define QDMA_DESC_NO_DROP_MASK BIT(24) +-#define QDMA_DESC_LEN_MASK GENMASK(15, 0) +-/* DATA */ +-#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) +-/* TX MSG0 */ +-#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) +-#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) +-#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) +-#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) +-#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) +-#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) +-#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) +-#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) +-#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) +-#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) +-/* TX MSG1 */ +-#define QDMA_ETH_TXMSG_NO_DROP BIT(31) +-#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ +-#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) +-#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) +-#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) +-#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) +-#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) +-#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ +-#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ +- +-/* RX MSG1 */ +-#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) +-#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) +-#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) +-#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) +-#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) +-#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) +-#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) +-#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) +-#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) +- +-struct airoha_qdma_desc { +- __le32 rsv; +- __le32 ctrl; +- __le32 addr; +- __le32 data; +- __le32 msg0; +- __le32 msg1; +- __le32 msg2; +- __le32 msg3; +-}; +- +-/* CTRL0 */ +-#define QDMA_FWD_DESC_CTX_MASK BIT(31) +-#define QDMA_FWD_DESC_RING_MASK GENMASK(30, 28) +-#define QDMA_FWD_DESC_IDX_MASK GENMASK(27, 16) +-#define QDMA_FWD_DESC_LEN_MASK GENMASK(15, 0) +-/* CTRL1 */ +-#define QDMA_FWD_DESC_FIRST_IDX_MASK GENMASK(15, 0) +-/* CTRL2 */ +-#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK GENMASK(2, 0) +- +-struct airoha_qdma_fwd_desc { +- __le32 addr; +- __le32 ctrl0; +- __le32 ctrl1; +- __le32 ctrl2; +- __le32 msg0; +- __le32 msg1; +- __le32 rsv0; +- __le32 rsv1; +-}; +- + u32 airoha_rr(void __iomem *base, u32 offset) + { + return readl(base + offset); +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -0,0 +1,670 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#ifndef AIROHA_REGS_H ++#define AIROHA_REGS_H ++ ++#include ++ ++/* FE */ ++#define PSE_BASE 0x0100 ++#define CSR_IFC_BASE 0x0200 ++#define CDM1_BASE 0x0400 ++#define GDM1_BASE 0x0500 ++#define PPE1_BASE 0x0c00 ++ ++#define CDM2_BASE 0x1400 ++#define GDM2_BASE 0x1500 ++ ++#define GDM3_BASE 0x1100 ++#define GDM4_BASE 0x2500 ++ ++#define GDM_BASE(_n) \ ++ ((_n) == 4 ? GDM4_BASE : \ ++ (_n) == 3 ? GDM3_BASE : \ ++ (_n) == 2 ? GDM2_BASE : GDM1_BASE) ++ ++#define REG_FE_DMA_GLO_CFG 0x0000 ++#define FE_DMA_GLO_L2_SPACE_MASK GENMASK(7, 4) ++#define FE_DMA_GLO_PG_SZ_MASK BIT(3) ++ ++#define REG_FE_RST_GLO_CFG 0x0004 ++#define FE_RST_GDM4_MBI_ARB_MASK BIT(3) ++#define FE_RST_GDM3_MBI_ARB_MASK BIT(2) ++#define FE_RST_CORE_MASK BIT(0) ++ ++#define REG_FE_WAN_MAC_H 0x0030 ++#define REG_FE_LAN_MAC_H 0x0040 ++ ++#define REG_FE_MAC_LMIN(_n) ((_n) + 0x04) ++#define REG_FE_MAC_LMAX(_n) ((_n) + 0x08) ++ ++#define REG_FE_CDM1_OQ_MAP0 0x0050 ++#define REG_FE_CDM1_OQ_MAP1 0x0054 ++#define REG_FE_CDM1_OQ_MAP2 0x0058 ++#define REG_FE_CDM1_OQ_MAP3 0x005c ++ ++#define REG_FE_PCE_CFG 0x0070 ++#define PCE_DPI_EN_MASK BIT(2) ++#define PCE_KA_EN_MASK BIT(1) ++#define PCE_MC_EN_MASK BIT(0) ++ ++#define REG_FE_PSE_QUEUE_CFG_WR 0x0080 ++#define PSE_CFG_PORT_ID_MASK GENMASK(27, 24) ++#define PSE_CFG_QUEUE_ID_MASK GENMASK(20, 16) ++#define PSE_CFG_WR_EN_MASK BIT(8) ++#define PSE_CFG_OQRSV_SEL_MASK BIT(0) ++ ++#define REG_FE_PSE_QUEUE_CFG_VAL 0x0084 ++#define PSE_CFG_OQ_RSV_MASK GENMASK(13, 0) ++ ++#define PSE_FQ_CFG 0x008c ++#define PSE_FQ_LIMIT_MASK GENMASK(14, 0) ++ ++#define REG_FE_PSE_BUF_SET 0x0090 ++#define PSE_SHARE_USED_LTHD_MASK GENMASK(31, 16) ++#define PSE_ALLRSV_MASK GENMASK(14, 0) ++ ++#define REG_PSE_SHARE_USED_THD 0x0094 ++#define PSE_SHARE_USED_MTHD_MASK GENMASK(31, 16) ++#define PSE_SHARE_USED_HTHD_MASK GENMASK(15, 0) ++ ++#define REG_GDM_MISC_CFG 0x0148 ++#define GDM2_RDM_ACK_WAIT_PREF_MASK BIT(9) ++#define GDM2_CHN_VLD_MODE_MASK BIT(5) ++ ++#define REG_FE_CSR_IFC_CFG CSR_IFC_BASE ++#define FE_IFC_EN_MASK BIT(0) ++ ++#define REG_FE_VIP_PORT_EN 0x01f0 ++#define REG_FE_IFC_PORT_EN 0x01f4 ++ ++#define REG_PSE_IQ_REV1 (PSE_BASE + 0x08) ++#define PSE_IQ_RES1_P2_MASK GENMASK(23, 16) ++ ++#define REG_PSE_IQ_REV2 (PSE_BASE + 0x0c) ++#define PSE_IQ_RES2_P5_MASK GENMASK(15, 8) ++#define PSE_IQ_RES2_P4_MASK GENMASK(7, 0) ++ ++#define REG_FE_VIP_EN(_n) (0x0300 + ((_n) << 3)) ++#define PATN_FCPU_EN_MASK BIT(7) ++#define PATN_SWP_EN_MASK BIT(6) ++#define PATN_DP_EN_MASK BIT(5) ++#define PATN_SP_EN_MASK BIT(4) ++#define PATN_TYPE_MASK GENMASK(3, 1) ++#define PATN_EN_MASK BIT(0) ++ ++#define REG_FE_VIP_PATN(_n) (0x0304 + ((_n) << 3)) ++#define PATN_DP_MASK GENMASK(31, 16) ++#define PATN_SP_MASK GENMASK(15, 0) ++ ++#define REG_CDM1_VLAN_CTRL CDM1_BASE ++#define CDM1_VLAN_MASK GENMASK(31, 16) ++ ++#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08) ++#define CDM1_VIP_QSEL_MASK GENMASK(24, 20) ++ ++#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2)) ++#define CDM1_CRSN_QSEL_REASON_MASK(_n) \ ++ GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) ++ ++#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08) ++#define CDM2_OAM_QSEL_MASK GENMASK(31, 27) ++#define CDM2_VIP_QSEL_MASK GENMASK(24, 20) ++ ++#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2)) ++#define CDM2_CRSN_QSEL_REASON_MASK(_n) \ ++ GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) ++ ++#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) ++#define GDM_DROP_CRC_ERR BIT(23) ++#define GDM_IP4_CKSUM BIT(22) ++#define GDM_TCP_CKSUM BIT(21) ++#define GDM_UDP_CKSUM BIT(20) ++#define GDM_UCFQ_MASK GENMASK(15, 12) ++#define GDM_BCFQ_MASK GENMASK(11, 8) ++#define GDM_MCFQ_MASK GENMASK(7, 4) ++#define GDM_OCFQ_MASK GENMASK(3, 0) ++ ++#define REG_GDM_INGRESS_CFG(_n) (GDM_BASE(_n) + 0x10) ++#define GDM_INGRESS_FC_EN_MASK BIT(1) ++#define GDM_STAG_EN_MASK BIT(0) ++ ++#define REG_GDM_LEN_CFG(_n) (GDM_BASE(_n) + 0x14) ++#define GDM_SHORT_LEN_MASK GENMASK(13, 0) ++#define GDM_LONG_LEN_MASK GENMASK(29, 16) ++ ++#define REG_FE_CPORT_CFG (GDM1_BASE + 0x40) ++#define FE_CPORT_PAD BIT(26) ++#define FE_CPORT_PORT_XFC_MASK BIT(25) ++#define FE_CPORT_QUEUE_XFC_MASK BIT(24) ++ ++#define REG_FE_GDM_MIB_CLEAR(_n) (GDM_BASE(_n) + 0xf0) ++#define FE_GDM_MIB_RX_CLEAR_MASK BIT(1) ++#define FE_GDM_MIB_TX_CLEAR_MASK BIT(0) ++ ++#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4) ++#define FE_STRICT_RFC2819_MODE_MASK BIT(31) ++#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17) ++#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16) ++#define FE_TX_MIB_ID_MASK GENMASK(15, 8) ++#define FE_RX_MIB_ID_MASK GENMASK(7, 0) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x104) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x10c) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x110) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x114) ++#define REG_FE_GDM_TX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x118) ++#define REG_FE_GDM_TX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x11c) ++#define REG_FE_GDM_TX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x120) ++#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x124) ++#define REG_FE_GDM_TX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x128) ++#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x12c) ++#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x130) ++#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x134) ++#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x138) ++#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x13c) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x140) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x148) ++#define REG_FE_GDM_RX_FC_DROP_CNT(_n) (GDM_BASE(_n) + 0x14c) ++#define REG_FE_GDM_RX_RC_DROP_CNT(_n) (GDM_BASE(_n) + 0x150) ++#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n) (GDM_BASE(_n) + 0x154) ++#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n) (GDM_BASE(_n) + 0x158) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x15c) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n) (GDM_BASE(_n) + 0x160) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n) (GDM_BASE(_n) + 0x164) ++#define REG_FE_GDM_RX_ETH_DROP_CNT(_n) (GDM_BASE(_n) + 0x168) ++#define REG_FE_GDM_RX_ETH_BC_CNT(_n) (GDM_BASE(_n) + 0x16c) ++#define REG_FE_GDM_RX_ETH_MC_CNT(_n) (GDM_BASE(_n) + 0x170) ++#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n) (GDM_BASE(_n) + 0x174) ++#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n) (GDM_BASE(_n) + 0x178) ++#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n) (GDM_BASE(_n) + 0x17c) ++#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n) (GDM_BASE(_n) + 0x180) ++#define REG_FE_GDM_RX_ETH_LONG_CNT(_n) (GDM_BASE(_n) + 0x184) ++#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n) (GDM_BASE(_n) + 0x188) ++#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n) (GDM_BASE(_n) + 0x18c) ++#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n) (GDM_BASE(_n) + 0x190) ++#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n) (GDM_BASE(_n) + 0x194) ++#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) ++ ++#define REG_PPE1_TB_HASH_CFG (PPE1_BASE + 0x250) ++#define PPE1_SRAM_TABLE_EN_MASK BIT(0) ++#define PPE1_SRAM_HASH1_EN_MASK BIT(8) ++#define PPE1_DRAM_TABLE_EN_MASK BIT(16) ++#define PPE1_DRAM_HASH1_EN_MASK BIT(24) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) ++#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) ++#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) ++#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) ++#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) ++#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) ++#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) ++#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) ++#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) ++#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) ++#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) ++ ++#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) ++#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) ++#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) ++ ++#define REG_GDM3_FWD_CFG GDM3_BASE ++#define GDM3_PAD_EN_MASK BIT(28) ++ ++#define REG_GDM4_FWD_CFG GDM4_BASE ++#define GDM4_PAD_EN_MASK BIT(28) ++#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8) ++ ++#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x23c) ++#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16) ++#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12) ++#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8) ++ ++#define REG_IP_FRAG_FP 0x2010 ++#define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21) ++#define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16) ++#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) ++#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) ++ ++#define REG_MC_VLAN_EN 0x2100 ++#define MC_VLAN_EN_MASK BIT(0) ++ ++#define REG_MC_VLAN_CFG 0x2104 ++#define MC_VLAN_CFG_CMD_DONE_MASK BIT(31) ++#define MC_VLAN_CFG_TABLE_ID_MASK GENMASK(21, 16) ++#define MC_VLAN_CFG_PORT_ID_MASK GENMASK(11, 8) ++#define MC_VLAN_CFG_TABLE_SEL_MASK BIT(4) ++#define MC_VLAN_CFG_RW_MASK BIT(0) ++ ++#define REG_MC_VLAN_DATA 0x2108 ++ ++#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 ++ ++/* QDMA */ ++#define REG_QDMA_GLOBAL_CFG 0x0004 ++#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) ++#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) ++#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) ++#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) ++#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) ++#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) ++#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) ++#define GLOBAL_CFG_RESET_MASK BIT(23) ++#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) ++#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) ++#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) ++#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) ++#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) ++#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) ++#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) ++#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) ++#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) ++#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) ++#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) ++#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) ++#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) ++#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) ++#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) ++ ++#define REG_FWD_DSCP_BASE 0x0010 ++#define REG_FWD_BUF_BASE 0x0014 ++ ++#define REG_HW_FWD_DSCP_CFG 0x0018 ++#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) ++#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) ++#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) ++ ++#define REG_INT_STATUS(_n) \ ++ (((_n) == 4) ? 0x0730 : \ ++ ((_n) == 3) ? 0x0724 : \ ++ ((_n) == 2) ? 0x0720 : \ ++ ((_n) == 1) ? 0x0024 : 0x0020) ++ ++#define REG_INT_ENABLE(_n) \ ++ (((_n) == 4) ? 0x0750 : \ ++ ((_n) == 3) ? 0x0744 : \ ++ ((_n) == 2) ? 0x0740 : \ ++ ((_n) == 1) ? 0x002c : 0x0028) ++ ++/* QDMA_CSR_INT_ENABLE1 */ ++#define RX15_COHERENT_INT_MASK BIT(31) ++#define RX14_COHERENT_INT_MASK BIT(30) ++#define RX13_COHERENT_INT_MASK BIT(29) ++#define RX12_COHERENT_INT_MASK BIT(28) ++#define RX11_COHERENT_INT_MASK BIT(27) ++#define RX10_COHERENT_INT_MASK BIT(26) ++#define RX9_COHERENT_INT_MASK BIT(25) ++#define RX8_COHERENT_INT_MASK BIT(24) ++#define RX7_COHERENT_INT_MASK BIT(23) ++#define RX6_COHERENT_INT_MASK BIT(22) ++#define RX5_COHERENT_INT_MASK BIT(21) ++#define RX4_COHERENT_INT_MASK BIT(20) ++#define RX3_COHERENT_INT_MASK BIT(19) ++#define RX2_COHERENT_INT_MASK BIT(18) ++#define RX1_COHERENT_INT_MASK BIT(17) ++#define RX0_COHERENT_INT_MASK BIT(16) ++#define TX7_COHERENT_INT_MASK BIT(15) ++#define TX6_COHERENT_INT_MASK BIT(14) ++#define TX5_COHERENT_INT_MASK BIT(13) ++#define TX4_COHERENT_INT_MASK BIT(12) ++#define TX3_COHERENT_INT_MASK BIT(11) ++#define TX2_COHERENT_INT_MASK BIT(10) ++#define TX1_COHERENT_INT_MASK BIT(9) ++#define TX0_COHERENT_INT_MASK BIT(8) ++#define CNT_OVER_FLOW_INT_MASK BIT(7) ++#define IRQ1_FULL_INT_MASK BIT(5) ++#define IRQ1_INT_MASK BIT(4) ++#define HWFWD_DSCP_LOW_INT_MASK BIT(3) ++#define HWFWD_DSCP_EMPTY_INT_MASK BIT(2) ++#define IRQ0_FULL_INT_MASK BIT(1) ++#define IRQ0_INT_MASK BIT(0) ++ ++#define TX_DONE_INT_MASK(_n) \ ++ ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ ++ : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) ++ ++#define INT_TX_MASK \ ++ (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ ++ IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) ++ ++#define INT_IDX0_MASK \ ++ (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ ++ TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ ++ TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ ++ TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ ++ RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ ++ RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ ++ RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ ++ RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ ++ RX15_COHERENT_INT_MASK | INT_TX_MASK) ++ ++/* QDMA_CSR_INT_ENABLE2 */ ++#define RX15_NO_CPU_DSCP_INT_MASK BIT(31) ++#define RX14_NO_CPU_DSCP_INT_MASK BIT(30) ++#define RX13_NO_CPU_DSCP_INT_MASK BIT(29) ++#define RX12_NO_CPU_DSCP_INT_MASK BIT(28) ++#define RX11_NO_CPU_DSCP_INT_MASK BIT(27) ++#define RX10_NO_CPU_DSCP_INT_MASK BIT(26) ++#define RX9_NO_CPU_DSCP_INT_MASK BIT(25) ++#define RX8_NO_CPU_DSCP_INT_MASK BIT(24) ++#define RX7_NO_CPU_DSCP_INT_MASK BIT(23) ++#define RX6_NO_CPU_DSCP_INT_MASK BIT(22) ++#define RX5_NO_CPU_DSCP_INT_MASK BIT(21) ++#define RX4_NO_CPU_DSCP_INT_MASK BIT(20) ++#define RX3_NO_CPU_DSCP_INT_MASK BIT(19) ++#define RX2_NO_CPU_DSCP_INT_MASK BIT(18) ++#define RX1_NO_CPU_DSCP_INT_MASK BIT(17) ++#define RX0_NO_CPU_DSCP_INT_MASK BIT(16) ++#define RX15_DONE_INT_MASK BIT(15) ++#define RX14_DONE_INT_MASK BIT(14) ++#define RX13_DONE_INT_MASK BIT(13) ++#define RX12_DONE_INT_MASK BIT(12) ++#define RX11_DONE_INT_MASK BIT(11) ++#define RX10_DONE_INT_MASK BIT(10) ++#define RX9_DONE_INT_MASK BIT(9) ++#define RX8_DONE_INT_MASK BIT(8) ++#define RX7_DONE_INT_MASK BIT(7) ++#define RX6_DONE_INT_MASK BIT(6) ++#define RX5_DONE_INT_MASK BIT(5) ++#define RX4_DONE_INT_MASK BIT(4) ++#define RX3_DONE_INT_MASK BIT(3) ++#define RX2_DONE_INT_MASK BIT(2) ++#define RX1_DONE_INT_MASK BIT(1) ++#define RX0_DONE_INT_MASK BIT(0) ++ ++#define RX_DONE_INT_MASK \ ++ (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ ++ RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ ++ RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ ++ RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ ++ RX15_DONE_INT_MASK) ++#define INT_IDX1_MASK \ ++ (RX_DONE_INT_MASK | \ ++ RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ ++ RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ ++ RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ ++ RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ ++ RX15_NO_CPU_DSCP_INT_MASK) ++ ++/* QDMA_CSR_INT_ENABLE5 */ ++#define TX31_COHERENT_INT_MASK BIT(31) ++#define TX30_COHERENT_INT_MASK BIT(30) ++#define TX29_COHERENT_INT_MASK BIT(29) ++#define TX28_COHERENT_INT_MASK BIT(28) ++#define TX27_COHERENT_INT_MASK BIT(27) ++#define TX26_COHERENT_INT_MASK BIT(26) ++#define TX25_COHERENT_INT_MASK BIT(25) ++#define TX24_COHERENT_INT_MASK BIT(24) ++#define TX23_COHERENT_INT_MASK BIT(23) ++#define TX22_COHERENT_INT_MASK BIT(22) ++#define TX21_COHERENT_INT_MASK BIT(21) ++#define TX20_COHERENT_INT_MASK BIT(20) ++#define TX19_COHERENT_INT_MASK BIT(19) ++#define TX18_COHERENT_INT_MASK BIT(18) ++#define TX17_COHERENT_INT_MASK BIT(17) ++#define TX16_COHERENT_INT_MASK BIT(16) ++#define TX15_COHERENT_INT_MASK BIT(15) ++#define TX14_COHERENT_INT_MASK BIT(14) ++#define TX13_COHERENT_INT_MASK BIT(13) ++#define TX12_COHERENT_INT_MASK BIT(12) ++#define TX11_COHERENT_INT_MASK BIT(11) ++#define TX10_COHERENT_INT_MASK BIT(10) ++#define TX9_COHERENT_INT_MASK BIT(9) ++#define TX8_COHERENT_INT_MASK BIT(8) ++ ++#define INT_IDX4_MASK \ ++ (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ ++ TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ ++ TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ ++ TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ ++ TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ ++ TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ ++ TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ ++ TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ ++ TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ ++ TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ ++ TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ ++ TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) ++ ++#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) ++ ++#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) ++#define TX_IRQ_THR_MASK GENMASK(27, 16) ++#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) ++ ++#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) ++#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) ++ ++#define REG_IRQ_STATUS(_n) ((_n) ? 0x0068 : 0x005c) ++#define IRQ_ENTRY_LEN_MASK GENMASK(27, 16) ++#define IRQ_HEAD_IDX_MASK GENMASK(11, 0) ++ ++#define REG_TX_RING_BASE(_n) \ ++ (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) ++ ++#define REG_TX_RING_BLOCKING(_n) \ ++ (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5)) ++ ++#define TX_RING_IRQ_BLOCKING_MAP_MASK BIT(6) ++#define TX_RING_IRQ_BLOCKING_CFG_MASK BIT(4) ++#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK BIT(2) ++#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK BIT(1) ++#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK BIT(0) ++ ++#define REG_TX_CPU_IDX(_n) \ ++ (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) ++ ++#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) ++ ++#define REG_TX_DMA_IDX(_n) \ ++ (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) ++ ++#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) ++ ++#define IRQ_RING_IDX_MASK GENMASK(20, 16) ++#define IRQ_DESC_IDX_MASK GENMASK(15, 0) ++ ++#define REG_RX_RING_BASE(_n) \ ++ (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) ++ ++#define REG_RX_RING_SIZE(_n) \ ++ (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) ++ ++#define RX_RING_THR_MASK GENMASK(31, 16) ++#define RX_RING_SIZE_MASK GENMASK(15, 0) ++ ++#define REG_RX_CPU_IDX(_n) \ ++ (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) ++ ++#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) ++ ++#define REG_RX_DMA_IDX(_n) \ ++ (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) ++ ++#define REG_RX_DELAY_INT_IDX(_n) \ ++ (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) ++ ++#define RX_DELAY_INT_MASK GENMASK(15, 0) ++ ++#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) ++ ++#define REG_INGRESS_TRTCM_CFG 0x0070 ++#define INGRESS_TRTCM_EN_MASK BIT(31) ++#define INGRESS_TRTCM_MODE_MASK BIT(30) ++#define INGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define INGRESS_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_QUEUE_CLOSE_CFG(_n) (0x00a0 + ((_n) & 0xfc)) ++#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m) BIT((_m) + (((_n) & 0x3) << 3)) ++ ++#define REG_TXQ_DIS_CFG_BASE(_n) ((_n) ? 0x20a0 : 0x00a0) ++#define REG_TXQ_DIS_CFG(_n, _m) (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2) ++ ++#define REG_CNTR_CFG(_n) (0x0400 + ((_n) << 3)) ++#define CNTR_EN_MASK BIT(31) ++#define CNTR_ALL_CHAN_EN_MASK BIT(30) ++#define CNTR_ALL_QUEUE_EN_MASK BIT(29) ++#define CNTR_ALL_DSCP_RING_EN_MASK BIT(28) ++#define CNTR_SRC_MASK GENMASK(27, 24) ++#define CNTR_DSCP_RING_MASK GENMASK(20, 16) ++#define CNTR_CHAN_MASK GENMASK(7, 3) ++#define CNTR_QUEUE_MASK GENMASK(2, 0) ++ ++#define REG_CNTR_VAL(_n) (0x0404 + ((_n) << 3)) ++ ++#define REG_LMGR_INIT_CFG 0x1000 ++#define LMGR_INIT_START BIT(31) ++#define LMGR_SRAM_MODE_MASK BIT(30) ++#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) ++#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) ++ ++#define REG_FWD_DSCP_LOW_THR 0x1004 ++#define FWD_DSCP_LOW_THR_MASK GENMASK(17, 0) ++ ++#define REG_EGRESS_RATE_METER_CFG 0x100c ++#define EGRESS_RATE_METER_EN_MASK BIT(31) ++#define EGRESS_RATE_METER_EQ_RATE_EN_MASK BIT(17) ++#define EGRESS_RATE_METER_WINDOW_SZ_MASK GENMASK(16, 12) ++#define EGRESS_RATE_METER_TIMESLICE_MASK GENMASK(10, 0) ++ ++#define REG_EGRESS_TRTCM_CFG 0x1010 ++#define EGRESS_TRTCM_EN_MASK BIT(31) ++#define EGRESS_TRTCM_MODE_MASK BIT(30) ++#define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define EGRESS_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define TRTCM_PARAM_RW_MASK BIT(31) ++#define TRTCM_PARAM_RW_DONE_MASK BIT(30) ++#define TRTCM_PARAM_TYPE_MASK GENMASK(29, 28) ++#define TRTCM_METER_GROUP_MASK GENMASK(27, 26) ++#define TRTCM_PARAM_INDEX_MASK GENMASK(23, 17) ++#define TRTCM_PARAM_RATE_TYPE_MASK BIT(16) ++ ++#define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4) ++#define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) ++#define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) ++ ++#define REG_TXWRR_MODE_CFG 0x1020 ++#define TWRR_WEIGHT_SCALE_MASK BIT(31) ++#define TWRR_WEIGHT_BASE_MASK BIT(3) ++ ++#define REG_TXWRR_WEIGHT_CFG 0x1024 ++#define TWRR_RW_CMD_MASK BIT(31) ++#define TWRR_RW_CMD_DONE BIT(30) ++#define TWRR_CHAN_IDX_MASK GENMASK(23, 19) ++#define TWRR_QUEUE_IDX_MASK GENMASK(18, 16) ++#define TWRR_VALUE_MASK GENMASK(15, 0) ++ ++#define REG_PSE_BUF_USAGE_CFG 0x1028 ++#define PSE_BUF_ESTIMATE_EN_MASK BIT(29) ++ ++#define REG_CHAN_QOS_MODE(_n) (0x1040 + ((_n) << 2)) ++#define CHAN_QOS_MODE_MASK(_n) GENMASK(2 + ((_n) << 2), (_n) << 2) ++ ++#define REG_GLB_TRTCM_CFG 0x1080 ++#define GLB_TRTCM_EN_MASK BIT(31) ++#define GLB_TRTCM_MODE_MASK BIT(30) ++#define GLB_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define GLB_FAST_TICK_MASK GENMASK(15, 0) ++ ++#define REG_TXQ_CNGST_CFG 0x10a0 ++#define TXQ_CNGST_DROP_EN BIT(31) ++#define TXQ_CNGST_DEI_DROP_EN BIT(30) ++ ++#define REG_SLA_TRTCM_CFG 0x1150 ++#define SLA_TRTCM_EN_MASK BIT(31) ++#define SLA_TRTCM_MODE_MASK BIT(30) ++#define SLA_SLOW_TICK_RATIO_MASK GENMASK(29, 16) ++#define SLA_FAST_TICK_MASK GENMASK(15, 0) ++ ++/* CTRL */ ++#define QDMA_DESC_DONE_MASK BIT(31) ++#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ ++#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ ++#define QDMA_DESC_DEI_MASK BIT(25) ++#define QDMA_DESC_NO_DROP_MASK BIT(24) ++#define QDMA_DESC_LEN_MASK GENMASK(15, 0) ++/* DATA */ ++#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) ++/* TX MSG0 */ ++#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) ++#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) ++#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) ++#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) ++#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) ++#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) ++#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) ++#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) ++#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) ++#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) ++/* TX MSG1 */ ++#define QDMA_ETH_TXMSG_NO_DROP BIT(31) ++#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ ++#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) ++#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) ++#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) ++#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) ++#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) ++#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ ++#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ ++ ++/* RX MSG1 */ ++#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) ++#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) ++#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) ++#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) ++#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) ++#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) ++#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) ++#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) ++#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) ++ ++struct airoha_qdma_desc { ++ __le32 rsv; ++ __le32 ctrl; ++ __le32 addr; ++ __le32 data; ++ __le32 msg0; ++ __le32 msg1; ++ __le32 msg2; ++ __le32 msg3; ++}; ++ ++/* CTRL0 */ ++#define QDMA_FWD_DESC_CTX_MASK BIT(31) ++#define QDMA_FWD_DESC_RING_MASK GENMASK(30, 28) ++#define QDMA_FWD_DESC_IDX_MASK GENMASK(27, 16) ++#define QDMA_FWD_DESC_LEN_MASK GENMASK(15, 0) ++/* CTRL1 */ ++#define QDMA_FWD_DESC_FIRST_IDX_MASK GENMASK(15, 0) ++/* CTRL2 */ ++#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK GENMASK(2, 0) ++ ++struct airoha_qdma_fwd_desc { ++ __le32 addr; ++ __le32 ctrl0; ++ __le32 ctrl1; ++ __le32 ctrl2; ++ __le32 msg0; ++ __le32 msg1; ++ __le32 rsv0; ++ __le32 rsv1; ++}; ++ ++#endif /* AIROHA_REGS_H */ diff --git a/target/linux/airoha/patches-6.12/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch b/target/linux/airoha/patches-6.12/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch new file mode 100644 index 00000000000..f9d6373fc95 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch @@ -0,0 +1,287 @@ +From af3cf757d5c99011b9b94ea8d78aeaccc0153fdc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:13 +0100 +Subject: [PATCH 05/15] net: airoha: Move DSA tag in DMA descriptor + +Packet Processor Engine (PPE) module reads DSA tags from the DMA descriptor +and requires untagged DSA packets to properly parse them. Move DSA tag +in the DMA descriptor on TX side and read DSA tag from DMA descriptor +on RX side. In order to avoid skb reallocation, store tag in skb_dst on +RX side. +This is a preliminary patch to enable netfilter flowtable hw offloading +on EN7581 SoC. + +Tested-by: Sayantan Nandy +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 125 ++++++++++++++++++++-- + drivers/net/ethernet/airoha/airoha_eth.h | 7 ++ + drivers/net/ethernet/airoha/airoha_regs.h | 2 + + 3 files changed, 128 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -656,6 +657,7 @@ static int airoha_qdma_rx_process(struct + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + dma_addr_t dma_addr = le32_to_cpu(desc->addr); + u32 desc_ctrl = le32_to_cpu(desc->ctrl); ++ struct airoha_gdm_port *port; + struct sk_buff *skb; + int len, p; + +@@ -683,6 +685,7 @@ static int airoha_qdma_rx_process(struct + continue; + } + ++ port = eth->ports[p]; + skb = napi_build_skb(e->buf, q->buf_size); + if (!skb) { + page_pool_put_full_page(q->page_pool, +@@ -694,10 +697,26 @@ static int airoha_qdma_rx_process(struct + skb_reserve(skb, 2); + __skb_put(skb, len); + skb_mark_for_recycle(skb); +- skb->dev = eth->ports[p]->dev; ++ skb->dev = port->dev; + skb->protocol = eth_type_trans(skb, skb->dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_record_rx_queue(skb, qid); ++ ++ if (netdev_uses_dsa(port->dev)) { ++ /* PPE module requires untagged packets to work ++ * properly and it provides DSA port index via the ++ * DMA descriptor. Report DSA tag to the DSA stack ++ * via skb dst info. ++ */ ++ u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG, ++ le32_to_cpu(desc->msg0)); ++ ++ if (sptag < ARRAY_SIZE(port->dsa_meta) && ++ port->dsa_meta[sptag]) ++ skb_dst_set_noref(skb, ++ &port->dsa_meta[sptag]->dst); ++ } ++ + napi_gro_receive(&q->napi, skb); + + done++; +@@ -1657,25 +1676,76 @@ static u16 airoha_dev_select_queue(struc + return queue < dev->num_tx_queues ? queue : 0; + } + ++static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev) ++{ ++#if IS_ENABLED(CONFIG_NET_DSA) ++ struct ethhdr *ehdr; ++ struct dsa_port *dp; ++ u8 xmit_tpid; ++ u16 tag; ++ ++ if (!netdev_uses_dsa(dev)) ++ return 0; ++ ++ dp = dev->dsa_ptr; ++ if (IS_ERR(dp)) ++ return 0; ++ ++ if (dp->tag_ops->proto != DSA_TAG_PROTO_MTK) ++ return 0; ++ ++ if (skb_cow_head(skb, 0)) ++ return 0; ++ ++ ehdr = (struct ethhdr *)skb->data; ++ tag = be16_to_cpu(ehdr->h_proto); ++ xmit_tpid = tag >> 8; ++ ++ switch (xmit_tpid) { ++ case MTK_HDR_XMIT_TAGGED_TPID_8100: ++ ehdr->h_proto = cpu_to_be16(ETH_P_8021Q); ++ tag &= ~(MTK_HDR_XMIT_TAGGED_TPID_8100 << 8); ++ break; ++ case MTK_HDR_XMIT_TAGGED_TPID_88A8: ++ ehdr->h_proto = cpu_to_be16(ETH_P_8021AD); ++ tag &= ~(MTK_HDR_XMIT_TAGGED_TPID_88A8 << 8); ++ break; ++ default: ++ /* PPE module requires untagged DSA packets to work properly, ++ * so move DSA tag to DMA descriptor. ++ */ ++ memmove(skb->data + MTK_HDR_LEN, skb->data, 2 * ETH_ALEN); ++ __skb_pull(skb, MTK_HDR_LEN); ++ break; ++ } ++ ++ return tag; ++#else ++ return 0; ++#endif ++} ++ + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, + struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags; +- u32 msg0, msg1, len = skb_headlen(skb); + struct airoha_qdma *qdma = port->qdma; ++ u32 nr_frags, tag, msg0, msg1, len; + struct netdev_queue *txq; + struct airoha_queue *q; +- void *data = skb->data; ++ void *data; + int i, qid; + u16 index; + u8 fport; + + qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); ++ tag = airoha_get_dsa_tag(skb, dev); ++ + msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, + qid / AIROHA_NUM_QOS_QUEUES) | + FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK, +- qid % AIROHA_NUM_QOS_QUEUES); ++ qid % AIROHA_NUM_QOS_QUEUES) | ++ FIELD_PREP(QDMA_ETH_TXMSG_SP_TAG_MASK, tag); + if (skb->ip_summed == CHECKSUM_PARTIAL) + msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) | + FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) | +@@ -1706,6 +1776,8 @@ static netdev_tx_t airoha_dev_xmit(struc + spin_lock_bh(&q->lock); + + txq = netdev_get_tx_queue(dev, qid); ++ nr_frags = 1 + skb_shinfo(skb)->nr_frags; ++ + if (q->queued + nr_frags > q->ndesc) { + /* not enough space in the queue */ + netif_tx_stop_queue(txq); +@@ -1713,7 +1785,10 @@ static netdev_tx_t airoha_dev_xmit(struc + return NETDEV_TX_BUSY; + } + ++ len = skb_headlen(skb); ++ data = skb->data; + index = q->head; ++ + for (i = 0; i < nr_frags; i++) { + struct airoha_qdma_desc *desc = &q->desc[index]; + struct airoha_queue_entry *e = &q->entry[index]; +@@ -2244,6 +2319,37 @@ static const struct ethtool_ops airoha_e + .get_rmon_stats = airoha_ethtool_get_rmon_stats, + }; + ++static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(port->dsa_meta); i++) { ++ struct metadata_dst *md_dst; ++ ++ md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, ++ GFP_KERNEL); ++ if (!md_dst) ++ return -ENOMEM; ++ ++ md_dst->u.port_info.port_id = i; ++ port->dsa_meta[i] = md_dst; ++ } ++ ++ return 0; ++} ++ ++static void airoha_metadata_dst_free(struct airoha_gdm_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(port->dsa_meta); i++) { ++ if (!port->dsa_meta[i]) ++ continue; ++ ++ metadata_dst_free(port->dsa_meta[i]); ++ } ++} ++ + static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np) + { + const __be32 *id_ptr = of_get_property(np, "reg", NULL); +@@ -2316,6 +2422,10 @@ static int airoha_alloc_gdm_port(struct + port->id = id; + eth->ports[index] = port; + ++ err = airoha_metadata_dst_alloc(port); ++ if (err) ++ return err; ++ + return register_netdev(dev); + } + +@@ -2408,8 +2518,10 @@ error_hw_cleanup: + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + +- if (port && port->dev->reg_state == NETREG_REGISTERED) ++ if (port && port->dev->reg_state == NETREG_REGISTERED) { + unregister_netdev(port->dev); ++ airoha_metadata_dst_free(port); ++ } + } + free_netdev(eth->napi_dev); + platform_set_drvdata(pdev, NULL); +@@ -2434,6 +2546,7 @@ static void airoha_remove(struct platfor + continue; + + unregister_netdev(port->dev); ++ airoha_metadata_dst_free(port); + } + free_netdev(eth->napi_dev); + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -15,6 +15,7 @@ + + #define AIROHA_MAX_NUM_GDM_PORTS 1 + #define AIROHA_MAX_NUM_QDMA 2 ++#define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 + #define AIROHA_MAX_MTU 2000 +@@ -43,6 +44,10 @@ + #define QDMA_METER_IDX(_n) ((_n) & 0xff) + #define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) + ++#define MTK_HDR_LEN 4 ++#define MTK_HDR_XMIT_TAGGED_TPID_8100 1 ++#define MTK_HDR_XMIT_TAGGED_TPID_88A8 2 ++ + enum { + QDMA_INT_REG_IDX0, + QDMA_INT_REG_IDX1, +@@ -231,6 +236,8 @@ struct airoha_gdm_port { + /* qos stats counters */ + u64 cpu_tx_packets; + u64 fwd_tx_packets; ++ ++ struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS]; + }; + + struct airoha_eth { +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -624,6 +624,8 @@ + #define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ + #define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ + ++/* RX MSG0 */ ++#define QDMA_ETH_RXMSG_SPTAG GENMASK(21, 14) + /* RX MSG1 */ + #define QDMA_ETH_RXMSG_DEI_MASK BIT(31) + #define QDMA_ETH_RXMSG_IP6_MASK BIT(30) diff --git a/target/linux/airoha/patches-6.12/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.12/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch new file mode 100644 index 00000000000..71822d69d9d --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch @@ -0,0 +1,46 @@ +From ab667db1e6014634c6607ebdddc16c1b8394a935 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:14 +0100 +Subject: [PATCH 06/15] net: dsa: mt7530: Enable Rx sptag for EN7581 SoC + +Packet Processor Engine (PPE) module used for hw acceleration on EN7581 +mac block, in order to properly parse packets, requires DSA untagged +packets on TX side and read DSA tag from DMA descriptor on RX side. +For this reason, enable RX Special Tag (SPTAG) for EN7581 SoC. +This is a preliminary patch to enable netfilter flowtable hw offloading +on EN7581 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 5 +++++ + drivers/net/dsa/mt7530.h | 4 ++++ + 2 files changed, 9 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2599,6 +2599,11 @@ mt7531_setup_common(struct dsa_switch *d + /* Allow mirroring frames received on the local port (monitor port). */ + mt7530_set(priv, MT753X_AGC, LOCAL_EN); + ++ /* Enable Special Tag for rx frames */ ++ if (priv->id == ID_EN7581) ++ mt7530_write(priv, MT753X_CPORT_SPTAG_CFG, ++ CPORT_SW2FE_STAG_EN | CPORT_FE2SW_STAG_EN); ++ + /* Flush the FDB table */ + ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); + if (ret < 0) +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -615,6 +615,10 @@ enum mt7531_xtal_fsel { + #define MT7531_GPIO12_RG_RXD3_MASK GENMASK(19, 16) + #define MT7531_EXT_P_MDIO_12 (2 << 16) + ++#define MT753X_CPORT_SPTAG_CFG 0x7c10 ++#define CPORT_SW2FE_STAG_EN BIT(1) ++#define CPORT_FE2SW_STAG_EN BIT(0) ++ + /* Registers for LED GPIO control (MT7530 only) + * All registers follow this pattern: + * [ 2: 0] port 0 diff --git a/target/linux/airoha/patches-6.12/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch b/target/linux/airoha/patches-6.12/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch new file mode 100644 index 00000000000..3441758c1fa --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch @@ -0,0 +1,144 @@ +From 80369686737fe07c233a1152da0b84372dabdcd6 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:15 +0100 +Subject: [PATCH 07/15] net: airoha: Enable support for multiple net_devices + +In the current codebase airoha_eth driver supports just a single +net_device connected to the Packet Switch Engine (PSE) lan port (GDM1). +As shown in commit 23020f049327 ("net: airoha: Introduce ethernet +support for EN7581 SoC"), PSE can switch packets between four GDM ports. +Enable the capability to create a net_device for each GDM port of the +PSE module. Moreover, since the QDMA blocks can be shared between +net_devices, do not stop TX/RX DMA in airoha_dev_stop() if there are +active net_devices for this QDMA block. +This is a preliminary patch to enable flowtable hw offloading for EN7581 +SoC. + +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 35 ++++++++++++++---------- + drivers/net/ethernet/airoha/airoha_eth.h | 4 ++- + 2 files changed, 24 insertions(+), 15 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1583,6 +1583,7 @@ static int airoha_dev_open(struct net_de + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); ++ atomic_inc(&qdma->users); + + return 0; + } +@@ -1598,16 +1599,20 @@ static int airoha_dev_stop(struct net_de + if (err) + return err; + +- airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, +- GLOBAL_CFG_TX_DMA_EN_MASK | +- GLOBAL_CFG_RX_DMA_EN_MASK); ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) ++ netdev_tx_reset_subqueue(dev, i); + +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- if (!qdma->q_tx[i].ndesc) +- continue; ++ if (atomic_dec_and_test(&qdma->users)) { ++ airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, ++ GLOBAL_CFG_TX_DMA_EN_MASK | ++ GLOBAL_CFG_RX_DMA_EN_MASK); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) ++ continue; + +- airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); +- netdev_tx_reset_subqueue(dev, i); ++ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); ++ } + } + + return 0; +@@ -2350,13 +2355,14 @@ static void airoha_metadata_dst_free(str + } + } + +-static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np) ++static int airoha_alloc_gdm_port(struct airoha_eth *eth, ++ struct device_node *np, int index) + { + const __be32 *id_ptr = of_get_property(np, "reg", NULL); + struct airoha_gdm_port *port; + struct airoha_qdma *qdma; + struct net_device *dev; +- int err, index; ++ int err, p; + u32 id; + + if (!id_ptr) { +@@ -2365,14 +2371,14 @@ static int airoha_alloc_gdm_port(struct + } + + id = be32_to_cpup(id_ptr); +- index = id - 1; ++ p = id - 1; + + if (!id || id > ARRAY_SIZE(eth->ports)) { + dev_err(eth->dev, "invalid gdm port id: %d\n", id); + return -EINVAL; + } + +- if (eth->ports[index]) { ++ if (eth->ports[p]) { + dev_err(eth->dev, "duplicate gdm port id: %d\n", id); + return -EINVAL; + } +@@ -2420,7 +2426,7 @@ static int airoha_alloc_gdm_port(struct + port->qdma = qdma; + port->dev = dev; + port->id = id; +- eth->ports[index] = port; ++ eth->ports[p] = port; + + err = airoha_metadata_dst_alloc(port); + if (err) +@@ -2492,6 +2498,7 @@ static int airoha_probe(struct platform_ + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_qdma_start_napi(ð->qdma[i]); + ++ i = 0; + for_each_child_of_node(pdev->dev.of_node, np) { + if (!of_device_is_compatible(np, "airoha,eth-mac")) + continue; +@@ -2499,7 +2506,7 @@ static int airoha_probe(struct platform_ + if (!of_device_is_available(np)) + continue; + +- err = airoha_alloc_gdm_port(eth, np); ++ err = airoha_alloc_gdm_port(eth, np, i++); + if (err) { + of_node_put(np); + goto error_napi_stop; +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -13,7 +13,7 @@ + #include + #include + +-#define AIROHA_MAX_NUM_GDM_PORTS 1 ++#define AIROHA_MAX_NUM_GDM_PORTS 4 + #define AIROHA_MAX_NUM_QDMA 2 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 +@@ -212,6 +212,8 @@ struct airoha_qdma { + u32 irqmask[QDMA_INT_REG_MAX]; + int irq; + ++ atomic_t users; ++ + struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; diff --git a/target/linux/airoha/patches-6.12/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch b/target/linux/airoha/patches-6.12/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch new file mode 100644 index 00000000000..4edf38bf9a4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch @@ -0,0 +1,77 @@ +From 67fde5d58cd43d129a979e918ec9cd5d2e2fbcfb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:16 +0100 +Subject: [PATCH 08/15] net: airoha: Move REG_GDM_FWD_CFG() initialization in + airoha_dev_init() + +Move REG_GDM_FWD_CFG() register initialization in airoha_dev_init +routine. Moreover, always send traffic PPE module in order to be +processed by hw accelerator. +This is a preliminary patch to enable netfilter flowtable hw offloading +on EN7581 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 14 ++++---------- + 1 file changed, 4 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -107,25 +107,20 @@ static void airoha_set_gdm_port_fwd_cfg( + + static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable) + { +- u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP; +- u32 vip_port, cfg_addr; ++ u32 vip_port; + + switch (port) { + case XSI_PCIE0_PORT: + vip_port = XSI_PCIE0_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(3); + break; + case XSI_PCIE1_PORT: + vip_port = XSI_PCIE1_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(3); + break; + case XSI_USB_PORT: + vip_port = XSI_USB_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(4); + break; + case XSI_ETH_PORT: + vip_port = XSI_ETH_VIP_PORT_MASK; +- cfg_addr = REG_GDM_FWD_CFG(4); + break; + default: + return -EINVAL; +@@ -139,8 +134,6 @@ static int airoha_set_gdm_port(struct ai + airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port); + } + +- airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val); +- + return 0; + } + +@@ -177,8 +170,6 @@ static void airoha_fe_maccr_init(struct + airoha_fe_set(eth, REG_GDM_FWD_CFG(p), + GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | + GDM_DROP_CRC_ERR); +- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p), +- FE_PSE_PORT_CDM1); + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +@@ -1635,8 +1626,11 @@ static int airoha_dev_set_macaddr(struct + static int airoha_dev_init(struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; + + airoha_set_macaddr(port, dev->dev_addr); ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), ++ FE_PSE_PORT_PPE1); + + return 0; + } diff --git a/target/linux/airoha/patches-6.12/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch b/target/linux/airoha/patches-6.12/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch new file mode 100644 index 00000000000..fb2dfed9b3f --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch @@ -0,0 +1,120 @@ +From c28b8375f6d02ef3b5e8c51234cc3f6d47d9fb7f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:17 +0100 +Subject: [PATCH 09/15] net: airoha: Rename airoha_set_gdm_port_fwd_cfg() in + airoha_set_vip_for_gdm_port() + +Rename airoha_set_gdm_port() in airoha_set_vip_for_gdm_port(). +Get rid of airoha_set_gdm_ports routine. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 49 ++++++------------------ + drivers/net/ethernet/airoha/airoha_eth.h | 8 ---- + 2 files changed, 11 insertions(+), 46 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -105,25 +105,23 @@ static void airoha_set_gdm_port_fwd_cfg( + FIELD_PREP(GDM_UCFQ_MASK, val)); + } + +-static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable) ++static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port, ++ bool enable) + { ++ struct airoha_eth *eth = port->qdma->eth; + u32 vip_port; + +- switch (port) { +- case XSI_PCIE0_PORT: ++ switch (port->id) { ++ case 3: ++ /* FIXME: handle XSI_PCIE1_PORT */ + vip_port = XSI_PCIE0_VIP_PORT_MASK; + break; +- case XSI_PCIE1_PORT: +- vip_port = XSI_PCIE1_VIP_PORT_MASK; +- break; +- case XSI_USB_PORT: +- vip_port = XSI_USB_VIP_PORT_MASK; +- break; +- case XSI_ETH_PORT: ++ case 4: ++ /* FIXME: handle XSI_USB_PORT */ + vip_port = XSI_ETH_VIP_PORT_MASK; + break; + default: +- return -EINVAL; ++ return 0; + } + + if (enable) { +@@ -137,31 +135,6 @@ static int airoha_set_gdm_port(struct ai + return 0; + } + +-static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable) +-{ +- const int port_list[] = { +- XSI_PCIE0_PORT, +- XSI_PCIE1_PORT, +- XSI_USB_PORT, +- XSI_ETH_PORT +- }; +- int i, err; +- +- for (i = 0; i < ARRAY_SIZE(port_list); i++) { +- err = airoha_set_gdm_port(eth, port_list[i], enable); +- if (err) +- goto error; +- } +- +- return 0; +- +-error: +- for (i--; i >= 0; i--) +- airoha_set_gdm_port(eth, port_list[i], false); +- +- return err; +-} +- + static void airoha_fe_maccr_init(struct airoha_eth *eth) + { + int p; +@@ -1560,7 +1533,7 @@ static int airoha_dev_open(struct net_de + int err; + + netif_tx_start_all_queues(dev); +- err = airoha_set_gdm_ports(qdma->eth, true); ++ err = airoha_set_vip_for_gdm_port(port, true); + if (err) + return err; + +@@ -1586,7 +1559,7 @@ static int airoha_dev_stop(struct net_de + int i, err; + + netif_tx_disable(dev); +- err = airoha_set_gdm_ports(qdma->eth, false); ++ err = airoha_set_vip_for_gdm_port(port, false); + if (err) + return err; + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -58,14 +58,6 @@ enum { + }; + + enum { +- XSI_PCIE0_PORT, +- XSI_PCIE1_PORT, +- XSI_USB_PORT, +- XSI_AE_PORT, +- XSI_ETH_PORT, +-}; +- +-enum { + XSI_PCIE0_VIP_PORT_MASK = BIT(22), + XSI_PCIE1_VIP_PORT_MASK = BIT(23), + XSI_USB_VIP_PORT_MASK = BIT(25), diff --git a/target/linux/airoha/patches-6.12/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch b/target/linux/airoha/patches-6.12/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch new file mode 100644 index 00000000000..41c56223040 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch @@ -0,0 +1,627 @@ +From 23290c7bc190def4e1ca61610992d9b7c32e33f3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:20 +0100 +Subject: [PATCH 12/15] net: airoha: Introduce Airoha NPU support + +Packet Processor Engine (PPE) module available on EN7581 SoC populates +the PPE table with 5-tuples flower rules learned from traffic forwarded +between the GDM ports connected to the Packet Switch Engine (PSE) module. +The airoha_eth driver can enable hw acceleration of learned 5-tuples +rules if the user configure them in netfilter flowtable (netfilter +flowtable support will be added with subsequent patches). +airoha_eth driver configures and collects data from the PPE module via a +Network Processor Unit (NPU) RISC-V module available on the EN7581 SoC. +Introduce basic support for Airoha NPU module. + +Tested-by: Sayantan Nandy +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/Kconfig | 9 + + drivers/net/ethernet/airoha/Makefile | 1 + + drivers/net/ethernet/airoha/airoha_eth.h | 2 + + drivers/net/ethernet/airoha/airoha_npu.c | 520 +++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 34 ++ + 5 files changed, 566 insertions(+) + create mode 100644 drivers/net/ethernet/airoha/airoha_npu.c + create mode 100644 drivers/net/ethernet/airoha/airoha_npu.h + +--- a/drivers/net/ethernet/airoha/Kconfig ++++ b/drivers/net/ethernet/airoha/Kconfig +@@ -7,9 +7,18 @@ config NET_VENDOR_AIROHA + + if NET_VENDOR_AIROHA + ++config NET_AIROHA_NPU ++ tristate "Airoha NPU support" ++ select WANT_DEV_COREDUMP ++ select REGMAP_MMIO ++ help ++ This driver supports Airoha Network Processor (NPU) available ++ on the Airoha Soc family. ++ + config NET_AIROHA + tristate "Airoha SoC Gigabit Ethernet support" + depends on NET_DSA || !NET_DSA ++ select NET_AIROHA_NPU + select PAGE_POOL + help + This driver supports the gigabit ethernet MACs in the +--- a/drivers/net/ethernet/airoha/Makefile ++++ b/drivers/net/ethernet/airoha/Makefile +@@ -4,3 +4,4 @@ + # + + obj-$(CONFIG_NET_AIROHA) += airoha_eth.o ++obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -240,6 +240,8 @@ struct airoha_eth { + unsigned long state; + void __iomem *fe_regs; + ++ struct airoha_npu __rcu *npu; ++ + struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; + struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; + +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -0,0 +1,520 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2025 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "airoha_npu.h" ++ ++#define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" ++#define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin" ++#define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE 0x200000 ++#define NPU_EN7581_FIRMWARE_DATA_MAX_SIZE 0x10000 ++#define NPU_DUMP_SIZE 512 ++ ++#define REG_NPU_LOCAL_SRAM 0x0 ++ ++#define NPU_PC_BASE_ADDR 0x305000 ++#define REG_PC_DBG(_n) (0x305000 + ((_n) * 0x100)) ++ ++#define NPU_CLUSTER_BASE_ADDR 0x306000 ++ ++#define REG_CR_BOOT_TRIGGER (NPU_CLUSTER_BASE_ADDR + 0x000) ++#define REG_CR_BOOT_CONFIG (NPU_CLUSTER_BASE_ADDR + 0x004) ++#define REG_CR_BOOT_BASE(_n) (NPU_CLUSTER_BASE_ADDR + 0x020 + ((_n) << 2)) ++ ++#define NPU_MBOX_BASE_ADDR 0x30c000 ++ ++#define REG_CR_MBOX_INT_STATUS (NPU_MBOX_BASE_ADDR + 0x000) ++#define MBOX_INT_STATUS_MASK BIT(8) ++ ++#define REG_CR_MBOX_INT_MASK(_n) (NPU_MBOX_BASE_ADDR + 0x004 + ((_n) << 2)) ++#define REG_CR_MBQ0_CTRL(_n) (NPU_MBOX_BASE_ADDR + 0x030 + ((_n) << 2)) ++#define REG_CR_MBQ8_CTRL(_n) (NPU_MBOX_BASE_ADDR + 0x0b0 + ((_n) << 2)) ++#define REG_CR_NPU_MIB(_n) (NPU_MBOX_BASE_ADDR + 0x140 + ((_n) << 2)) ++ ++#define NPU_TIMER_BASE_ADDR 0x310100 ++#define REG_WDT_TIMER_CTRL(_n) (NPU_TIMER_BASE_ADDR + ((_n) * 0x100)) ++#define WDT_EN_MASK BIT(25) ++#define WDT_INTR_MASK BIT(21) ++ ++enum { ++ NPU_OP_SET = 1, ++ NPU_OP_SET_NO_WAIT, ++ NPU_OP_GET, ++ NPU_OP_GET_NO_WAIT, ++}; ++ ++enum { ++ NPU_FUNC_WIFI, ++ NPU_FUNC_TUNNEL, ++ NPU_FUNC_NOTIFY, ++ NPU_FUNC_DBA, ++ NPU_FUNC_TR471, ++ NPU_FUNC_PPE, ++}; ++ ++enum { ++ NPU_MBOX_ERROR, ++ NPU_MBOX_SUCCESS, ++}; ++ ++enum { ++ PPE_FUNC_SET_WAIT, ++ PPE_FUNC_SET_WAIT_HWNAT_INIT, ++ PPE_FUNC_SET_WAIT_HWNAT_DEINIT, ++ PPE_FUNC_SET_WAIT_API, ++}; ++ ++enum { ++ PPE2_SRAM_SET_ENTRY, ++ PPE_SRAM_SET_ENTRY, ++ PPE_SRAM_SET_VAL, ++ PPE_SRAM_RESET_VAL, ++}; ++ ++enum { ++ QDMA_WAN_ETHER = 1, ++ QDMA_WAN_PON_XDSL, ++}; ++ ++#define MBOX_MSG_FUNC_ID GENMASK(14, 11) ++#define MBOX_MSG_STATIC_BUF BIT(5) ++#define MBOX_MSG_STATUS GENMASK(4, 2) ++#define MBOX_MSG_DONE BIT(1) ++#define MBOX_MSG_WAIT_RSP BIT(0) ++ ++#define PPE_TYPE_L2B_IPV4 2 ++#define PPE_TYPE_L2B_IPV4_IPV6 3 ++ ++struct ppe_mbox_data { ++ u32 func_type; ++ u32 func_id; ++ union { ++ struct { ++ u8 cds; ++ u8 xpon_hal_api; ++ u8 wan_xsi; ++ u8 ct_joyme4; ++ int ppe_type; ++ int wan_mode; ++ int wan_sel; ++ } init_info; ++ struct { ++ int func_id; ++ u32 size; ++ u32 data; ++ } set_info; ++ }; ++}; ++ ++static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, ++ void *p, int size) ++{ ++ u16 core = 0; /* FIXME */ ++ u32 val, offset = core << 4; ++ dma_addr_t dma_addr; ++ void *addr; ++ int ret; ++ ++ addr = kmemdup(p, size, GFP_ATOMIC); ++ if (!addr) ++ return -ENOMEM; ++ ++ dma_addr = dma_map_single(npu->dev, addr, size, DMA_TO_DEVICE); ++ ret = dma_mapping_error(npu->dev, dma_addr); ++ if (ret) ++ goto out; ++ ++ spin_lock_bh(&npu->cores[core].lock); ++ ++ regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(0) + offset, dma_addr); ++ regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(1) + offset, size); ++ regmap_read(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, &val); ++ regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, val + 1); ++ val = FIELD_PREP(MBOX_MSG_FUNC_ID, func_id) | MBOX_MSG_WAIT_RSP; ++ regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(3) + offset, val); ++ ++ ret = regmap_read_poll_timeout_atomic(npu->regmap, ++ REG_CR_MBQ0_CTRL(3) + offset, ++ val, (val & MBOX_MSG_DONE), ++ 100, 100 * MSEC_PER_SEC); ++ if (!ret && FIELD_GET(MBOX_MSG_STATUS, val) != NPU_MBOX_SUCCESS) ++ ret = -EINVAL; ++ ++ spin_unlock_bh(&npu->cores[core].lock); ++ ++ dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); ++out: ++ kfree(addr); ++ ++ return ret; ++} ++ ++static int airoha_npu_run_firmware(struct device *dev, void __iomem *base, ++ struct reserved_mem *rmem) ++{ ++ const struct firmware *fw; ++ void __iomem *addr; ++ int ret; ++ ++ ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_RV32, dev); ++ if (ret) ++ return ret == -ENOENT ? -EPROBE_DEFER : ret; ++ ++ if (fw->size > NPU_EN7581_FIRMWARE_RV32_MAX_SIZE) { ++ dev_err(dev, "%s: fw size too overlimit (%zu)\n", ++ NPU_EN7581_FIRMWARE_RV32, fw->size); ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ addr = devm_ioremap(dev, rmem->base, rmem->size); ++ if (!addr) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ memcpy_toio(addr, fw->data, fw->size); ++ release_firmware(fw); ++ ++ ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_DATA, dev); ++ if (ret) ++ return ret == -ENOENT ? -EPROBE_DEFER : ret; ++ ++ if (fw->size > NPU_EN7581_FIRMWARE_DATA_MAX_SIZE) { ++ dev_err(dev, "%s: fw size too overlimit (%zu)\n", ++ NPU_EN7581_FIRMWARE_DATA, fw->size); ++ ret = -E2BIG; ++ goto out; ++ } ++ ++ memcpy_toio(base + REG_NPU_LOCAL_SRAM, fw->data, fw->size); ++out: ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance) ++{ ++ struct airoha_npu *npu = npu_instance; ++ ++ /* clear mbox interrupt status */ ++ regmap_write(npu->regmap, REG_CR_MBOX_INT_STATUS, ++ MBOX_INT_STATUS_MASK); ++ ++ /* acknowledge npu */ ++ regmap_update_bits(npu->regmap, REG_CR_MBQ8_CTRL(3), ++ MBOX_MSG_STATUS | MBOX_MSG_DONE, MBOX_MSG_DONE); ++ ++ return IRQ_HANDLED; ++} ++ ++static void airoha_npu_wdt_work(struct work_struct *work) ++{ ++ struct airoha_npu_core *core; ++ struct airoha_npu *npu; ++ void *dump; ++ u32 val[3]; ++ int c; ++ ++ core = container_of(work, struct airoha_npu_core, wdt_work); ++ npu = core->npu; ++ ++ dump = vzalloc(NPU_DUMP_SIZE); ++ if (!dump) ++ return; ++ ++ c = core - &npu->cores[0]; ++ regmap_bulk_read(npu->regmap, REG_PC_DBG(c), val, ARRAY_SIZE(val)); ++ snprintf(dump, NPU_DUMP_SIZE, "PC: %08x SP: %08x LR: %08x\n", ++ val[0], val[1], val[2]); ++ ++ dev_coredumpv(npu->dev, dump, NPU_DUMP_SIZE, GFP_KERNEL); ++} ++ ++static irqreturn_t airoha_npu_wdt_handler(int irq, void *core_instance) ++{ ++ struct airoha_npu_core *core = core_instance; ++ struct airoha_npu *npu = core->npu; ++ int c = core - &npu->cores[0]; ++ u32 val; ++ ++ regmap_set_bits(npu->regmap, REG_WDT_TIMER_CTRL(c), WDT_INTR_MASK); ++ if (!regmap_read(npu->regmap, REG_WDT_TIMER_CTRL(c), &val) && ++ FIELD_GET(WDT_EN_MASK, val)) ++ schedule_work(&core->wdt_work); ++ ++ return IRQ_HANDLED; ++} ++ ++static int airoha_npu_ppe_init(struct airoha_npu *npu) ++{ ++ struct ppe_mbox_data ppe_data = { ++ .func_type = NPU_OP_SET, ++ .func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT, ++ .init_info = { ++ .ppe_type = PPE_TYPE_L2B_IPV4_IPV6, ++ .wan_mode = QDMA_WAN_ETHER, ++ }, ++ }; ++ ++ return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, ++ sizeof(struct ppe_mbox_data)); ++} ++ ++static int airoha_npu_ppe_deinit(struct airoha_npu *npu) ++{ ++ struct ppe_mbox_data ppe_data = { ++ .func_type = NPU_OP_SET, ++ .func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT, ++ }; ++ ++ return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, ++ sizeof(struct ppe_mbox_data)); ++} ++ ++static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ int sram_num_entries) ++{ ++ struct ppe_mbox_data ppe_data = { ++ .func_type = NPU_OP_SET, ++ .func_id = PPE_FUNC_SET_WAIT_API, ++ .set_info = { ++ .func_id = PPE_SRAM_RESET_VAL, ++ .data = foe_addr, ++ .size = sram_num_entries, ++ }, ++ }; ++ ++ return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, ++ sizeof(struct ppe_mbox_data)); ++} ++ ++static int airoha_npu_foe_commit_entry(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ u32 entry_size, u32 hash, bool ppe2) ++{ ++ struct ppe_mbox_data ppe_data = { ++ .func_type = NPU_OP_SET, ++ .func_id = PPE_FUNC_SET_WAIT_API, ++ .set_info = { ++ .data = foe_addr, ++ .size = entry_size, ++ }, ++ }; ++ int err; ++ ++ ppe_data.set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY ++ : PPE_SRAM_SET_ENTRY; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, ++ sizeof(struct ppe_mbox_data)); ++ if (err) ++ return err; ++ ++ ppe_data.set_info.func_id = PPE_SRAM_SET_VAL; ++ ppe_data.set_info.data = hash; ++ ppe_data.set_info.size = sizeof(u32); ++ ++ return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, ++ sizeof(struct ppe_mbox_data)); ++} ++ ++struct airoha_npu *airoha_npu_get(struct device *dev) ++{ ++ struct platform_device *pdev; ++ struct device_node *np; ++ struct airoha_npu *npu; ++ ++ np = of_parse_phandle(dev->of_node, "airoha,npu", 0); ++ if (!np) ++ return ERR_PTR(-ENODEV); ++ ++ pdev = of_find_device_by_node(np); ++ of_node_put(np); ++ ++ if (!pdev) { ++ dev_err(dev, "cannot find device node %s\n", np->name); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ if (!try_module_get(THIS_MODULE)) { ++ dev_err(dev, "failed to get the device driver module\n"); ++ npu = ERR_PTR(-ENODEV); ++ goto error_pdev_put; ++ } ++ ++ npu = platform_get_drvdata(pdev); ++ if (!npu) { ++ npu = ERR_PTR(-ENODEV); ++ goto error_module_put; ++ } ++ ++ if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER)) { ++ dev_err(&pdev->dev, ++ "failed to create device link to consumer %s\n", ++ dev_name(dev)); ++ npu = ERR_PTR(-EINVAL); ++ goto error_module_put; ++ } ++ ++ return npu; ++ ++error_module_put: ++ module_put(THIS_MODULE); ++error_pdev_put: ++ platform_device_put(pdev); ++ ++ return npu; ++} ++EXPORT_SYMBOL_GPL(airoha_npu_get); ++ ++void airoha_npu_put(struct airoha_npu *npu) ++{ ++ module_put(THIS_MODULE); ++ put_device(npu->dev); ++} ++EXPORT_SYMBOL_GPL(airoha_npu_put); ++ ++static const struct of_device_id of_airoha_npu_match[] = { ++ { .compatible = "airoha,en7581-npu" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, of_airoha_npu_match); ++ ++static const struct regmap_config regmap_config = { ++ .name = "npu", ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .disable_locking = true, ++}; ++ ++static int airoha_npu_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct reserved_mem *rmem; ++ struct airoha_npu *npu; ++ struct device_node *np; ++ void __iomem *base; ++ int i, irq, err; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ npu = devm_kzalloc(dev, sizeof(*npu), GFP_KERNEL); ++ if (!npu) ++ return -ENOMEM; ++ ++ npu->dev = dev; ++ npu->ops.ppe_init = airoha_npu_ppe_init; ++ npu->ops.ppe_deinit = airoha_npu_ppe_deinit; ++ npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries; ++ npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry; ++ ++ npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); ++ if (IS_ERR(npu->regmap)) ++ return PTR_ERR(npu->regmap); ++ ++ np = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!np) ++ return -ENODEV; ++ ++ rmem = of_reserved_mem_lookup(np); ++ of_node_put(np); ++ ++ if (!rmem) ++ return -ENODEV; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ err = devm_request_irq(dev, irq, airoha_npu_mbox_handler, ++ IRQF_SHARED, "airoha-npu-mbox", npu); ++ if (err) ++ return err; ++ ++ for (i = 0; i < ARRAY_SIZE(npu->cores); i++) { ++ struct airoha_npu_core *core = &npu->cores[i]; ++ ++ spin_lock_init(&core->lock); ++ core->npu = npu; ++ ++ irq = platform_get_irq(pdev, i + 1); ++ if (irq < 0) ++ return irq; ++ ++ err = devm_request_irq(dev, irq, airoha_npu_wdt_handler, ++ IRQF_SHARED, "airoha-npu-wdt", core); ++ if (err) ++ return err; ++ ++ INIT_WORK(&core->wdt_work, airoha_npu_wdt_work); ++ } ++ ++ err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); ++ if (err) ++ return err; ++ ++ err = airoha_npu_run_firmware(dev, base, rmem); ++ if (err) ++ return dev_err_probe(dev, err, "failed to run npu firmware\n"); ++ ++ regmap_write(npu->regmap, REG_CR_NPU_MIB(10), ++ rmem->base + NPU_EN7581_FIRMWARE_RV32_MAX_SIZE); ++ regmap_write(npu->regmap, REG_CR_NPU_MIB(11), 0x40000); /* SRAM 256K */ ++ regmap_write(npu->regmap, REG_CR_NPU_MIB(12), 0); ++ regmap_write(npu->regmap, REG_CR_NPU_MIB(21), 1); ++ msleep(100); ++ ++ /* setting booting address */ ++ for (i = 0; i < NPU_NUM_CORES; i++) ++ regmap_write(npu->regmap, REG_CR_BOOT_BASE(i), rmem->base); ++ usleep_range(1000, 2000); ++ ++ /* enable NPU cores */ ++ /* do not start core3 since it is used for WiFi offloading */ ++ regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xf7); ++ regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1); ++ msleep(100); ++ ++ platform_set_drvdata(pdev, npu); ++ ++ return 0; ++} ++ ++static void airoha_npu_remove(struct platform_device *pdev) ++{ ++ struct airoha_npu *npu = platform_get_drvdata(pdev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(npu->cores); i++) ++ cancel_work_sync(&npu->cores[i].wdt_work); ++} ++ ++static struct platform_driver airoha_npu_driver = { ++ .probe = airoha_npu_probe, ++ .remove_new = airoha_npu_remove, ++ .driver = { ++ .name = "airoha-npu", ++ .of_match_table = of_airoha_npu_match, ++ }, ++}; ++module_platform_driver(airoha_npu_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_DESCRIPTION("Airoha Network Processor Unit driver"); +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -0,0 +1,34 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2025 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#define NPU_NUM_CORES 8 ++ ++struct airoha_npu { ++ struct device *dev; ++ struct regmap *regmap; ++ ++ struct airoha_npu_core { ++ struct airoha_npu *npu; ++ /* protect concurrent npu memory accesses */ ++ spinlock_t lock; ++ struct work_struct wdt_work; ++ } cores[NPU_NUM_CORES]; ++ ++ struct { ++ int (*ppe_init)(struct airoha_npu *npu); ++ int (*ppe_deinit)(struct airoha_npu *npu); ++ int (*ppe_flush_sram_entries)(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ int sram_num_entries); ++ int (*ppe_foe_commit_entry)(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ u32 entry_size, u32 hash, ++ bool ppe2); ++ } ops; ++}; ++ ++struct airoha_npu *airoha_npu_get(struct device *dev); ++void airoha_npu_put(struct airoha_npu *npu); diff --git a/target/linux/airoha/patches-6.12/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch b/target/linux/airoha/patches-6.12/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch new file mode 100644 index 00000000000..bb2394acc1c --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch @@ -0,0 +1,1481 @@ +From 00a7678310fe3d3f408513e55d9a0b67f0db380f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:21 +0100 +Subject: [PATCH 13/15] net: airoha: Introduce flowtable offload support + +Introduce netfilter flowtable integration in order to allow airoha_eth +driver to offload 5-tuple flower rules learned by the PPE module if the +user accelerates them using a nft configuration similar to the one reported +below: + +table inet filter { + flowtable ft { + hook ingress priority filter + devices = { lan1, lan2, lan3, lan4, eth1 } + flags offload; + } + chain forward { + type filter hook forward priority filter; policy accept; + meta l4proto { tcp, udp } flow add @ft + } +} + +Tested-by: Sayantan Nandy +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/Makefile | 3 +- + drivers/net/ethernet/airoha/airoha_eth.c | 60 +- + drivers/net/ethernet/airoha/airoha_eth.h | 250 ++++++ + drivers/net/ethernet/airoha/airoha_ppe.c | 901 ++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_regs.h | 107 ++- + 5 files changed, 1314 insertions(+), 7 deletions(-) + create mode 100644 drivers/net/ethernet/airoha/airoha_ppe.c + +--- a/drivers/net/ethernet/airoha/Makefile ++++ b/drivers/net/ethernet/airoha/Makefile +@@ -3,5 +3,6 @@ + # Airoha for the Mediatek SoCs built-in ethernet macs + # + +-obj-$(CONFIG_NET_AIROHA) += airoha_eth.o ++obj-$(CONFIG_NET_AIROHA) += airoha-eth.o ++airoha-eth-y := airoha_eth.o airoha_ppe.o + obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -8,7 +8,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -619,6 +618,7 @@ static int airoha_qdma_rx_process(struct + while (done < budget) { + struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; ++ u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); + dma_addr_t dma_addr = le32_to_cpu(desc->addr); + u32 desc_ctrl = le32_to_cpu(desc->ctrl); + struct airoha_gdm_port *port; +@@ -681,6 +681,15 @@ static int airoha_qdma_rx_process(struct + &port->dsa_meta[sptag]->dst); + } + ++ hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1); ++ if (hash != AIROHA_RXD4_FOE_ENTRY) ++ skb_set_hash(skb, jhash_1word(hash, 0), ++ PKT_HASH_TYPE_L4); ++ ++ reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); ++ if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) ++ airoha_ppe_check_skb(eth->ppe, hash); ++ + napi_gro_receive(&q->napi, skb); + + done++; +@@ -1322,6 +1331,10 @@ static int airoha_hw_init(struct platfor + return err; + } + ++ err = airoha_ppe_init(eth); ++ if (err) ++ return err; ++ + set_bit(DEV_STATE_INITIALIZED, ð->state); + + return 0; +@@ -2186,6 +2199,47 @@ static int airoha_tc_htb_alloc_leaf_queu + return 0; + } + ++static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port, ++ struct flow_block_offload *f) ++{ ++ flow_setup_cb_t *cb = airoha_ppe_setup_tc_block_cb; ++ static LIST_HEAD(block_cb_list); ++ struct flow_block_cb *block_cb; ++ ++ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) ++ return -EOPNOTSUPP; ++ ++ f->driver_block_list = &block_cb_list; ++ switch (f->command) { ++ case FLOW_BLOCK_BIND: ++ block_cb = flow_block_cb_lookup(f->block, cb, port->dev); ++ if (block_cb) { ++ flow_block_cb_incref(block_cb); ++ return 0; ++ } ++ block_cb = flow_block_cb_alloc(cb, port->dev, port->dev, NULL); ++ if (IS_ERR(block_cb)) ++ return PTR_ERR(block_cb); ++ ++ flow_block_cb_incref(block_cb); ++ flow_block_cb_add(block_cb, f); ++ list_add_tail(&block_cb->driver_list, &block_cb_list); ++ return 0; ++ case FLOW_BLOCK_UNBIND: ++ block_cb = flow_block_cb_lookup(f->block, cb, port->dev); ++ if (!block_cb) ++ return -ENOENT; ++ ++ if (!flow_block_cb_decref(block_cb)) { ++ flow_block_cb_remove(block_cb, f); ++ list_del(&block_cb->driver_list); ++ } ++ return 0; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ + static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue) + { + struct net_device *dev = port->dev; +@@ -2269,6 +2323,9 @@ static int airoha_dev_tc_setup(struct ne + return airoha_tc_setup_qdisc_ets(port, type_data); + case TC_SETUP_QDISC_HTB: + return airoha_tc_setup_qdisc_htb(port, type_data); ++ case TC_SETUP_BLOCK: ++ case TC_SETUP_FT: ++ return airoha_dev_setup_tc_block(port, type_data); + default: + return -EOPNOTSUPP; + } +@@ -2524,6 +2581,7 @@ static void airoha_remove(struct platfor + } + free_netdev(eth->napi_dev); + ++ airoha_ppe_deinit(eth); + platform_set_drvdata(pdev, NULL); + } + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #define AIROHA_MAX_NUM_GDM_PORTS 4 + #define AIROHA_MAX_NUM_QDMA 2 +@@ -44,6 +45,15 @@ + #define QDMA_METER_IDX(_n) ((_n) & 0xff) + #define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) + ++#define PPE_NUM 2 ++#define PPE1_SRAM_NUM_ENTRIES (8 * 1024) ++#define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES) ++#define PPE_DRAM_NUM_ENTRIES (16 * 1024) ++#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) ++#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1) ++#define PPE_ENTRY_SIZE 80 ++#define PPE_RAM_NUM_ENTRIES_SHIFT(_n) (__ffs((_n) >> 10)) ++ + #define MTK_HDR_LEN 4 + #define MTK_HDR_XMIT_TAGGED_TPID_8100 1 + #define MTK_HDR_XMIT_TAGGED_TPID_88A8 2 +@@ -195,6 +205,224 @@ struct airoha_hw_stats { + u64 rx_len[7]; + }; + ++enum { ++ PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f, ++}; ++ ++enum { ++ AIROHA_FOE_STATE_INVALID, ++ AIROHA_FOE_STATE_UNBIND, ++ AIROHA_FOE_STATE_BIND, ++ AIROHA_FOE_STATE_FIN ++}; ++ ++enum { ++ PPE_PKT_TYPE_IPV4_HNAPT = 0, ++ PPE_PKT_TYPE_IPV4_ROUTE = 1, ++ PPE_PKT_TYPE_BRIDGE = 2, ++ PPE_PKT_TYPE_IPV4_DSLITE = 3, ++ PPE_PKT_TYPE_IPV6_ROUTE_3T = 4, ++ PPE_PKT_TYPE_IPV6_ROUTE_5T = 5, ++ PPE_PKT_TYPE_IPV6_6RD = 7, ++}; ++ ++#define AIROHA_FOE_MAC_SMAC_ID GENMASK(20, 16) ++#define AIROHA_FOE_MAC_PPPOE_ID GENMASK(15, 0) ++ ++struct airoha_foe_mac_info_common { ++ u16 vlan1; ++ u16 etype; ++ ++ u32 dest_mac_hi; ++ ++ u16 vlan2; ++ u16 dest_mac_lo; ++ ++ u32 src_mac_hi; ++}; ++ ++struct airoha_foe_mac_info { ++ struct airoha_foe_mac_info_common common; ++ ++ u16 pppoe_id; ++ u16 src_mac_lo; ++}; ++ ++#define AIROHA_FOE_IB1_UNBIND_PREBIND BIT(24) ++#define AIROHA_FOE_IB1_UNBIND_PACKETS GENMASK(23, 8) ++#define AIROHA_FOE_IB1_UNBIND_TIMESTAMP GENMASK(7, 0) ++ ++#define AIROHA_FOE_IB1_BIND_STATIC BIT(31) ++#define AIROHA_FOE_IB1_BIND_UDP BIT(30) ++#define AIROHA_FOE_IB1_BIND_STATE GENMASK(29, 28) ++#define AIROHA_FOE_IB1_BIND_PACKET_TYPE GENMASK(27, 25) ++#define AIROHA_FOE_IB1_BIND_TTL BIT(24) ++#define AIROHA_FOE_IB1_BIND_TUNNEL_DECAP BIT(23) ++#define AIROHA_FOE_IB1_BIND_PPPOE BIT(22) ++#define AIROHA_FOE_IB1_BIND_VPM GENMASK(21, 20) ++#define AIROHA_FOE_IB1_BIND_VLAN_LAYER GENMASK(19, 16) ++#define AIROHA_FOE_IB1_BIND_KEEPALIVE BIT(15) ++#define AIROHA_FOE_IB1_BIND_TIMESTAMP GENMASK(14, 0) ++ ++#define AIROHA_FOE_IB2_DSCP GENMASK(31, 24) ++#define AIROHA_FOE_IB2_PORT_AG GENMASK(23, 13) ++#define AIROHA_FOE_IB2_PCP BIT(12) ++#define AIROHA_FOE_IB2_MULTICAST BIT(11) ++#define AIROHA_FOE_IB2_FAST_PATH BIT(10) ++#define AIROHA_FOE_IB2_PSE_QOS BIT(9) ++#define AIROHA_FOE_IB2_PSE_PORT GENMASK(8, 5) ++#define AIROHA_FOE_IB2_NBQ GENMASK(4, 0) ++ ++#define AIROHA_FOE_ACTDP GENMASK(31, 24) ++#define AIROHA_FOE_SHAPER_ID GENMASK(23, 16) ++#define AIROHA_FOE_CHANNEL GENMASK(15, 11) ++#define AIROHA_FOE_QID GENMASK(10, 8) ++#define AIROHA_FOE_DPI BIT(7) ++#define AIROHA_FOE_TUNNEL BIT(6) ++#define AIROHA_FOE_TUNNEL_ID GENMASK(5, 0) ++ ++struct airoha_foe_bridge { ++ u32 dest_mac_hi; ++ ++ u16 src_mac_hi; ++ u16 dest_mac_lo; ++ ++ u32 src_mac_lo; ++ ++ u32 ib2; ++ ++ u32 rsv[5]; ++ ++ u32 data; ++ ++ struct airoha_foe_mac_info l2; ++}; ++ ++struct airoha_foe_ipv4_tuple { ++ u32 src_ip; ++ u32 dest_ip; ++ union { ++ struct { ++ u16 dest_port; ++ u16 src_port; ++ }; ++ struct { ++ u8 protocol; ++ u8 _pad[3]; /* fill with 0xa5a5a5 */ ++ }; ++ u32 ports; ++ }; ++}; ++ ++struct airoha_foe_ipv4 { ++ struct airoha_foe_ipv4_tuple orig_tuple; ++ ++ u32 ib2; ++ ++ struct airoha_foe_ipv4_tuple new_tuple; ++ ++ u32 rsv[2]; ++ ++ u32 data; ++ ++ struct airoha_foe_mac_info l2; ++}; ++ ++struct airoha_foe_ipv4_dslite { ++ struct airoha_foe_ipv4_tuple ip4; ++ ++ u32 ib2; ++ ++ u8 flow_label[3]; ++ u8 priority; ++ ++ u32 rsv[4]; ++ ++ u32 data; ++ ++ struct airoha_foe_mac_info l2; ++}; ++ ++struct airoha_foe_ipv6 { ++ u32 src_ip[4]; ++ u32 dest_ip[4]; ++ ++ union { ++ struct { ++ u16 dest_port; ++ u16 src_port; ++ }; ++ struct { ++ u8 protocol; ++ u8 pad[3]; ++ }; ++ u32 ports; ++ }; ++ ++ u32 data; ++ ++ u32 ib2; ++ ++ struct airoha_foe_mac_info_common l2; ++}; ++ ++struct airoha_foe_entry { ++ union { ++ struct { ++ u32 ib1; ++ union { ++ struct airoha_foe_bridge bridge; ++ struct airoha_foe_ipv4 ipv4; ++ struct airoha_foe_ipv4_dslite dslite; ++ struct airoha_foe_ipv6 ipv6; ++ DECLARE_FLEX_ARRAY(u32, d); ++ }; ++ }; ++ u8 data[PPE_ENTRY_SIZE]; ++ }; ++}; ++ ++struct airoha_flow_data { ++ struct ethhdr eth; ++ ++ union { ++ struct { ++ __be32 src_addr; ++ __be32 dst_addr; ++ } v4; ++ ++ struct { ++ struct in6_addr src_addr; ++ struct in6_addr dst_addr; ++ } v6; ++ }; ++ ++ __be16 src_port; ++ __be16 dst_port; ++ ++ struct { ++ struct { ++ u16 id; ++ __be16 proto; ++ } hdr[2]; ++ u8 num; ++ } vlan; ++ struct { ++ u16 sid; ++ u8 num; ++ } pppoe; ++}; ++ ++struct airoha_flow_table_entry { ++ struct hlist_node list; ++ ++ struct airoha_foe_entry data; ++ u32 hash; ++ ++ struct rhash_head node; ++ unsigned long cookie; ++}; ++ + struct airoha_qdma { + struct airoha_eth *eth; + void __iomem *regs; +@@ -234,6 +462,19 @@ struct airoha_gdm_port { + struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS]; + }; + ++#define AIROHA_RXD4_PPE_CPU_REASON GENMASK(20, 16) ++#define AIROHA_RXD4_FOE_ENTRY GENMASK(15, 0) ++ ++struct airoha_ppe { ++ struct airoha_eth *eth; ++ ++ void *foe; ++ dma_addr_t foe_dma; ++ ++ struct hlist_head *foe_flow; ++ u16 foe_check_time[PPE_NUM_ENTRIES]; ++}; ++ + struct airoha_eth { + struct device *dev; + +@@ -242,6 +483,9 @@ struct airoha_eth { + + struct airoha_npu __rcu *npu; + ++ struct airoha_ppe *ppe; ++ struct rhashtable flow_table; ++ + struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; + struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; + +@@ -277,4 +521,10 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + ++void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash); ++int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, ++ void *cb_priv); ++int airoha_ppe_init(struct airoha_eth *eth); ++void airoha_ppe_deinit(struct airoha_eth *eth); ++ + #endif /* AIROHA_ETH_H */ +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -0,0 +1,901 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2025 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "airoha_npu.h" ++#include "airoha_regs.h" ++#include "airoha_eth.h" ++ ++static DEFINE_MUTEX(flow_offload_mutex); ++static DEFINE_SPINLOCK(ppe_lock); ++ ++static const struct rhashtable_params airoha_flow_table_params = { ++ .head_offset = offsetof(struct airoha_flow_table_entry, node), ++ .key_offset = offsetof(struct airoha_flow_table_entry, cookie), ++ .key_len = sizeof(unsigned long), ++ .automatic_shrinking = true, ++}; ++ ++static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) ++{ ++ return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; ++} ++ ++static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) ++{ ++ u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS); ++ ++ return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); ++} ++ ++static void airoha_ppe_hw_init(struct airoha_ppe *ppe) ++{ ++ u32 sram_tb_size, sram_num_entries, dram_num_entries; ++ struct airoha_eth *eth = ppe->eth; ++ int i; ++ ++ sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry); ++ dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES); ++ ++ for (i = 0; i < PPE_NUM; i++) { ++ int p; ++ ++ airoha_fe_wr(eth, REG_PPE_TB_BASE(i), ++ ppe->foe_dma + sram_tb_size); ++ ++ airoha_fe_rmw(eth, REG_PPE_BND_AGE0(i), ++ PPE_BIND_AGE0_DELTA_NON_L4 | ++ PPE_BIND_AGE0_DELTA_UDP, ++ FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 1) | ++ FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 12)); ++ airoha_fe_rmw(eth, REG_PPE_BND_AGE1(i), ++ PPE_BIND_AGE1_DELTA_TCP_FIN | ++ PPE_BIND_AGE1_DELTA_TCP, ++ FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP_FIN, 1) | ++ FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 7)); ++ ++ airoha_fe_rmw(eth, REG_PPE_TB_HASH_CFG(i), ++ PPE_SRAM_TABLE_EN_MASK | ++ PPE_SRAM_HASH1_EN_MASK | ++ PPE_DRAM_TABLE_EN_MASK | ++ PPE_SRAM_HASH0_MODE_MASK | ++ PPE_SRAM_HASH1_MODE_MASK | ++ PPE_DRAM_HASH0_MODE_MASK | ++ PPE_DRAM_HASH1_MODE_MASK, ++ FIELD_PREP(PPE_SRAM_TABLE_EN_MASK, 1) | ++ FIELD_PREP(PPE_SRAM_HASH1_EN_MASK, 1) | ++ FIELD_PREP(PPE_SRAM_HASH1_MODE_MASK, 1) | ++ FIELD_PREP(PPE_DRAM_HASH1_MODE_MASK, 3)); ++ ++ airoha_fe_rmw(eth, REG_PPE_TB_CFG(i), ++ PPE_TB_CFG_SEARCH_MISS_MASK | ++ PPE_TB_ENTRY_SIZE_MASK, ++ FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) | ++ FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0)); ++ ++ airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); ++ ++ for (p = 0; p < ARRAY_SIZE(eth->ports); p++) ++ airoha_fe_rmw(eth, REG_PPE_MTU(i, p), ++ FP0_EGRESS_MTU_MASK | ++ FP1_EGRESS_MTU_MASK, ++ FIELD_PREP(FP0_EGRESS_MTU_MASK, ++ AIROHA_MAX_MTU) | ++ FIELD_PREP(FP1_EGRESS_MTU_MASK, ++ AIROHA_MAX_MTU)); ++ } ++ ++ if (airoha_ppe2_is_enabled(eth)) { ++ sram_num_entries = ++ PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_ENTRIES); ++ airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), ++ PPE_SRAM_TB_NUM_ENTRY_MASK | ++ PPE_DRAM_TB_NUM_ENTRY_MASK, ++ FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, ++ sram_num_entries) | ++ FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, ++ dram_num_entries)); ++ airoha_fe_rmw(eth, REG_PPE_TB_CFG(1), ++ PPE_SRAM_TB_NUM_ENTRY_MASK | ++ PPE_DRAM_TB_NUM_ENTRY_MASK, ++ FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, ++ sram_num_entries) | ++ FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, ++ dram_num_entries)); ++ } else { ++ sram_num_entries = ++ PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_ENTRIES); ++ airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), ++ PPE_SRAM_TB_NUM_ENTRY_MASK | ++ PPE_DRAM_TB_NUM_ENTRY_MASK, ++ FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, ++ sram_num_entries) | ++ FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, ++ dram_num_entries)); ++ } ++} ++ ++static void airoha_ppe_flow_mangle_eth(const struct flow_action_entry *act, void *eth) ++{ ++ void *dest = eth + act->mangle.offset; ++ const void *src = &act->mangle.val; ++ ++ if (act->mangle.offset > 8) ++ return; ++ ++ if (act->mangle.mask == 0xffff) { ++ src += 2; ++ dest += 2; ++ } ++ ++ memcpy(dest, src, act->mangle.mask ? 2 : 4); ++} ++ ++static int airoha_ppe_flow_mangle_ports(const struct flow_action_entry *act, ++ struct airoha_flow_data *data) ++{ ++ u32 val = be32_to_cpu((__force __be32)act->mangle.val); ++ ++ switch (act->mangle.offset) { ++ case 0: ++ if ((__force __be32)act->mangle.mask == ~cpu_to_be32(0xffff)) ++ data->dst_port = cpu_to_be16(val); ++ else ++ data->src_port = cpu_to_be16(val >> 16); ++ break; ++ case 2: ++ data->dst_port = cpu_to_be16(val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int airoha_ppe_flow_mangle_ipv4(const struct flow_action_entry *act, ++ struct airoha_flow_data *data) ++{ ++ __be32 *dest; ++ ++ switch (act->mangle.offset) { ++ case offsetof(struct iphdr, saddr): ++ dest = &data->v4.src_addr; ++ break; ++ case offsetof(struct iphdr, daddr): ++ dest = &data->v4.dst_addr; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ memcpy(dest, &act->mangle.val, sizeof(u32)); ++ ++ return 0; ++} ++ ++static int airoha_get_dsa_port(struct net_device **dev) ++{ ++#if IS_ENABLED(CONFIG_NET_DSA) ++ struct dsa_port *dp = dsa_port_from_netdev(*dev); ++ ++ if (IS_ERR(dp)) ++ return -ENODEV; ++ ++ *dev = dsa_port_to_conduit(dp); ++ return dp->index; ++#else ++ return -ENODEV; ++#endif ++} ++ ++static int airoha_ppe_foe_entry_prepare(struct airoha_foe_entry *hwe, ++ struct net_device *dev, int type, ++ struct airoha_flow_data *data, ++ int l4proto) ++{ ++ int dsa_port = airoha_get_dsa_port(&dev); ++ struct airoha_foe_mac_info_common *l2; ++ u32 qdata, ports_pad, val; ++ ++ memset(hwe, 0, sizeof(*hwe)); ++ ++ val = FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, AIROHA_FOE_STATE_BIND) | ++ FIELD_PREP(AIROHA_FOE_IB1_BIND_PACKET_TYPE, type) | ++ FIELD_PREP(AIROHA_FOE_IB1_BIND_UDP, l4proto == IPPROTO_UDP) | ++ FIELD_PREP(AIROHA_FOE_IB1_BIND_VLAN_LAYER, data->vlan.num) | ++ FIELD_PREP(AIROHA_FOE_IB1_BIND_VPM, data->vlan.num) | ++ AIROHA_FOE_IB1_BIND_TTL; ++ hwe->ib1 = val; ++ ++ val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f); ++ if (dsa_port >= 0) ++ val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port); ++ ++ if (dev) { ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u8 pse_port; ++ ++ pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; ++ val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); ++ } ++ ++ /* FIXME: implement QoS support setting pse_port to 2 (loopback) ++ * for uplink and setting qos bit in ib2 ++ */ ++ ++ if (is_multicast_ether_addr(data->eth.h_dest)) ++ val |= AIROHA_FOE_IB2_MULTICAST; ++ ++ ports_pad = 0xa5a5a500 | (l4proto & 0xff); ++ if (type == PPE_PKT_TYPE_IPV4_ROUTE) ++ hwe->ipv4.orig_tuple.ports = ports_pad; ++ if (type == PPE_PKT_TYPE_IPV6_ROUTE_3T) ++ hwe->ipv6.ports = ports_pad; ++ ++ qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); ++ if (type == PPE_PKT_TYPE_BRIDGE) { ++ hwe->bridge.dest_mac_hi = get_unaligned_be32(data->eth.h_dest); ++ hwe->bridge.dest_mac_lo = ++ get_unaligned_be16(data->eth.h_dest + 4); ++ hwe->bridge.src_mac_hi = ++ get_unaligned_be16(data->eth.h_source); ++ hwe->bridge.src_mac_lo = ++ get_unaligned_be32(data->eth.h_source + 2); ++ hwe->bridge.data = qdata; ++ hwe->bridge.ib2 = val; ++ l2 = &hwe->bridge.l2.common; ++ } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ hwe->ipv6.data = qdata; ++ hwe->ipv6.ib2 = val; ++ l2 = &hwe->ipv6.l2; ++ } else { ++ hwe->ipv4.data = qdata; ++ hwe->ipv4.ib2 = val; ++ l2 = &hwe->ipv4.l2.common; ++ } ++ ++ l2->dest_mac_hi = get_unaligned_be32(data->eth.h_dest); ++ l2->dest_mac_lo = get_unaligned_be16(data->eth.h_dest + 4); ++ if (type <= PPE_PKT_TYPE_IPV4_DSLITE) { ++ l2->src_mac_hi = get_unaligned_be32(data->eth.h_source); ++ hwe->ipv4.l2.src_mac_lo = ++ get_unaligned_be16(data->eth.h_source + 4); ++ } else { ++ l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, 0xf); ++ } ++ ++ if (data->vlan.num) { ++ l2->etype = dsa_port >= 0 ? BIT(dsa_port) : 0; ++ l2->vlan1 = data->vlan.hdr[0].id; ++ if (data->vlan.num == 2) ++ l2->vlan2 = data->vlan.hdr[1].id; ++ } else if (dsa_port >= 0) { ++ l2->etype = BIT(15) | BIT(dsa_port); ++ } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ l2->etype = ETH_P_IPV6; ++ } else { ++ l2->etype = ETH_P_IP; ++ } ++ ++ return 0; ++} ++ ++static int airoha_ppe_foe_entry_set_ipv4_tuple(struct airoha_foe_entry *hwe, ++ struct airoha_flow_data *data, ++ bool egress) ++{ ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ struct airoha_foe_ipv4_tuple *t; ++ ++ switch (type) { ++ case PPE_PKT_TYPE_IPV4_HNAPT: ++ if (egress) { ++ t = &hwe->ipv4.new_tuple; ++ break; ++ } ++ fallthrough; ++ case PPE_PKT_TYPE_IPV4_DSLITE: ++ case PPE_PKT_TYPE_IPV4_ROUTE: ++ t = &hwe->ipv4.orig_tuple; ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return -EINVAL; ++ } ++ ++ t->src_ip = be32_to_cpu(data->v4.src_addr); ++ t->dest_ip = be32_to_cpu(data->v4.dst_addr); ++ ++ if (type != PPE_PKT_TYPE_IPV4_ROUTE) { ++ t->src_port = be16_to_cpu(data->src_port); ++ t->dest_port = be16_to_cpu(data->dst_port); ++ } ++ ++ return 0; ++} ++ ++static int airoha_ppe_foe_entry_set_ipv6_tuple(struct airoha_foe_entry *hwe, ++ struct airoha_flow_data *data) ++ ++{ ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ u32 *src, *dest; ++ ++ switch (type) { ++ case PPE_PKT_TYPE_IPV6_ROUTE_5T: ++ case PPE_PKT_TYPE_IPV6_6RD: ++ hwe->ipv6.src_port = be16_to_cpu(data->src_port); ++ hwe->ipv6.dest_port = be16_to_cpu(data->dst_port); ++ fallthrough; ++ case PPE_PKT_TYPE_IPV6_ROUTE_3T: ++ src = hwe->ipv6.src_ip; ++ dest = hwe->ipv6.dest_ip; ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return -EINVAL; ++ } ++ ++ ipv6_addr_be32_to_cpu(src, data->v6.src_addr.s6_addr32); ++ ipv6_addr_be32_to_cpu(dest, data->v6.dst_addr.s6_addr32); ++ ++ return 0; ++} ++ ++static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe) ++{ ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ u32 hash, hv1, hv2, hv3; ++ ++ switch (type) { ++ case PPE_PKT_TYPE_IPV4_ROUTE: ++ case PPE_PKT_TYPE_IPV4_HNAPT: ++ hv1 = hwe->ipv4.orig_tuple.ports; ++ hv2 = hwe->ipv4.orig_tuple.dest_ip; ++ hv3 = hwe->ipv4.orig_tuple.src_ip; ++ break; ++ case PPE_PKT_TYPE_IPV6_ROUTE_3T: ++ case PPE_PKT_TYPE_IPV6_ROUTE_5T: ++ hv1 = hwe->ipv6.src_ip[3] ^ hwe->ipv6.dest_ip[3]; ++ hv1 ^= hwe->ipv6.ports; ++ ++ hv2 = hwe->ipv6.src_ip[2] ^ hwe->ipv6.dest_ip[2]; ++ hv2 ^= hwe->ipv6.dest_ip[0]; ++ ++ hv3 = hwe->ipv6.src_ip[1] ^ hwe->ipv6.dest_ip[1]; ++ hv3 ^= hwe->ipv6.src_ip[0]; ++ break; ++ case PPE_PKT_TYPE_IPV4_DSLITE: ++ case PPE_PKT_TYPE_IPV6_6RD: ++ default: ++ WARN_ON_ONCE(1); ++ return PPE_HASH_MASK; ++ } ++ ++ hash = (hv1 & hv2) | ((~hv1) & hv3); ++ hash = (hash >> 24) | ((hash & 0xffffff) << 8); ++ hash ^= hv1 ^ hv2 ^ hv3; ++ hash ^= hash >> 16; ++ hash &= PPE_NUM_ENTRIES - 1; ++ ++ return hash; ++} ++ ++static struct airoha_foe_entry * ++airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash) ++{ ++ if (hash < PPE_SRAM_NUM_ENTRIES) { ++ u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); ++ struct airoha_eth *eth = ppe->eth; ++ bool ppe2; ++ u32 val; ++ int i; ++ ++ ppe2 = airoha_ppe2_is_enabled(ppe->eth) && ++ hash >= PPE1_SRAM_NUM_ENTRIES; ++ airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2), ++ FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) | ++ PPE_SRAM_CTRL_REQ_MASK); ++ if (read_poll_timeout_atomic(airoha_fe_rr, val, ++ val & PPE_SRAM_CTRL_ACK_MASK, ++ 10, 100, false, eth, ++ REG_PPE_RAM_CTRL(ppe2))) ++ return NULL; ++ ++ for (i = 0; i < sizeof(struct airoha_foe_entry) / 4; i++) ++ hwe[i] = airoha_fe_rr(eth, ++ REG_PPE_RAM_ENTRY(ppe2, i)); ++ } ++ ++ return ppe->foe + hash * sizeof(struct airoha_foe_entry); ++} ++ ++static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e, ++ struct airoha_foe_entry *hwe) ++{ ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1); ++ int len; ++ ++ if ((hwe->ib1 ^ e->data.ib1) & AIROHA_FOE_IB1_BIND_UDP) ++ return false; ++ ++ if (type > PPE_PKT_TYPE_IPV4_DSLITE) ++ len = offsetof(struct airoha_foe_entry, ipv6.data); ++ else ++ len = offsetof(struct airoha_foe_entry, ipv4.ib2); ++ ++ return !memcmp(&e->data.d, &hwe->d, len - sizeof(hwe->ib1)); ++} ++ ++static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe, ++ struct airoha_foe_entry *e, ++ u32 hash) ++{ ++ struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); ++ u32 ts = airoha_ppe_get_timestamp(ppe); ++ struct airoha_eth *eth = ppe->eth; ++ ++ memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1)); ++ wmb(); ++ ++ e->ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts); ++ hwe->ib1 = e->ib1; ++ ++ if (hash < PPE_SRAM_NUM_ENTRIES) { ++ dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); ++ bool ppe2 = airoha_ppe2_is_enabled(eth) && ++ hash >= PPE1_SRAM_NUM_ENTRIES; ++ struct airoha_npu *npu; ++ int err = -ENODEV; ++ ++ rcu_read_lock(); ++ npu = rcu_dereference(eth->npu); ++ if (npu) ++ err = npu->ops.ppe_foe_commit_entry(npu, addr, ++ sizeof(*hwe), hash, ++ ppe2); ++ rcu_read_unlock(); ++ ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash) ++{ ++ struct airoha_flow_table_entry *e; ++ struct airoha_foe_entry *hwe; ++ struct hlist_node *n; ++ u32 index, state; ++ ++ spin_lock_bh(&ppe_lock); ++ ++ hwe = airoha_ppe_foe_get_entry(ppe, hash); ++ if (!hwe) ++ goto unlock; ++ ++ state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); ++ if (state == AIROHA_FOE_STATE_BIND) ++ goto unlock; ++ ++ index = airoha_ppe_foe_get_entry_hash(hwe); ++ hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) { ++ if (airoha_ppe_foe_compare_entry(e, hwe)) { ++ airoha_ppe_foe_commit_entry(ppe, &e->data, hash); ++ e->hash = hash; ++ break; ++ } ++ } ++unlock: ++ spin_unlock_bh(&ppe_lock); ++} ++ ++static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ u32 hash = airoha_ppe_foe_get_entry_hash(&e->data); ++ ++ e->hash = 0xffff; ++ ++ spin_lock_bh(&ppe_lock); ++ hlist_add_head(&e->list, &ppe->foe_flow[hash]); ++ spin_unlock_bh(&ppe_lock); ++ ++ return 0; ++} ++ ++static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ spin_lock_bh(&ppe_lock); ++ ++ hlist_del_init(&e->list); ++ if (e->hash != 0xffff) { ++ e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; ++ e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, ++ AIROHA_FOE_STATE_INVALID); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); ++ e->hash = 0xffff; ++ } ++ ++ spin_unlock_bh(&ppe_lock); ++} ++ ++static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, ++ struct flow_cls_offload *f) ++{ ++ struct flow_rule *rule = flow_cls_offload_flow_rule(f); ++ struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_flow_table_entry *e; ++ struct airoha_flow_data data = {}; ++ struct net_device *odev = NULL; ++ struct flow_action_entry *act; ++ struct airoha_foe_entry hwe; ++ int err, i, offload_type; ++ u16 addr_type = 0; ++ u8 l4proto = 0; ++ ++ if (rhashtable_lookup(ð->flow_table, &f->cookie, ++ airoha_flow_table_params)) ++ return -EEXIST; ++ ++ if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) ++ return -EOPNOTSUPP; ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { ++ struct flow_match_control match; ++ ++ flow_rule_match_control(rule, &match); ++ addr_type = match.key->addr_type; ++ if (flow_rule_has_control_flags(match.mask->flags, ++ f->common.extack)) ++ return -EOPNOTSUPP; ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { ++ struct flow_match_basic match; ++ ++ flow_rule_match_basic(rule, &match); ++ l4proto = match.key->ip_proto; ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ switch (addr_type) { ++ case 0: ++ offload_type = PPE_PKT_TYPE_BRIDGE; ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { ++ struct flow_match_eth_addrs match; ++ ++ flow_rule_match_eth_addrs(rule, &match); ++ memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN); ++ memcpy(data.eth.h_source, match.key->src, ETH_ALEN); ++ } else { ++ return -EOPNOTSUPP; ++ } ++ break; ++ case FLOW_DISSECTOR_KEY_IPV4_ADDRS: ++ offload_type = PPE_PKT_TYPE_IPV4_HNAPT; ++ break; ++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS: ++ offload_type = PPE_PKT_TYPE_IPV6_ROUTE_5T; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ flow_action_for_each(i, act, &rule->action) { ++ switch (act->id) { ++ case FLOW_ACTION_MANGLE: ++ if (offload_type == PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ ++ if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH) ++ airoha_ppe_flow_mangle_eth(act, &data.eth); ++ break; ++ case FLOW_ACTION_REDIRECT: ++ odev = act->dev; ++ break; ++ case FLOW_ACTION_CSUM: ++ break; ++ case FLOW_ACTION_VLAN_PUSH: ++ if (data.vlan.num == 2 || ++ act->vlan.proto != htons(ETH_P_8021Q)) ++ return -EOPNOTSUPP; ++ ++ data.vlan.hdr[data.vlan.num].id = act->vlan.vid; ++ data.vlan.hdr[data.vlan.num].proto = act->vlan.proto; ++ data.vlan.num++; ++ break; ++ case FLOW_ACTION_VLAN_POP: ++ break; ++ case FLOW_ACTION_PPPOE_PUSH: ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ if (!is_valid_ether_addr(data.eth.h_source) || ++ !is_valid_ether_addr(data.eth.h_dest)) ++ return -EINVAL; ++ ++ err = airoha_ppe_foe_entry_prepare(&hwe, odev, offload_type, ++ &data, l4proto); ++ if (err) ++ return err; ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { ++ struct flow_match_ports ports; ++ ++ if (offload_type == PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ ++ flow_rule_match_ports(rule, &ports); ++ data.src_port = ports.key->src; ++ data.dst_port = ports.key->dst; ++ } else if (offload_type != PPE_PKT_TYPE_BRIDGE) { ++ return -EOPNOTSUPP; ++ } ++ ++ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { ++ struct flow_match_ipv4_addrs addrs; ++ ++ flow_rule_match_ipv4_addrs(rule, &addrs); ++ data.v4.src_addr = addrs.key->src; ++ data.v4.dst_addr = addrs.key->dst; ++ airoha_ppe_foe_entry_set_ipv4_tuple(&hwe, &data, false); ++ } ++ ++ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { ++ struct flow_match_ipv6_addrs addrs; ++ ++ flow_rule_match_ipv6_addrs(rule, &addrs); ++ ++ data.v6.src_addr = addrs.key->src; ++ data.v6.dst_addr = addrs.key->dst; ++ airoha_ppe_foe_entry_set_ipv6_tuple(&hwe, &data); ++ } ++ ++ flow_action_for_each(i, act, &rule->action) { ++ if (act->id != FLOW_ACTION_MANGLE) ++ continue; ++ ++ if (offload_type == PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ ++ switch (act->mangle.htype) { ++ case FLOW_ACT_MANGLE_HDR_TYPE_TCP: ++ case FLOW_ACT_MANGLE_HDR_TYPE_UDP: ++ err = airoha_ppe_flow_mangle_ports(act, &data); ++ break; ++ case FLOW_ACT_MANGLE_HDR_TYPE_IP4: ++ err = airoha_ppe_flow_mangle_ipv4(act, &data); ++ break; ++ case FLOW_ACT_MANGLE_HDR_TYPE_ETH: ++ /* handled earlier */ ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ if (err) ++ return err; ++ } ++ ++ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { ++ err = airoha_ppe_foe_entry_set_ipv4_tuple(&hwe, &data, true); ++ if (err) ++ return err; ++ } ++ ++ e = kzalloc(sizeof(*e), GFP_KERNEL); ++ if (!e) ++ return -ENOMEM; ++ ++ e->cookie = f->cookie; ++ memcpy(&e->data, &hwe, sizeof(e->data)); ++ ++ err = airoha_ppe_foe_flow_commit_entry(eth->ppe, e); ++ if (err) ++ goto free_entry; ++ ++ err = rhashtable_insert_fast(ð->flow_table, &e->node, ++ airoha_flow_table_params); ++ if (err < 0) ++ goto remove_foe_entry; ++ ++ return 0; ++ ++remove_foe_entry: ++ airoha_ppe_foe_flow_remove_entry(eth->ppe, e); ++free_entry: ++ kfree(e); ++ ++ return err; ++} ++ ++static int airoha_ppe_flow_offload_destroy(struct airoha_gdm_port *port, ++ struct flow_cls_offload *f) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_flow_table_entry *e; ++ ++ e = rhashtable_lookup(ð->flow_table, &f->cookie, ++ airoha_flow_table_params); ++ if (!e) ++ return -ENOENT; ++ ++ airoha_ppe_foe_flow_remove_entry(eth->ppe, e); ++ rhashtable_remove_fast(ð->flow_table, &e->node, ++ airoha_flow_table_params); ++ kfree(e); ++ ++ return 0; ++} ++ ++static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, ++ struct flow_cls_offload *f) ++{ ++ switch (f->command) { ++ case FLOW_CLS_REPLACE: ++ return airoha_ppe_flow_offload_replace(port, f); ++ case FLOW_CLS_DESTROY: ++ return airoha_ppe_flow_offload_destroy(port, f); ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe, ++ struct airoha_npu *npu) ++{ ++ int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES; ++ struct airoha_foe_entry *hwe = ppe->foe; ++ ++ if (airoha_ppe2_is_enabled(ppe->eth)) ++ sram_num_entries = sram_num_entries / 2; ++ ++ for (i = 0; i < sram_num_entries; i++) ++ memset(&hwe[i], 0, sizeof(*hwe)); ++ ++ return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma, ++ PPE_SRAM_NUM_ENTRIES); ++} ++ ++static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) ++{ ++ struct airoha_npu *npu = airoha_npu_get(eth->dev); ++ ++ if (IS_ERR(npu)) { ++ request_module("airoha-npu"); ++ npu = airoha_npu_get(eth->dev); ++ } ++ ++ return npu; ++} ++ ++static int airoha_ppe_offload_setup(struct airoha_eth *eth) ++{ ++ struct airoha_npu *npu = airoha_ppe_npu_get(eth); ++ int err; ++ ++ if (IS_ERR(npu)) ++ return PTR_ERR(npu); ++ ++ err = npu->ops.ppe_init(npu); ++ if (err) ++ goto error_npu_put; ++ ++ airoha_ppe_hw_init(eth->ppe); ++ err = airoha_ppe_flush_sram_entries(eth->ppe, npu); ++ if (err) ++ goto error_npu_put; ++ ++ rcu_assign_pointer(eth->npu, npu); ++ synchronize_rcu(); ++ ++ return 0; ++ ++error_npu_put: ++ airoha_npu_put(npu); ++ ++ return err; ++} ++ ++int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, ++ void *cb_priv) ++{ ++ struct flow_cls_offload *cls = type_data; ++ struct net_device *dev = cb_priv; ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; ++ int err = 0; ++ ++ if (!tc_can_offload(dev) || type != TC_SETUP_CLSFLOWER) ++ return -EOPNOTSUPP; ++ ++ mutex_lock(&flow_offload_mutex); ++ ++ if (!eth->npu) ++ err = airoha_ppe_offload_setup(eth); ++ if (!err) ++ err = airoha_ppe_flow_offload_cmd(port, cls); ++ ++ mutex_unlock(&flow_offload_mutex); ++ ++ return err; ++} ++ ++void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash) ++{ ++ u16 now, diff; ++ ++ if (hash > PPE_HASH_MASK) ++ return; ++ ++ now = (u16)jiffies; ++ diff = now - ppe->foe_check_time[hash]; ++ if (diff < HZ / 10) ++ return; ++ ++ ppe->foe_check_time[hash] = now; ++ airoha_ppe_foe_insert_entry(ppe, hash); ++} ++ ++int airoha_ppe_init(struct airoha_eth *eth) ++{ ++ struct airoha_ppe *ppe; ++ int foe_size; ++ ++ ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL); ++ if (!ppe) ++ return -ENOMEM; ++ ++ foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); ++ ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma, ++ GFP_KERNEL); ++ if (!ppe->foe) ++ return -ENOMEM; ++ ++ ppe->eth = eth; ++ eth->ppe = ppe; ++ ++ ppe->foe_flow = devm_kzalloc(eth->dev, ++ PPE_NUM_ENTRIES * sizeof(*ppe->foe_flow), ++ GFP_KERNEL); ++ if (!ppe->foe_flow) ++ return -ENOMEM; ++ ++ return rhashtable_init(ð->flow_table, &airoha_flow_table_params); ++} ++ ++void airoha_ppe_deinit(struct airoha_eth *eth) ++{ ++ struct airoha_npu *npu; ++ ++ rcu_read_lock(); ++ npu = rcu_dereference(eth->npu); ++ if (npu) { ++ npu->ops.ppe_deinit(npu); ++ airoha_npu_put(npu); ++ } ++ rcu_read_unlock(); ++ ++ rhashtable_destroy(ð->flow_table); ++} +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -15,6 +15,7 @@ + #define CDM1_BASE 0x0400 + #define GDM1_BASE 0x0500 + #define PPE1_BASE 0x0c00 ++#define PPE2_BASE 0x1c00 + + #define CDM2_BASE 0x1400 + #define GDM2_BASE 0x1500 +@@ -36,6 +37,7 @@ + #define FE_RST_GDM3_MBI_ARB_MASK BIT(2) + #define FE_RST_CORE_MASK BIT(0) + ++#define REG_FE_FOE_TS 0x0010 + #define REG_FE_WAN_MAC_H 0x0030 + #define REG_FE_LAN_MAC_H 0x0040 + +@@ -192,11 +194,106 @@ + #define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) + #define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) + +-#define REG_PPE1_TB_HASH_CFG (PPE1_BASE + 0x250) +-#define PPE1_SRAM_TABLE_EN_MASK BIT(0) +-#define PPE1_SRAM_HASH1_EN_MASK BIT(8) +-#define PPE1_DRAM_TABLE_EN_MASK BIT(16) +-#define PPE1_DRAM_HASH1_EN_MASK BIT(24) ++#define REG_PPE_GLO_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x200) ++#define PPE_GLO_CFG_BUSY_MASK BIT(31) ++#define PPE_GLO_CFG_FLOW_DROP_UPDATE_MASK BIT(9) ++#define PPE_GLO_CFG_PSE_HASH_OFS_MASK BIT(6) ++#define PPE_GLO_CFG_PPE_BSWAP_MASK BIT(5) ++#define PPE_GLO_CFG_TTL_DROP_MASK BIT(4) ++#define PPE_GLO_CFG_IP4_CS_DROP_MASK BIT(3) ++#define PPE_GLO_CFG_IP4_L4_CS_DROP_MASK BIT(2) ++#define PPE_GLO_CFG_EN_MASK BIT(0) ++ ++#define REG_PPE_PPE_FLOW_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x204) ++#define PPE_FLOW_CFG_IP6_HASH_GRE_KEY_MASK BIT(20) ++#define PPE_FLOW_CFG_IP4_HASH_GRE_KEY_MASK BIT(19) ++#define PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL_MASK BIT(18) ++#define PPE_FLOW_CFG_IP4_NAT_FRAG_MASK BIT(17) ++#define PPE_FLOW_CFG_IP_PROTO_BLACKLIST_MASK BIT(16) ++#define PPE_FLOW_CFG_IP4_DSLITE_MASK BIT(14) ++#define PPE_FLOW_CFG_IP4_NAPT_MASK BIT(13) ++#define PPE_FLOW_CFG_IP4_NAT_MASK BIT(12) ++#define PPE_FLOW_CFG_IP6_6RD_MASK BIT(10) ++#define PPE_FLOW_CFG_IP6_5T_ROUTE_MASK BIT(9) ++#define PPE_FLOW_CFG_IP6_3T_ROUTE_MASK BIT(8) ++#define PPE_FLOW_CFG_IP4_UDP_FRAG_MASK BIT(7) ++#define PPE_FLOW_CFG_IP4_TCP_FRAG_MASK BIT(6) ++ ++#define REG_PPE_IP_PROTO_CHK(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x208) ++#define PPE_IP_PROTO_CHK_IPV4_MASK GENMASK(15, 0) ++#define PPE_IP_PROTO_CHK_IPV6_MASK GENMASK(31, 16) ++ ++#define REG_PPE_TB_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x21c) ++#define PPE_SRAM_TB_NUM_ENTRY_MASK GENMASK(26, 24) ++#define PPE_TB_CFG_KEEPALIVE_MASK GENMASK(13, 12) ++#define PPE_TB_CFG_AGE_TCP_FIN_MASK BIT(11) ++#define PPE_TB_CFG_AGE_UDP_MASK BIT(10) ++#define PPE_TB_CFG_AGE_TCP_MASK BIT(9) ++#define PPE_TB_CFG_AGE_UNBIND_MASK BIT(8) ++#define PPE_TB_CFG_AGE_NON_L4_MASK BIT(7) ++#define PPE_TB_CFG_AGE_PREBIND_MASK BIT(6) ++#define PPE_TB_CFG_SEARCH_MISS_MASK GENMASK(5, 4) ++#define PPE_TB_ENTRY_SIZE_MASK BIT(3) ++#define PPE_DRAM_TB_NUM_ENTRY_MASK GENMASK(2, 0) ++ ++#define REG_PPE_TB_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x220) ++ ++#define REG_PPE_BIND_RATE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x228) ++#define PPE_BIND_RATE_L2B_BIND_MASK GENMASK(31, 16) ++#define PPE_BIND_RATE_BIND_MASK GENMASK(15, 0) ++ ++#define REG_PPE_BIND_LIMIT0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x22c) ++#define PPE_BIND_LIMIT0_HALF_MASK GENMASK(29, 16) ++#define PPE_BIND_LIMIT0_QUARTER_MASK GENMASK(13, 0) ++ ++#define REG_PPE_BIND_LIMIT1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x230) ++#define PPE_BIND_LIMIT1_NON_L4_MASK GENMASK(23, 16) ++#define PPE_BIND_LIMIT1_FULL_MASK GENMASK(13, 0) ++ ++#define REG_PPE_BND_AGE0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x23c) ++#define PPE_BIND_AGE0_DELTA_NON_L4 GENMASK(30, 16) ++#define PPE_BIND_AGE0_DELTA_UDP GENMASK(14, 0) ++ ++#define REG_PPE_UNBIND_AGE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x238) ++#define PPE_UNBIND_AGE_MIN_PACKETS_MASK GENMASK(31, 16) ++#define PPE_UNBIND_AGE_DELTA_MASK GENMASK(7, 0) ++ ++#define REG_PPE_BND_AGE1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x240) ++#define PPE_BIND_AGE1_DELTA_TCP_FIN GENMASK(30, 16) ++#define PPE_BIND_AGE1_DELTA_TCP GENMASK(14, 0) ++ ++#define REG_PPE_HASH_SEED(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x244) ++#define PPE_HASH_SEED 0x12345678 ++ ++#define REG_PPE_DFT_CPORT0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248) ++ ++#define REG_PPE_DFT_CPORT1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x24c) ++ ++#define REG_PPE_TB_HASH_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x250) ++#define PPE_DRAM_HASH1_MODE_MASK GENMASK(31, 28) ++#define PPE_DRAM_HASH1_EN_MASK BIT(24) ++#define PPE_DRAM_HASH0_MODE_MASK GENMASK(23, 20) ++#define PPE_DRAM_TABLE_EN_MASK BIT(16) ++#define PPE_SRAM_HASH1_MODE_MASK GENMASK(15, 12) ++#define PPE_SRAM_HASH1_EN_MASK BIT(8) ++#define PPE_SRAM_HASH0_MODE_MASK GENMASK(7, 4) ++#define PPE_SRAM_TABLE_EN_MASK BIT(0) ++ ++#define REG_PPE_MTU_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304) ++#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + ((_n) << 2)) ++#define FP1_EGRESS_MTU_MASK GENMASK(29, 16) ++#define FP0_EGRESS_MTU_MASK GENMASK(13, 0) ++ ++#define REG_PPE_RAM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c) ++#define PPE_SRAM_CTRL_ACK_MASK BIT(31) ++#define PPE_SRAM_CTRL_DUAL_SUCESS_MASK BIT(30) ++#define PPE_SRAM_CTRL_ENTRY_MASK GENMASK(23, 8) ++#define PPE_SRAM_WR_DUAL_DIRECTION_MASK BIT(2) ++#define PPE_SRAM_CTRL_WR_MASK BIT(1) ++#define PPE_SRAM_CTRL_REQ_MASK BIT(0) ++ ++#define REG_PPE_RAM_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x320) ++#define REG_PPE_RAM_ENTRY(_m, _n) (REG_PPE_RAM_BASE(_m) + ((_n) << 2)) + + #define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) + #define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) diff --git a/target/linux/airoha/patches-6.12/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch b/target/linux/airoha/patches-6.12/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch new file mode 100644 index 00000000000..173900380f9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch @@ -0,0 +1,210 @@ +From 9cd451d414f6e29f507a216fb3b19fa68c011f8c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:22 +0100 +Subject: [PATCH 14/15] net: airoha: Add loopback support for GDM2 + +Enable hw redirection for traffic received on GDM2 port to GDM{3,4}. +This is required to apply Qdisc offloading (HTB or ETS) for traffic to +and from GDM{3,4} port. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 71 ++++++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_eth.h | 7 +++ + drivers/net/ethernet/airoha/airoha_ppe.c | 12 ++-- + drivers/net/ethernet/airoha/airoha_regs.h | 29 +++++++++ + 4 files changed, 111 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1609,14 +1609,81 @@ static int airoha_dev_set_macaddr(struct + return 0; + } + ++static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) ++{ ++ u32 pse_port = port->id == 3 ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4; ++ struct airoha_eth *eth = port->qdma->eth; ++ u32 chan = port->id == 3 ? 4 : 0; ++ ++ /* Forward the traffic to the proper GDM port */ ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port); ++ airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC); ++ ++ /* Enable GDM2 loopback */ ++ airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff); ++ airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff); ++ airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2), ++ LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK, ++ FIELD_PREP(LPBK_CHAN_MASK, chan) | LPBK_EN_MASK); ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(2), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); ++ ++ /* Disable VIP and IFC for GDM2 */ ++ airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2)); ++ airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2)); ++ ++ if (port->id == 3) { ++ /* FIXME: handle XSI_PCE1_PORT */ ++ airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), 0x5500); ++ airoha_fe_rmw(eth, REG_FE_WAN_PORT, ++ WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, ++ FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT)); ++ airoha_fe_rmw(eth, ++ REG_SP_DFT_CPORT(HSGMII_LAN_PCIE0_SRCPORT >> 3), ++ SP_CPORT_PCIE0_MASK, ++ FIELD_PREP(SP_CPORT_PCIE0_MASK, ++ FE_PSE_PORT_CDM2)); ++ } else { ++ /* FIXME: handle XSI_USB_PORT */ ++ airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, ++ FC_ID_OF_SRC_PORT24_MASK, ++ FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2)); ++ airoha_fe_rmw(eth, REG_FE_WAN_PORT, ++ WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, ++ FIELD_PREP(WAN0_MASK, HSGMII_LAN_ETH_SRCPORT)); ++ airoha_fe_rmw(eth, ++ REG_SP_DFT_CPORT(HSGMII_LAN_ETH_SRCPORT >> 3), ++ SP_CPORT_ETH_MASK, ++ FIELD_PREP(SP_CPORT_ETH_MASK, FE_PSE_PORT_CDM2)); ++ } ++} ++ + static int airoha_dev_init(struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_eth *eth = port->qdma->eth; ++ u32 pse_port; + + airoha_set_macaddr(port, dev->dev_addr); +- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), +- FE_PSE_PORT_PPE1); ++ ++ switch (port->id) { ++ case 3: ++ case 4: ++ /* If GDM2 is active we can't enable loopback */ ++ if (!eth->ports[1]) ++ airhoha_set_gdm2_loopback(port); ++ fallthrough; ++ case 2: ++ pse_port = FE_PSE_PORT_PPE2; ++ break; ++ default: ++ pse_port = FE_PSE_PORT_PPE1; ++ break; ++ } ++ ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port); + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -68,6 +68,13 @@ enum { + }; + + enum { ++ HSGMII_LAN_PCIE0_SRCPORT = 0x16, ++ HSGMII_LAN_PCIE1_SRCPORT, ++ HSGMII_LAN_ETH_SRCPORT, ++ HSGMII_LAN_USB_SRCPORT, ++}; ++ ++enum { + XSI_PCIE0_VIP_PORT_MASK = BIT(22), + XSI_PCIE1_VIP_PORT_MASK = BIT(23), + XSI_USB_VIP_PORT_MASK = BIT(25), +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -216,7 +216,8 @@ static int airoha_ppe_foe_entry_prepare( + AIROHA_FOE_IB1_BIND_TTL; + hwe->ib1 = val; + +- val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f); ++ val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f) | ++ AIROHA_FOE_IB2_PSE_QOS; + if (dsa_port >= 0) + val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port); + +@@ -224,14 +225,13 @@ static int airoha_ppe_foe_entry_prepare( + struct airoha_gdm_port *port = netdev_priv(dev); + u8 pse_port; + +- pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; ++ if (dsa_port >= 0) ++ pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; ++ else ++ pse_port = 2; /* uplink relies on GDM2 loopback */ + val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); + } + +- /* FIXME: implement QoS support setting pse_port to 2 (loopback) +- * for uplink and setting qos bit in ib2 +- */ +- + if (is_multicast_ether_addr(data->eth.h_dest)) + val |= AIROHA_FOE_IB2_MULTICAST; + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -38,6 +38,12 @@ + #define FE_RST_CORE_MASK BIT(0) + + #define REG_FE_FOE_TS 0x0010 ++ ++#define REG_FE_WAN_PORT 0x0024 ++#define WAN1_EN_MASK BIT(16) ++#define WAN1_MASK GENMASK(12, 8) ++#define WAN0_MASK GENMASK(4, 0) ++ + #define REG_FE_WAN_MAC_H 0x0030 + #define REG_FE_LAN_MAC_H 0x0040 + +@@ -126,6 +132,7 @@ + #define GDM_IP4_CKSUM BIT(22) + #define GDM_TCP_CKSUM BIT(21) + #define GDM_UDP_CKSUM BIT(20) ++#define GDM_STRIP_CRC BIT(16) + #define GDM_UCFQ_MASK GENMASK(15, 12) + #define GDM_BCFQ_MASK GENMASK(11, 8) + #define GDM_MCFQ_MASK GENMASK(7, 4) +@@ -139,6 +146,16 @@ + #define GDM_SHORT_LEN_MASK GENMASK(13, 0) + #define GDM_LONG_LEN_MASK GENMASK(29, 16) + ++#define REG_GDM_LPBK_CFG(_n) (GDM_BASE(_n) + 0x1c) ++#define LPBK_GAP_MASK GENMASK(31, 24) ++#define LPBK_LEN_MASK GENMASK(23, 10) ++#define LPBK_CHAN_MASK GENMASK(8, 4) ++#define LPBK_MODE_MASK GENMASK(3, 1) ++#define LPBK_EN_MASK BIT(0) ++ ++#define REG_GDM_TXCHN_EN(_n) (GDM_BASE(_n) + 0x24) ++#define REG_GDM_RXCHN_EN(_n) (GDM_BASE(_n) + 0x28) ++ + #define REG_FE_CPORT_CFG (GDM1_BASE + 0x40) + #define FE_CPORT_PAD BIT(26) + #define FE_CPORT_PORT_XFC_MASK BIT(25) +@@ -351,6 +368,18 @@ + + #define REG_MC_VLAN_DATA 0x2108 + ++#define REG_SP_DFT_CPORT(_n) (0x20e0 + ((_n) << 2)) ++#define SP_CPORT_PCIE1_MASK GENMASK(31, 28) ++#define SP_CPORT_PCIE0_MASK GENMASK(27, 24) ++#define SP_CPORT_USB_MASK GENMASK(7, 4) ++#define SP_CPORT_ETH_MASK GENMASK(7, 4) ++ ++#define REG_SRC_PORT_FC_MAP6 0x2298 ++#define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24) ++#define FC_ID_OF_SRC_PORT26_MASK GENMASK(20, 16) ++#define FC_ID_OF_SRC_PORT25_MASK GENMASK(12, 8) ++#define FC_ID_OF_SRC_PORT24_MASK GENMASK(4, 0) ++ + #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 + + /* QDMA */ diff --git a/target/linux/airoha/patches-6.12/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch b/target/linux/airoha/patches-6.12/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch new file mode 100644 index 00000000000..50d7fa12668 --- /dev/null +++ b/target/linux/airoha/patches-6.12/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch @@ -0,0 +1,291 @@ +From 3fe15c640f3808c3faf235553c67c867d1389e5c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 28 Feb 2025 11:54:23 +0100 +Subject: [PATCH 15/15] net: airoha: Introduce PPE debugfs support + +Similar to PPE support for Mediatek devices, introduce PPE debugfs +in order to dump binded and unbinded flows. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/Makefile | 1 + + drivers/net/ethernet/airoha/airoha_eth.h | 14 ++ + drivers/net/ethernet/airoha/airoha_ppe.c | 17 +- + .../net/ethernet/airoha/airoha_ppe_debugfs.c | 181 ++++++++++++++++++ + 4 files changed, 209 insertions(+), 4 deletions(-) + create mode 100644 drivers/net/ethernet/airoha/airoha_ppe_debugfs.c + +--- a/drivers/net/ethernet/airoha/Makefile ++++ b/drivers/net/ethernet/airoha/Makefile +@@ -5,4 +5,5 @@ + + obj-$(CONFIG_NET_AIROHA) += airoha-eth.o + airoha-eth-y := airoha_eth.o airoha_ppe.o ++airoha-eth-$(CONFIG_DEBUG_FS) += airoha_ppe_debugfs.o + obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -7,6 +7,7 @@ + #ifndef AIROHA_ETH_H + #define AIROHA_ETH_H + ++#include + #include + #include + #include +@@ -480,6 +481,8 @@ struct airoha_ppe { + + struct hlist_head *foe_flow; + u16 foe_check_time[PPE_NUM_ENTRIES]; ++ ++ struct dentry *debugfs_dir; + }; + + struct airoha_eth { +@@ -533,5 +536,16 @@ int airoha_ppe_setup_tc_block_cb(enum tc + void *cb_priv); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); ++struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, ++ u32 hash); ++ ++#if CONFIG_DEBUG_FS ++int airoha_ppe_debugfs_init(struct airoha_ppe *ppe); ++#else ++static inline int airoha_ppe_debugfs_init(struct airoha_ppe *ppe) ++{ ++ return 0; ++} ++#endif + + #endif /* AIROHA_ETH_H */ +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -390,8 +390,8 @@ static u32 airoha_ppe_foe_get_entry_hash + return hash; + } + +-static struct airoha_foe_entry * +-airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash) ++struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, ++ u32 hash) + { + if (hash < PPE_SRAM_NUM_ENTRIES) { + u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); +@@ -861,7 +861,7 @@ void airoha_ppe_check_skb(struct airoha_ + int airoha_ppe_init(struct airoha_eth *eth) + { + struct airoha_ppe *ppe; +- int foe_size; ++ int foe_size, err; + + ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL); + if (!ppe) +@@ -882,7 +882,15 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe->foe_flow) + return -ENOMEM; + +- return rhashtable_init(ð->flow_table, &airoha_flow_table_params); ++ err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); ++ if (err) ++ return err; ++ ++ err = airoha_ppe_debugfs_init(ppe); ++ if (err) ++ rhashtable_destroy(ð->flow_table); ++ ++ return err; + } + + void airoha_ppe_deinit(struct airoha_eth *eth) +@@ -898,4 +906,5 @@ void airoha_ppe_deinit(struct airoha_eth + rcu_read_unlock(); + + rhashtable_destroy(ð->flow_table); ++ debugfs_remove(eth->ppe->debugfs_dir); + } +--- /dev/null ++++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +@@ -0,0 +1,181 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2025 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#include "airoha_eth.h" ++ ++static void airoha_debugfs_ppe_print_tuple(struct seq_file *m, ++ void *src_addr, void *dest_addr, ++ u16 *src_port, u16 *dest_port, ++ bool ipv6) ++{ ++ __be32 n_addr[IPV6_ADDR_WORDS]; ++ ++ if (ipv6) { ++ ipv6_addr_cpu_to_be32(n_addr, src_addr); ++ seq_printf(m, "%pI6", n_addr); ++ } else { ++ seq_printf(m, "%pI4h", src_addr); ++ } ++ if (src_port) ++ seq_printf(m, ":%d", *src_port); ++ ++ seq_puts(m, "->"); ++ ++ if (ipv6) { ++ ipv6_addr_cpu_to_be32(n_addr, dest_addr); ++ seq_printf(m, "%pI6", n_addr); ++ } else { ++ seq_printf(m, "%pI4h", dest_addr); ++ } ++ if (dest_port) ++ seq_printf(m, ":%d", *dest_port); ++} ++ ++static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, ++ bool bind) ++{ ++ static const char *const ppe_type_str[] = { ++ [PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T", ++ [PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T", ++ [PPE_PKT_TYPE_BRIDGE] = "L2B", ++ [PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE", ++ [PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T", ++ [PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T", ++ [PPE_PKT_TYPE_IPV6_6RD] = "6RD", ++ }; ++ static const char *const ppe_state_str[] = { ++ [AIROHA_FOE_STATE_INVALID] = "INV", ++ [AIROHA_FOE_STATE_UNBIND] = "UNB", ++ [AIROHA_FOE_STATE_BIND] = "BND", ++ [AIROHA_FOE_STATE_FIN] = "FIN", ++ }; ++ struct airoha_ppe *ppe = m->private; ++ int i; ++ ++ for (i = 0; i < PPE_NUM_ENTRIES; i++) { ++ const char *state_str, *type_str = "UNKNOWN"; ++ void *src_addr = NULL, *dest_addr = NULL; ++ u16 *src_port = NULL, *dest_port = NULL; ++ struct airoha_foe_mac_info_common *l2; ++ unsigned char h_source[ETH_ALEN] = {}; ++ unsigned char h_dest[ETH_ALEN]; ++ struct airoha_foe_entry *hwe; ++ u32 type, state, ib2, data; ++ bool ipv6 = false; ++ ++ hwe = airoha_ppe_foe_get_entry(ppe, i); ++ if (!hwe) ++ continue; ++ ++ state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); ++ if (!state) ++ continue; ++ ++ if (bind && state != AIROHA_FOE_STATE_BIND) ++ continue; ++ ++ state_str = ppe_state_str[state % ARRAY_SIZE(ppe_state_str)]; ++ type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ if (type < ARRAY_SIZE(ppe_type_str) && ppe_type_str[type]) ++ type_str = ppe_type_str[type]; ++ ++ seq_printf(m, "%05x %s %7s", i, state_str, type_str); ++ ++ switch (type) { ++ case PPE_PKT_TYPE_IPV4_HNAPT: ++ case PPE_PKT_TYPE_IPV4_DSLITE: ++ src_port = &hwe->ipv4.orig_tuple.src_port; ++ dest_port = &hwe->ipv4.orig_tuple.dest_port; ++ fallthrough; ++ case PPE_PKT_TYPE_IPV4_ROUTE: ++ src_addr = &hwe->ipv4.orig_tuple.src_ip; ++ dest_addr = &hwe->ipv4.orig_tuple.dest_ip; ++ break; ++ case PPE_PKT_TYPE_IPV6_ROUTE_5T: ++ src_port = &hwe->ipv6.src_port; ++ dest_port = &hwe->ipv6.dest_port; ++ fallthrough; ++ case PPE_PKT_TYPE_IPV6_ROUTE_3T: ++ case PPE_PKT_TYPE_IPV6_6RD: ++ src_addr = &hwe->ipv6.src_ip; ++ dest_addr = &hwe->ipv6.dest_ip; ++ ipv6 = true; ++ break; ++ default: ++ break; ++ } ++ ++ if (src_addr && dest_addr) { ++ seq_puts(m, " orig="); ++ airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr, ++ src_port, dest_port, ipv6); ++ } ++ ++ switch (type) { ++ case PPE_PKT_TYPE_IPV4_HNAPT: ++ case PPE_PKT_TYPE_IPV4_DSLITE: ++ src_port = &hwe->ipv4.new_tuple.src_port; ++ dest_port = &hwe->ipv4.new_tuple.dest_port; ++ fallthrough; ++ case PPE_PKT_TYPE_IPV4_ROUTE: ++ src_addr = &hwe->ipv4.new_tuple.src_ip; ++ dest_addr = &hwe->ipv4.new_tuple.dest_ip; ++ seq_puts(m, " new="); ++ airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr, ++ src_port, dest_port, ++ ipv6); ++ break; ++ default: ++ break; ++ } ++ ++ if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ data = hwe->ipv6.data; ++ ib2 = hwe->ipv6.ib2; ++ l2 = &hwe->ipv6.l2; ++ } else { ++ data = hwe->ipv4.data; ++ ib2 = hwe->ipv4.ib2; ++ l2 = &hwe->ipv4.l2.common; ++ *((__be16 *)&h_source[4]) = ++ cpu_to_be16(hwe->ipv4.l2.src_mac_lo); ++ } ++ ++ *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi); ++ *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo); ++ *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi); ++ ++ seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x" ++ " vlan=%d,%d ib1=%08x ib2=%08x\n", ++ h_source, h_dest, l2->etype, data, ++ l2->vlan1, l2->vlan2, hwe->ib1, ib2); ++ } ++ ++ return 0; ++} ++ ++static int airoha_ppe_debugfs_foe_all_show(struct seq_file *m, void *private) ++{ ++ return airoha_ppe_debugfs_foe_show(m, private, false); ++} ++DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_all); ++ ++static int airoha_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private) ++{ ++ return airoha_ppe_debugfs_foe_show(m, private, true); ++} ++DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_bind); ++ ++int airoha_ppe_debugfs_init(struct airoha_ppe *ppe) ++{ ++ ppe->debugfs_dir = debugfs_create_dir("ppe", NULL); ++ debugfs_create_file("entries", 0444, ppe->debugfs_dir, ppe, ++ &airoha_ppe_debugfs_foe_all_fops); ++ debugfs_create_file("bind", 0444, ppe->debugfs_dir, ppe, ++ &airoha_ppe_debugfs_foe_bind_fops); ++ ++ return 0; ++} diff --git a/target/linux/airoha/patches-6.12/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch b/target/linux/airoha/patches-6.12/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch new file mode 100644 index 00000000000..ea68cab33e7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch @@ -0,0 +1,550 @@ +From 42de37f40e1bc818df216dfa0918c114cfb5941d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 11 May 2025 20:49:55 +0200 +Subject: [PATCH] thermal/drivers: Add support for Airoha EN7581 thermal sensor + +Add support for Airoha EN7581 thermal sensor. This provide support for +reading the CPU or SoC Package sensor and to setup trip points for hot +and critical condition. An interrupt is fired to react on this and +doesn't require passive poll to read the temperature. + +The thermal regs provide a way to read the ADC value from an external +register placed in the Chip SCU regs. Monitor will read this value and +fire an interrupt if the trip condition configured is reached. + +The Thermal Trip and Interrupt logic is conceptually similar to Mediatek +LVTS Thermal but differ in register mapping and actual function/bug +workaround. The implementation only share some register names but from +functionality observation it's very different and used only for the +basic function of periodically poll the temp and trip the interrupt. + +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250511185003.3754495-2-ansuelsmth@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/Kconfig | 9 + + drivers/thermal/Makefile | 1 + + drivers/thermal/airoha_thermal.c | 489 +++++++++++++++++++++++++++++++ + 3 files changed, 499 insertions(+) + create mode 100644 drivers/thermal/airoha_thermal.c + +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -318,6 +318,15 @@ config QORIQ_THERMAL + cpufreq is used as the cooling device to throttle CPUs when the + passive trip is crossed. + ++config AIROHA_THERMAL ++ tristate "Airoha thermal sensor driver" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ depends on MFD_SYSCON ++ depends on OF ++ help ++ Enable this to plug the Airoha thermal sensor driver into the Linux ++ thermal framework. ++ + config SPEAR_THERMAL + tristate "SPEAr thermal sensor driver" + depends on PLAT_SPEAR || COMPILE_TEST +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_K3_THERMAL) += k3_bandgap.o + # platform thermal drivers + obj-y += broadcom/ + obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o ++obj-$(CONFIG_AIROHA_THERMAL) += airoha_thermal.o + obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o + obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o + obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o +--- /dev/null ++++ b/drivers/thermal/airoha_thermal.c +@@ -0,0 +1,489 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SCU regs */ ++#define EN7581_PLLRG_PROTECT 0x268 ++#define EN7581_PWD_TADC 0x2ec ++#define EN7581_MUX_TADC GENMASK(3, 1) ++#define EN7581_DOUT_TADC 0x2f8 ++#define EN7581_DOUT_TADC_MASK GENMASK(15, 0) ++ ++/* PTP_THERMAL regs */ ++#define EN7581_TEMPMONCTL0 0x800 ++#define EN7581_SENSE3_EN BIT(3) ++#define EN7581_SENSE2_EN BIT(2) ++#define EN7581_SENSE1_EN BIT(1) ++#define EN7581_SENSE0_EN BIT(0) ++#define EN7581_TEMPMONCTL1 0x804 ++/* period unit calculated in BUS clock * 256 scaling-up */ ++#define EN7581_PERIOD_UNIT GENMASK(9, 0) ++#define EN7581_TEMPMONCTL2 0x808 ++#define EN7581_FILT_INTERVAL GENMASK(25, 16) ++#define EN7581_SEN_INTERVAL GENMASK(9, 0) ++#define EN7581_TEMPMONINT 0x80C ++#define EN7581_STAGE3_INT_EN BIT(31) ++#define EN7581_STAGE2_INT_EN BIT(30) ++#define EN7581_STAGE1_INT_EN BIT(29) ++#define EN7581_FILTER_INT_EN_3 BIT(28) ++#define EN7581_IMMD_INT_EN3 BIT(27) ++#define EN7581_NOHOTINTEN3 BIT(26) ++#define EN7581_HOFSINTEN3 BIT(25) ++#define EN7581_LOFSINTEN3 BIT(24) ++#define EN7581_HINTEN3 BIT(23) ++#define EN7581_CINTEN3 BIT(22) ++#define EN7581_FILTER_INT_EN_2 BIT(21) ++#define EN7581_FILTER_INT_EN_1 BIT(20) ++#define EN7581_FILTER_INT_EN_0 BIT(19) ++#define EN7581_IMMD_INT_EN2 BIT(18) ++#define EN7581_IMMD_INT_EN1 BIT(17) ++#define EN7581_IMMD_INT_EN0 BIT(16) ++#define EN7581_TIME_OUT_INT_EN BIT(15) ++#define EN7581_NOHOTINTEN2 BIT(14) ++#define EN7581_HOFSINTEN2 BIT(13) ++#define EN7581_LOFSINTEN2 BIT(12) ++#define EN7581_HINTEN2 BIT(11) ++#define EN7581_CINTEN2 BIT(10) ++#define EN7581_NOHOTINTEN1 BIT(9) ++#define EN7581_HOFSINTEN1 BIT(8) ++#define EN7581_LOFSINTEN1 BIT(7) ++#define EN7581_HINTEN1 BIT(6) ++#define EN7581_CINTEN1 BIT(5) ++#define EN7581_NOHOTINTEN0 BIT(4) ++/* Similar to COLD and HOT also these seems to be swapped in documentation */ ++#define EN7581_LOFSINTEN0 BIT(3) /* In documentation: BIT(2) */ ++#define EN7581_HOFSINTEN0 BIT(2) /* In documentation: BIT(3) */ ++/* It seems documentation have these swapped as the HW ++ * - Fire BIT(1) when lower than EN7581_COLD_THRE ++ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or ++ * EN7581_HOT_THRE ++ */ ++#define EN7581_CINTEN0 BIT(1) /* In documentation: BIT(0) */ ++#define EN7581_HINTEN0 BIT(0) /* In documentation: BIT(1) */ ++#define EN7581_TEMPMONINTSTS 0x810 ++#define EN7581_STAGE3_INT_STAT BIT(31) ++#define EN7581_STAGE2_INT_STAT BIT(30) ++#define EN7581_STAGE1_INT_STAT BIT(29) ++#define EN7581_FILTER_INT_STAT_3 BIT(28) ++#define EN7581_IMMD_INT_STS3 BIT(27) ++#define EN7581_NOHOTINTSTS3 BIT(26) ++#define EN7581_HOFSINTSTS3 BIT(25) ++#define EN7581_LOFSINTSTS3 BIT(24) ++#define EN7581_HINTSTS3 BIT(23) ++#define EN7581_CINTSTS3 BIT(22) ++#define EN7581_FILTER_INT_STAT_2 BIT(21) ++#define EN7581_FILTER_INT_STAT_1 BIT(20) ++#define EN7581_FILTER_INT_STAT_0 BIT(19) ++#define EN7581_IMMD_INT_STS2 BIT(18) ++#define EN7581_IMMD_INT_STS1 BIT(17) ++#define EN7581_IMMD_INT_STS0 BIT(16) ++#define EN7581_TIME_OUT_INT_STAT BIT(15) ++#define EN7581_NOHOTINTSTS2 BIT(14) ++#define EN7581_HOFSINTSTS2 BIT(13) ++#define EN7581_LOFSINTSTS2 BIT(12) ++#define EN7581_HINTSTS2 BIT(11) ++#define EN7581_CINTSTS2 BIT(10) ++#define EN7581_NOHOTINTSTS1 BIT(9) ++#define EN7581_HOFSINTSTS1 BIT(8) ++#define EN7581_LOFSINTSTS1 BIT(7) ++#define EN7581_HINTSTS1 BIT(6) ++#define EN7581_CINTSTS1 BIT(5) ++#define EN7581_NOHOTINTSTS0 BIT(4) ++/* Similar to COLD and HOT also these seems to be swapped in documentation */ ++#define EN7581_LOFSINTSTS0 BIT(3) /* In documentation: BIT(2) */ ++#define EN7581_HOFSINTSTS0 BIT(2) /* In documentation: BIT(3) */ ++/* It seems documentation have these swapped as the HW ++ * - Fire BIT(1) when lower than EN7581_COLD_THRE ++ * - Fire BIT(0) and BIT(5) when higher than EN7581_HOT2NORMAL_THRE or ++ * EN7581_HOT_THRE ++ * ++ * To clear things, we swap the define but we keep them documented here. ++ */ ++#define EN7581_CINTSTS0 BIT(1) /* In documentation: BIT(0) */ ++#define EN7581_HINTSTS0 BIT(0) /* In documentation: BIT(1)*/ ++/* Monitor will take the bigger threshold between HOT2NORMAL and HOT ++ * and will fire both HOT2NORMAL and HOT interrupt when higher than the 2 ++ * ++ * It has also been observed that not setting HOT2NORMAL makes the monitor ++ * treat COLD threshold as HOT2NORMAL. ++ */ ++#define EN7581_TEMPH2NTHRE 0x824 ++/* It seems HOT2NORMAL is actually NORMAL2HOT */ ++#define EN7581_HOT2NORMAL_THRE GENMASK(11, 0) ++#define EN7581_TEMPHTHRE 0x828 ++#define EN7581_HOT_THRE GENMASK(11, 0) ++/* Monitor will use this as HOT2NORMAL (fire interrupt when lower than...)*/ ++#define EN7581_TEMPCTHRE 0x82c ++#define EN7581_COLD_THRE GENMASK(11, 0) ++/* Also LOW and HIGH offset register are swapped */ ++#define EN7581_TEMPOFFSETL 0x830 /* In documentation: 0x834 */ ++#define EN7581_LOW_OFFSET GENMASK(11, 0) ++#define EN7581_TEMPOFFSETH 0x834 /* In documentation: 0x830 */ ++#define EN7581_HIGH_OFFSET GENMASK(11, 0) ++#define EN7581_TEMPMSRCTL0 0x838 ++#define EN7581_MSRCTL3 GENMASK(11, 9) ++#define EN7581_MSRCTL2 GENMASK(8, 6) ++#define EN7581_MSRCTL1 GENMASK(5, 3) ++#define EN7581_MSRCTL0 GENMASK(2, 0) ++#define EN7581_TEMPADCVALIDADDR 0x878 ++#define EN7581_ADC_VALID_ADDR GENMASK(31, 0) ++#define EN7581_TEMPADCVOLTADDR 0x87c ++#define EN7581_ADC_VOLT_ADDR GENMASK(31, 0) ++#define EN7581_TEMPRDCTRL 0x880 ++/* ++ * NOTICE: AHB have this set to 0 by default. Means that ++ * the same addr is used for ADC volt and valid reading. ++ * In such case, VALID ADDR is used and volt addr is ignored. ++ */ ++#define EN7581_RD_CTRL_DIFF BIT(0) ++#define EN7581_TEMPADCVALIDMASK 0x884 ++#define EN7581_ADV_RD_VALID_POLARITY BIT(5) ++#define EN7581_ADV_RD_VALID_POS GENMASK(4, 0) ++#define EN7581_TEMPADCVOLTAGESHIFT 0x888 ++#define EN7581_ADC_VOLTAGE_SHIFT GENMASK(4, 0) ++/* ++ * Same values for each CTL. ++ * Can operate in: ++ * - 1 sample ++ * - 2 sample and make average of them ++ * - 4,6,10,16 sample, drop max and min and make avgerage of them ++ */ ++#define EN7581_MSRCTL_1SAMPLE 0x0 ++#define EN7581_MSRCTL_AVG2SAMPLE 0x1 ++#define EN7581_MSRCTL_4SAMPLE_MAX_MIX_AVG2 0x2 ++#define EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4 0x3 ++#define EN7581_MSRCTL_10SAMPLE_MAX_MIX_AVG8 0x4 ++#define EN7581_MSRCTL_18SAMPLE_MAX_MIX_AVG16 0x5 ++#define EN7581_TEMPAHBPOLL 0x840 ++#define EN7581_ADC_POLL_INTVL GENMASK(31, 0) ++/* PTPSPARE0,2 reg are used to store efuse info for calibrated temp offset */ ++#define EN7581_EFUSE_TEMP_OFFSET_REG 0xf20 /* PTPSPARE0 */ ++#define EN7581_EFUSE_TEMP_OFFSET GENMASK(31, 16) ++#define EN7581_PTPSPARE1 0xf24 /* PTPSPARE1 */ ++#define EN7581_EFUSE_TEMP_CPU_SENSOR_REG 0xf28 /* PTPSPARE2 */ ++ ++#define EN7581_SLOPE_X100_DIO_DEFAULT 5645 ++#define EN7581_SLOPE_X100_DIO_AVS 5645 ++ ++#define EN7581_INIT_TEMP_CPK_X10 300 ++#define EN7581_INIT_TEMP_FTK_X10 620 ++#define EN7581_INIT_TEMP_NONK_X10 550 ++ ++#define EN7581_SCU_THERMAL_PROTECT_KEY 0x12 ++#define EN7581_SCU_THERMAL_MUX_DIODE1 0x7 ++ ++/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */ ++#define TEMP_TO_RAW(priv, temp) ((((((temp) / 100) - (priv)->init_temp) * \ ++ (priv)->default_slope) / 1000) + \ ++ (priv)->default_offset) ++ ++/* Convert raw to temp ((((temp - offset) * 1000) / slope + init) * 100) */ ++#define RAW_TO_TEMP(priv, raw) (((((raw) - (priv)->default_offset) * 1000) / \ ++ (priv)->default_slope + \ ++ (priv)->init_temp) * 100) ++ ++#define AIROHA_MAX_SAMPLES 6 ++ ++struct airoha_thermal_priv { ++ void __iomem *base; ++ struct regmap *chip_scu; ++ struct resource scu_adc_res; ++ ++ struct thermal_zone_device *tz; ++ int init_temp; ++ int default_slope; ++ int default_offset; ++}; ++ ++static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv) ++{ ++ u32 val; ++ ++ regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val); ++ return FIELD_GET(EN7581_DOUT_TADC_MASK, val); ++} ++ ++static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv) ++{ ++ u32 adc_mux, pllrg; ++ ++ /* Save PLLRG current value */ ++ regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg); ++ ++ /* Give access to thermal regs */ ++ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY); ++ adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1); ++ regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux); ++ ++ /* Restore PLLRG value on exit */ ++ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg); ++} ++ ++static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp) ++{ ++ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); ++ int min_value, max_value, avg_value, value; ++ int i; ++ ++ avg_value = 0; ++ min_value = INT_MAX; ++ max_value = INT_MIN; ++ ++ for (i = 0; i < AIROHA_MAX_SAMPLES; i++) { ++ value = airoha_get_thermal_ADC(priv); ++ min_value = min(value, min_value); ++ max_value = max(value, max_value); ++ avg_value += value; ++ } ++ ++ /* Drop min and max and average for the remaining sample */ ++ avg_value -= (min_value + max_value); ++ avg_value /= AIROHA_MAX_SAMPLES - 2; ++ ++ *temp = RAW_TO_TEMP(priv, avg_value); ++ return 0; ++} ++ ++static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low, ++ int high) ++{ ++ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); ++ bool enable_monitor = false; ++ ++ if (high != INT_MAX) { ++ /* Validate high and clamp it a supported value */ ++ high = clamp_t(int, high, RAW_TO_TEMP(priv, 0), ++ RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK))); ++ ++ /* We offset the high temp of 1°C to trigger correct event */ ++ writel(TEMP_TO_RAW(priv, high) >> 4, ++ priv->base + EN7581_TEMPOFFSETH); ++ ++ enable_monitor = true; ++ } ++ ++ if (low != -INT_MAX) { ++ /* Validate low and clamp it to a supported value */ ++ low = clamp_t(int, high, RAW_TO_TEMP(priv, 0), ++ RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK))); ++ ++ /* We offset the low temp of 1°C to trigger correct event */ ++ writel(TEMP_TO_RAW(priv, low) >> 4, ++ priv->base + EN7581_TEMPOFFSETL); ++ ++ enable_monitor = true; ++ } ++ ++ /* Enable sensor 0 monitor after trip are set */ ++ if (enable_monitor) ++ writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0); ++ ++ return 0; ++} ++ ++static const struct thermal_zone_device_ops thdev_ops = { ++ .get_temp = airoha_thermal_get_temp, ++ .set_trips = airoha_thermal_set_trips, ++}; ++ ++static irqreturn_t airoha_thermal_irq(int irq, void *data) ++{ ++ struct airoha_thermal_priv *priv = data; ++ enum thermal_notify_event event; ++ bool update = false; ++ u32 status; ++ ++ status = readl(priv->base + EN7581_TEMPMONINTSTS); ++ switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) { ++ case EN7581_HOFSINTSTS0: ++ event = THERMAL_TRIP_VIOLATED; ++ update = true; ++ break; ++ case EN7581_LOFSINTSTS0: ++ event = THERMAL_EVENT_UNSPECIFIED; ++ update = true; ++ break; ++ default: ++ /* Should be impossible as we enable only these Interrupt */ ++ break; ++ } ++ ++ /* Reset Interrupt */ ++ writel(status, priv->base + EN7581_TEMPMONINTSTS); ++ ++ if (update) ++ thermal_zone_device_update(priv->tz, event); ++ ++ return IRQ_HANDLED; ++} ++ ++static void airoha_thermal_setup_adc_val(struct device *dev, ++ struct airoha_thermal_priv *priv) ++{ ++ u32 efuse_calib_info, cpu_sensor; ++ ++ /* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */ ++ airoha_init_thermal_ADC_mode(priv); ++ /* sleep 10 ms for ADC to enable */ ++ usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); ++ ++ efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG); ++ if (efuse_calib_info) { ++ priv->default_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info); ++ /* Different slope are applied if the sensor is used for CPU or for package */ ++ cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG); ++ if (cpu_sensor) { ++ priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT; ++ priv->init_temp = EN7581_INIT_TEMP_FTK_X10; ++ } else { ++ priv->default_slope = EN7581_SLOPE_X100_DIO_AVS; ++ priv->init_temp = EN7581_INIT_TEMP_CPK_X10; ++ } ++ } else { ++ priv->default_offset = airoha_get_thermal_ADC(priv); ++ priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT; ++ priv->init_temp = EN7581_INIT_TEMP_NONK_X10; ++ dev_info(dev, "missing thermal calibrarion EFUSE, using non calibrated value\n"); ++ } ++} ++ ++static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv) ++{ ++ /* Set measure mode */ ++ writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4), ++ priv->base + EN7581_TEMPMSRCTL0); ++ ++ /* ++ * Configure ADC valid reading addr ++ * The AHB temp monitor system doesn't have direct access to the ++ * thermal sensor. It does instead work by providing all kind of ++ * address to configure how to access and setup an ADC for the ++ * sensor. EN7581 supports only one sensor hence the ++ * implementation is greatly simplified but the AHB supports ++ * up to 4 different sensor from the same ADC that can be ++ * switched by tuning the ADC mux or wiriting address. ++ * ++ * We set valid instead of volt as we don't enable valid/volt ++ * split reading and AHB read valid addr in such case. ++ */ ++ writel(priv->scu_adc_res.start + EN7581_DOUT_TADC, ++ priv->base + EN7581_TEMPADCVALIDADDR); ++ ++ /* ++ * Configure valid bit on a fake value of bit 16. The ADC outputs ++ * max of 2 bytes for voltage. ++ */ ++ writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16), ++ priv->base + EN7581_TEMPADCVALIDMASK); ++ ++ /* ++ * AHB supports max 12 bytes for ADC voltage. Shift the read ++ * value 4 bit to the right. Precision lost by this is minimal ++ * in the order of half a °C and is acceptable in the context ++ * of triggering interrupt in critical condition. ++ */ ++ writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4), ++ priv->base + EN7581_TEMPADCVOLTAGESHIFT); ++ ++ /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */ ++ writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3), ++ priv->base + EN7581_TEMPMONCTL1); ++ ++ /* ++ * filt interval is 1 * 52.715us = 52.715us, ++ * sen interval is 379 * 52.715us = 19.97ms ++ */ ++ writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) | ++ FIELD_PREP(EN7581_FILT_INTERVAL, 379), ++ priv->base + EN7581_TEMPMONCTL2); ++ ++ /* AHB poll is set to 146 * 68.64 = 10.02us */ ++ writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146), ++ priv->base + EN7581_TEMPAHBPOLL); ++} ++ ++static int airoha_thermal_probe(struct platform_device *pdev) ++{ ++ struct airoha_thermal_priv *priv; ++ struct device_node *chip_scu_np; ++ struct device *dev = &pdev->dev; ++ int irq, ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0); ++ if (!chip_scu_np) ++ return -EINVAL; ++ ++ priv->chip_scu = syscon_node_to_regmap(chip_scu_np); ++ if (IS_ERR(priv->chip_scu)) ++ return PTR_ERR(priv->chip_scu); ++ ++ of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res); ++ of_node_put(chip_scu_np); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ++ airoha_thermal_irq, IRQF_ONESHOT, ++ pdev->name, priv); ++ if (ret) { ++ dev_err(dev, "Can't get interrupt working.\n"); ++ return ret; ++ } ++ ++ airoha_thermal_setup_monitor(priv); ++ airoha_thermal_setup_adc_val(dev, priv); ++ ++ /* register of thermal sensor and get info from DT */ ++ priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &thdev_ops); ++ if (IS_ERR(priv->tz)) { ++ dev_err(dev, "register thermal zone sensor failed\n"); ++ return PTR_ERR(priv->tz); ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ ++ /* Enable LOW and HIGH interrupt */ ++ writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0, ++ priv->base + EN7581_TEMPMONINT); ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_thermal_match[] = { ++ { .compatible = "airoha,en7581-thermal" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, airoha_thermal_match); ++ ++static struct platform_driver airoha_thermal_driver = { ++ .driver = { ++ .name = "airoha-thermal", ++ .of_match_table = airoha_thermal_match, ++ }, ++ .probe = airoha_thermal_probe, ++}; ++ ++module_platform_driver(airoha_thermal_driver); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha thermal driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch b/target/linux/airoha/patches-6.12/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch new file mode 100644 index 00000000000..7b1b9478c37 --- /dev/null +++ b/target/linux/airoha/patches-6.12/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch @@ -0,0 +1,44 @@ +From e23cba0ab49a9cf95e9bc3a86cfbf336b0e285f6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 14 May 2025 23:39:12 +0200 +Subject: [PATCH] thermal/drivers/airoha: Fix spelling mistake + +Fix various spelling mistake in airoha_thermal_setup_monitor() and +define. + +Reported-by: Alok Tiwari +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250514213919.2321490-1-ansuelsmth@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/airoha_thermal.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/thermal/airoha_thermal.c ++++ b/drivers/thermal/airoha_thermal.c +@@ -155,7 +155,7 @@ + * Can operate in: + * - 1 sample + * - 2 sample and make average of them +- * - 4,6,10,16 sample, drop max and min and make avgerage of them ++ * - 4,6,10,16 sample, drop max and min and make average of them + */ + #define EN7581_MSRCTL_1SAMPLE 0x0 + #define EN7581_MSRCTL_AVG2SAMPLE 0x1 +@@ -365,12 +365,12 @@ static void airoha_thermal_setup_monitor + /* + * Configure ADC valid reading addr + * The AHB temp monitor system doesn't have direct access to the +- * thermal sensor. It does instead work by providing all kind of +- * address to configure how to access and setup an ADC for the ++ * thermal sensor. It does instead work by providing various ++ * addresses to configure how to access and setup an ADC for the + * sensor. EN7581 supports only one sensor hence the + * implementation is greatly simplified but the AHB supports +- * up to 4 different sensor from the same ADC that can be +- * switched by tuning the ADC mux or wiriting address. ++ * up to 4 different sensors from the same ADC that can be ++ * switched by tuning the ADC mux or writing address. + * + * We set valid instead of volt as we don't enable valid/volt + * split reading and AHB read valid addr in such case. diff --git a/target/linux/airoha/patches-6.12/051-v6.15-pinctrl-airoha-fix-wrong-PHY-LED-mapping-and-PHY2-LE.patch b/target/linux/airoha/patches-6.12/051-v6.15-pinctrl-airoha-fix-wrong-PHY-LED-mapping-and-PHY2-LE.patch new file mode 100644 index 00000000000..15bbee2a240 --- /dev/null +++ b/target/linux/airoha/patches-6.12/051-v6.15-pinctrl-airoha-fix-wrong-PHY-LED-mapping-and-PHY2-LE.patch @@ -0,0 +1,435 @@ +From 457d9772e8a5cdae64f66b5f7d5b0247365191ec Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 1 Apr 2025 15:50:21 +0200 +Subject: [PATCH] pinctrl: airoha: fix wrong PHY LED mapping and PHY2 LED + defines + +The current PHY2 LED define are wrong and actually set BITs outside the +related mask. Fix it and set the correct value. While at it, also use +FIELD_PREP_CONST macro to make it simple to understand what values are +actually applied for the mask. + +Also fix wrong PHY LED mapping. The SoC Switch supports up to 4 port but +the register define mapping for 5 PHY port, starting from 0. The mapping +was wrongly defined starting from PHY1. Reorder the function group to +start from PHY0. PHY4 is actually never supported as we don't have a +GPIO pin to assign. + +Cc: stable@vger.kernel.org +Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") +Reviewed-by: Benjamin Larsson +Signed-off-by: Christian Marangi +Acked-by: Lorenzo Bianconi +Link: https://lore.kernel.org/20250401135026.18018-1-ansuelsmth@gmail.com +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 159 ++++++++++------------ + 1 file changed, 70 insertions(+), 89 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -112,39 +113,19 @@ + #define REG_LAN_LED1_MAPPING 0x0280 + + #define LAN4_LED_MAPPING_MASK GENMASK(18, 16) +-#define LAN4_PHY4_LED_MAP BIT(18) +-#define LAN4_PHY2_LED_MAP BIT(17) +-#define LAN4_PHY1_LED_MAP BIT(16) +-#define LAN4_PHY0_LED_MAP 0 +-#define LAN4_PHY3_LED_MAP GENMASK(17, 16) ++#define LAN4_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN4_LED_MAPPING_MASK, (_n)) + + #define LAN3_LED_MAPPING_MASK GENMASK(14, 12) +-#define LAN3_PHY4_LED_MAP BIT(14) +-#define LAN3_PHY2_LED_MAP BIT(13) +-#define LAN3_PHY1_LED_MAP BIT(12) +-#define LAN3_PHY0_LED_MAP 0 +-#define LAN3_PHY3_LED_MAP GENMASK(13, 12) ++#define LAN3_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN3_LED_MAPPING_MASK, (_n)) + + #define LAN2_LED_MAPPING_MASK GENMASK(10, 8) +-#define LAN2_PHY4_LED_MAP BIT(12) +-#define LAN2_PHY2_LED_MAP BIT(11) +-#define LAN2_PHY1_LED_MAP BIT(10) +-#define LAN2_PHY0_LED_MAP 0 +-#define LAN2_PHY3_LED_MAP GENMASK(11, 10) ++#define LAN2_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN2_LED_MAPPING_MASK, (_n)) + + #define LAN1_LED_MAPPING_MASK GENMASK(6, 4) +-#define LAN1_PHY4_LED_MAP BIT(6) +-#define LAN1_PHY2_LED_MAP BIT(5) +-#define LAN1_PHY1_LED_MAP BIT(4) +-#define LAN1_PHY0_LED_MAP 0 +-#define LAN1_PHY3_LED_MAP GENMASK(5, 4) ++#define LAN1_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN1_LED_MAPPING_MASK, (_n)) + + #define LAN0_LED_MAPPING_MASK GENMASK(2, 0) +-#define LAN0_PHY4_LED_MAP BIT(3) +-#define LAN0_PHY2_LED_MAP BIT(2) +-#define LAN0_PHY1_LED_MAP BIT(1) +-#define LAN0_PHY0_LED_MAP 0 +-#define LAN0_PHY3_LED_MAP GENMASK(2, 1) ++#define LAN0_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN0_LED_MAPPING_MASK, (_n)) + + /* CONF */ + #define REG_I2C_SDA_E2 0x001c +@@ -1476,8 +1457,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY1_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1491,8 +1472,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY1_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1506,8 +1487,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY1_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1521,8 +1502,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY1_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, +@@ -1540,8 +1521,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY2_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1555,8 +1536,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY2_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1570,8 +1551,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY2_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1585,8 +1566,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY2_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, +@@ -1604,8 +1585,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY3_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1619,8 +1600,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY3_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1634,8 +1615,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY3_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1649,8 +1630,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY3_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, +@@ -1668,8 +1649,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY4_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1683,8 +1664,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY4_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1698,8 +1679,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY4_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1713,8 +1694,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY4_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, +@@ -1732,8 +1713,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY1_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1747,8 +1728,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY1_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1762,8 +1743,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY1_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1777,8 +1758,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY1_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, +@@ -1796,8 +1777,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY2_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1811,8 +1792,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY2_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1826,8 +1807,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY2_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1841,8 +1822,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY2_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, +@@ -1860,8 +1841,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY3_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1875,8 +1856,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY3_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1890,8 +1871,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY3_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1905,8 +1886,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY3_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, +@@ -1924,8 +1905,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY4_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1939,8 +1920,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY4_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1954,8 +1935,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY4_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1969,8 +1950,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY4_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, diff --git a/target/linux/airoha/patches-6.12/063-01-v6.15-net-airoha-Move-min-max-packet-len-configuration-in-.patch b/target/linux/airoha/patches-6.12/063-01-v6.15-net-airoha-Move-min-max-packet-len-configuration-in-.patch new file mode 100644 index 00000000000..904c424187b --- /dev/null +++ b/target/linux/airoha/patches-6.12/063-01-v6.15-net-airoha-Move-min-max-packet-len-configuration-in-.patch @@ -0,0 +1,59 @@ +From 54d989d58d2ac87c8504c2306ba8b4957c60e8dc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:08 +0100 +Subject: [PATCH 1/6] net: airoha: Move min/max packet len configuration in + airoha_dev_open() + +In order to align max allowed packet size to the configured mtu, move +REG_GDM_LEN_CFG configuration in airoha_dev_open routine. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-1-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -138,15 +138,10 @@ static void airoha_fe_maccr_init(struct + { + int p; + +- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { ++ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) + airoha_fe_set(eth, REG_GDM_FWD_CFG(p), + GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | + GDM_DROP_CRC_ERR); +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), +- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +- FIELD_PREP(GDM_LONG_LEN_MASK, 4004)); +- } + + airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, + FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); +@@ -1541,9 +1536,9 @@ static void airoha_update_hw_stats(struc + + static int airoha_dev_open(struct net_device *dev) + { ++ int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN; + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; +- int err; + + netif_tx_start_all_queues(dev); + err = airoha_set_vip_for_gdm_port(port, true); +@@ -1557,6 +1552,11 @@ static int airoha_dev_open(struct net_de + airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + ++ airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++ + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); diff --git a/target/linux/airoha/patches-6.12/063-02-v6.15-net-airoha-Enable-Rx-Scatter-Gather.patch b/target/linux/airoha/patches-6.12/063-02-v6.15-net-airoha-Enable-Rx-Scatter-Gather.patch new file mode 100644 index 00000000000..87c180f0732 --- /dev/null +++ b/target/linux/airoha/patches-6.12/063-02-v6.15-net-airoha-Enable-Rx-Scatter-Gather.patch @@ -0,0 +1,170 @@ +From e12182ddb6e712951d21a50e2c8ccd700e41a40c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:09 +0100 +Subject: [PATCH 2/6] net: airoha: Enable Rx Scatter-Gather + +EN7581 SoC can receive 9k frames. Enable the reception of Scatter-Gather +(SG) frames. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-2-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 68 ++++++++++++++--------- + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_regs.h | 5 ++ + 3 files changed, 48 insertions(+), 26 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -615,10 +615,10 @@ static int airoha_qdma_rx_process(struct + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); + dma_addr_t dma_addr = le32_to_cpu(desc->addr); ++ struct page *page = virt_to_head_page(e->buf); + u32 desc_ctrl = le32_to_cpu(desc->ctrl); + struct airoha_gdm_port *port; +- struct sk_buff *skb; +- int len, p; ++ int data_len, len, p; + + if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) + break; +@@ -636,30 +636,41 @@ static int airoha_qdma_rx_process(struct + dma_sync_single_for_cpu(eth->dev, dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), dir); + ++ data_len = q->skb ? q->buf_size ++ : SKB_WITH_OVERHEAD(q->buf_size); ++ if (data_len < len) ++ goto free_frag; ++ + p = airoha_qdma_get_gdm_port(eth, desc); +- if (p < 0 || !eth->ports[p]) { +- page_pool_put_full_page(q->page_pool, +- virt_to_head_page(e->buf), +- true); +- continue; +- } ++ if (p < 0 || !eth->ports[p]) ++ goto free_frag; + + port = eth->ports[p]; +- skb = napi_build_skb(e->buf, q->buf_size); +- if (!skb) { +- page_pool_put_full_page(q->page_pool, +- virt_to_head_page(e->buf), +- true); +- break; ++ if (!q->skb) { /* first buffer */ ++ q->skb = napi_build_skb(e->buf, q->buf_size); ++ if (!q->skb) ++ goto free_frag; ++ ++ __skb_put(q->skb, len); ++ skb_mark_for_recycle(q->skb); ++ q->skb->dev = port->dev; ++ q->skb->protocol = eth_type_trans(q->skb, port->dev); ++ q->skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb_record_rx_queue(q->skb, qid); ++ } else { /* scattered frame */ ++ struct skb_shared_info *shinfo = skb_shinfo(q->skb); ++ int nr_frags = shinfo->nr_frags; ++ ++ if (nr_frags >= ARRAY_SIZE(shinfo->frags)) ++ goto free_frag; ++ ++ skb_add_rx_frag(q->skb, nr_frags, page, ++ e->buf - page_address(page), len, ++ q->buf_size); + } + +- skb_reserve(skb, 2); +- __skb_put(skb, len); +- skb_mark_for_recycle(skb); +- skb->dev = port->dev; +- skb->protocol = eth_type_trans(skb, skb->dev); +- skb->ip_summed = CHECKSUM_UNNECESSARY; +- skb_record_rx_queue(skb, qid); ++ if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl)) ++ continue; + + if (netdev_uses_dsa(port->dev)) { + /* PPE module requires untagged packets to work +@@ -672,22 +683,27 @@ static int airoha_qdma_rx_process(struct + + if (sptag < ARRAY_SIZE(port->dsa_meta) && + port->dsa_meta[sptag]) +- skb_dst_set_noref(skb, ++ skb_dst_set_noref(q->skb, + &port->dsa_meta[sptag]->dst); + } + + hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1); + if (hash != AIROHA_RXD4_FOE_ENTRY) +- skb_set_hash(skb, jhash_1word(hash, 0), ++ skb_set_hash(q->skb, jhash_1word(hash, 0), + PKT_HASH_TYPE_L4); + + reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) + airoha_ppe_check_skb(eth->ppe, hash); + +- napi_gro_receive(&q->napi, skb); +- + done++; ++ napi_gro_receive(&q->napi, q->skb); ++ q->skb = NULL; ++ continue; ++free_frag: ++ page_pool_put_full_page(q->page_pool, page, true); ++ dev_kfree_skb(q->skb); ++ q->skb = NULL; + } + airoha_qdma_fill_rx_queue(q); + +@@ -762,6 +778,7 @@ static int airoha_qdma_init_rx_queue(str + FIELD_PREP(RX_RING_THR_MASK, thr)); + airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); ++ airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid), RX_RING_SG_EN_MASK); + + airoha_qdma_fill_rx_queue(q); + +@@ -1182,7 +1199,6 @@ static int airoha_qdma_hw_init(struct ai + } + + airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, +- GLOBAL_CFG_RX_2B_OFFSET_MASK | + FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | + GLOBAL_CFG_CPU_TXR_RR_MASK | + GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -176,6 +176,7 @@ struct airoha_queue { + + struct napi_struct napi; + struct page_pool *page_pool; ++ struct sk_buff *skb; + }; + + struct airoha_tx_irq_queue { +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -626,10 +626,15 @@ + #define REG_RX_DELAY_INT_IDX(_n) \ + (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) + ++#define REG_RX_SCATTER_CFG(_n) \ ++ (((_n) < 16) ? 0x0214 + ((_n) << 5) : 0x0e14 + (((_n) - 16) << 5)) ++ + #define RX_DELAY_INT_MASK GENMASK(15, 0) + + #define RX_RING_DMA_IDX_MASK GENMASK(15, 0) + ++#define RX_RING_SG_EN_MASK BIT(0) ++ + #define REG_INGRESS_TRTCM_CFG 0x0070 + #define INGRESS_TRTCM_EN_MASK BIT(31) + #define INGRESS_TRTCM_MODE_MASK BIT(30) diff --git a/target/linux/airoha/patches-6.12/063-03-v6.15-net-airoha-Introduce-airoha_dev_change_mtu-callback.patch b/target/linux/airoha/patches-6.12/063-03-v6.15-net-airoha-Introduce-airoha_dev_change_mtu-callback.patch new file mode 100644 index 00000000000..ba403459f35 --- /dev/null +++ b/target/linux/airoha/patches-6.12/063-03-v6.15-net-airoha-Introduce-airoha_dev_change_mtu-callback.patch @@ -0,0 +1,47 @@ +From 03b1b69f0662c46f258a45e4a7d7837351c11692 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:10 +0100 +Subject: [PATCH 3/6] net: airoha: Introduce airoha_dev_change_mtu callback + +Add airoha_dev_change_mtu callback to update the MTU of a running +device. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-3-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1726,6 +1726,20 @@ static void airoha_dev_get_stats64(struc + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + ++static int airoha_dev_change_mtu(struct net_device *dev, int mtu) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; ++ u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; ++ ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), ++ GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++ WRITE_ONCE(dev->mtu, mtu); ++ ++ return 0; ++} ++ + static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) + { +@@ -2418,6 +2432,7 @@ static const struct net_device_ops airoh + .ndo_init = airoha_dev_init, + .ndo_open = airoha_dev_open, + .ndo_stop = airoha_dev_stop, ++ .ndo_change_mtu = airoha_dev_change_mtu, + .ndo_select_queue = airoha_dev_select_queue, + .ndo_start_xmit = airoha_dev_xmit, + .ndo_get_stats64 = airoha_dev_get_stats64, diff --git a/target/linux/airoha/patches-6.12/063-04-v6.15-net-airoha-Increase-max-mtu-to-9k.patch b/target/linux/airoha/patches-6.12/063-04-v6.15-net-airoha-Increase-max-mtu-to-9k.patch new file mode 100644 index 00000000000..8771ff22db5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/063-04-v6.15-net-airoha-Increase-max-mtu-to-9k.patch @@ -0,0 +1,26 @@ +From 168ef0c1dee83c401896a0bca680e9f97b1ebd64 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:11 +0100 +Subject: [PATCH 4/6] net: airoha: Increase max mtu to 9k + +EN7581 SoC supports 9k maximum MTU. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-4-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -20,7 +20,7 @@ + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 +-#define AIROHA_MAX_MTU 2000 ++#define AIROHA_MAX_MTU 9216 + #define AIROHA_MAX_PACKET_SIZE 2048 + #define AIROHA_NUM_QOS_CHANNELS 4 + #define AIROHA_NUM_QOS_QUEUES 8 diff --git a/target/linux/airoha/patches-6.12/063-05-v6.15-net-airoha-Fix-lan4-support-in-airoha_qdma_get_gdm_p.patch b/target/linux/airoha/patches-6.12/063-05-v6.15-net-airoha-Fix-lan4-support-in-airoha_qdma_get_gdm_p.patch new file mode 100644 index 00000000000..1c3030afd0c --- /dev/null +++ b/target/linux/airoha/patches-6.12/063-05-v6.15-net-airoha-Fix-lan4-support-in-airoha_qdma_get_gdm_p.patch @@ -0,0 +1,29 @@ +From 35ea4f06fd33fc32f556a0c26d1d8340497fa7f8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:38:05 +0100 +Subject: [PATCH 5/6] net: airoha: Fix lan4 support in + airoha_qdma_get_gdm_port() + +EN7581 SoC supports lan{1,4} ports on MT7530 DSA switch. Fix lan4 +reported value in airoha_qdma_get_gdm_port routine. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-fix-lan4-v1-1-832417da4bb5@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -589,7 +589,7 @@ static int airoha_qdma_get_gdm_port(stru + + sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); + switch (sport) { +- case 0x10 ... 0x13: ++ case 0x10 ... 0x14: + port = 0; + break; + case 0x2 ... 0x4: diff --git a/target/linux/airoha/patches-6.12/063-06-v6.15-net-airoha-Enable-TSO-Scatter-Gather-for-LAN-port.patch b/target/linux/airoha/patches-6.12/063-06-v6.15-net-airoha-Enable-TSO-Scatter-Gather-for-LAN-port.patch new file mode 100644 index 00000000000..61d7e5fe4bb --- /dev/null +++ b/target/linux/airoha/patches-6.12/063-06-v6.15-net-airoha-Enable-TSO-Scatter-Gather-for-LAN-port.patch @@ -0,0 +1,27 @@ +From a202dfe31cae2f2120297a7142385d80a5577d42 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 16:46:40 +0100 +Subject: [PATCH 6/6] net: airoha: Enable TSO/Scatter Gather for LAN port + +Set net_device vlan_features in order to enable TSO and Scatter Gather +for DSA user ports. + +Reviewed-by: Mateusz Polchlopek +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-lan-enable-tso-v1-1-b398eb9976ba@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2523,6 +2523,7 @@ static int airoha_alloc_gdm_port(struct + NETIF_F_SG | NETIF_F_TSO | + NETIF_F_HW_TC; + dev->features |= dev->hw_features; ++ dev->vlan_features = dev->hw_features; + dev->dev.of_node = np; + dev->irq = qdma->irq; + SET_NETDEV_DEV(dev, eth->dev); diff --git a/target/linux/airoha/patches-6.12/064-v6.15-net-airoha-Fix-dev-dsa_ptr-check-in-airoha_get_dsa_t.patch b/target/linux/airoha/patches-6.12/064-v6.15-net-airoha-Fix-dev-dsa_ptr-check-in-airoha_get_dsa_t.patch new file mode 100644 index 00000000000..ef8c11de793 --- /dev/null +++ b/target/linux/airoha/patches-6.12/064-v6.15-net-airoha-Fix-dev-dsa_ptr-check-in-airoha_get_dsa_t.patch @@ -0,0 +1,47 @@ +From e368d2a1e8b6f0926e4e76a56b484249905192f5 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 6 Mar 2025 11:52:20 +0100 +Subject: [PATCH] net: airoha: Fix dev->dsa_ptr check in airoha_get_dsa_tag() + +Fix the following warning reported by Smatch static checker in +airoha_get_dsa_tag routine: + +drivers/net/ethernet/airoha/airoha_eth.c:1722 airoha_get_dsa_tag() +warn: 'dp' isn't an ERR_PTR + +dev->dsa_ptr can't be set to an error pointer, it can just be NULL. +Remove this check since it is already performed in netdev_uses_dsa(). + +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/netdev/Z8l3E0lGOcrel07C@lore-desk/T/#m54adc113fcdd8c5e6c5f65ffd60d8e8b1d483d90 +Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250306-airoha-flowtable-fixes-v1-1-68d3c1296cdd@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1762,18 +1762,13 @@ static u32 airoha_get_dsa_tag(struct sk_ + { + #if IS_ENABLED(CONFIG_NET_DSA) + struct ethhdr *ehdr; +- struct dsa_port *dp; + u8 xmit_tpid; + u16 tag; + + if (!netdev_uses_dsa(dev)) + return 0; + +- dp = dev->dsa_ptr; +- if (IS_ERR(dp)) +- return 0; +- +- if (dp->tag_ops->proto != DSA_TAG_PROTO_MTK) ++ if (dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) + return 0; + + if (skb_cow_head(skb, 0)) diff --git a/target/linux/airoha/patches-6.12/065-v6.15-net-airoha-fix-CONFIG_DEBUG_FS-check.patch b/target/linux/airoha/patches-6.12/065-v6.15-net-airoha-fix-CONFIG_DEBUG_FS-check.patch new file mode 100644 index 00000000000..a8467408ed5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/065-v6.15-net-airoha-fix-CONFIG_DEBUG_FS-check.patch @@ -0,0 +1,35 @@ +From 08d0185e36ad8bb5902a73711bf114765d282161 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Fri, 14 Mar 2025 16:49:59 +0100 +Subject: [PATCH] net: airoha: fix CONFIG_DEBUG_FS check + +The #if check causes a build failure when CONFIG_DEBUG_FS is turned +off: + +In file included from drivers/net/ethernet/airoha/airoha_eth.c:17: +drivers/net/ethernet/airoha/airoha_eth.h:543:5: error: "CONFIG_DEBUG_FS" is not defined, evaluates to 0 [-Werror=undef] + 543 | #if CONFIG_DEBUG_FS + | ^~~~~~~~~~~~~~~ + +Replace it with the correct #ifdef. + +Fixes: 3fe15c640f38 ("net: airoha: Introduce PPE debugfs support") +Signed-off-by: Arnd Bergmann +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250314155009.4114308-1-arnd@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -540,7 +540,7 @@ void airoha_ppe_deinit(struct airoha_eth + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); + +-#if CONFIG_DEBUG_FS ++#ifdef CONFIG_DEBUG_FS + int airoha_ppe_debugfs_init(struct airoha_ppe *ppe); + #else + static inline int airoha_ppe_debugfs_init(struct airoha_ppe *ppe) diff --git a/target/linux/airoha/patches-6.12/066-01-v6.15-net-airoha-Fix-qid-report-in-airoha_tc_get_htb_get_l.patch b/target/linux/airoha/patches-6.12/066-01-v6.15-net-airoha-Fix-qid-report-in-airoha_tc_get_htb_get_l.patch new file mode 100644 index 00000000000..bbe441a9d7d --- /dev/null +++ b/target/linux/airoha/patches-6.12/066-01-v6.15-net-airoha-Fix-qid-report-in-airoha_tc_get_htb_get_l.patch @@ -0,0 +1,77 @@ +From 57b290d97c6150774bf929117ca737a26d8fc33d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 31 Mar 2025 08:52:53 +0200 +Subject: [PATCH 1/2] net: airoha: Fix qid report in + airoha_tc_get_htb_get_leaf_queue() + +Fix the following kernel warning deleting HTB offloaded leafs and/or root +HTB qdisc in airoha_eth driver properly reporting qid in +airoha_tc_get_htb_get_leaf_queue routine. + +$tc qdisc replace dev eth1 root handle 10: htb offload +$tc class add dev eth1 arent 10: classid 10:4 htb rate 100mbit ceil 100mbit +$tc qdisc replace dev eth1 parent 10:4 handle 4: ets bands 8 \ + quanta 1514 3028 4542 6056 7570 9084 10598 12112 +$tc qdisc del dev eth1 root + +[ 55.827864] ------------[ cut here ]------------ +[ 55.832493] WARNING: CPU: 3 PID: 2678 at 0xffffffc0798695a4 +[ 55.956510] CPU: 3 PID: 2678 Comm: tc Tainted: G O 6.6.71 #0 +[ 55.963557] Hardware name: Airoha AN7581 Evaluation Board (DT) +[ 55.969383] pstate: 20400005 (nzCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 55.976344] pc : 0xffffffc0798695a4 +[ 55.979851] lr : 0xffffffc079869a20 +[ 55.983358] sp : ffffffc0850536a0 +[ 55.986665] x29: ffffffc0850536a0 x28: 0000000000000024 x27: 0000000000000001 +[ 55.993800] x26: 0000000000000000 x25: ffffff8008b19000 x24: ffffff800222e800 +[ 56.000935] x23: 0000000000000001 x22: 0000000000000000 x21: ffffff8008b19000 +[ 56.008071] x20: ffffff8002225800 x19: ffffff800379d000 x18: 0000000000000000 +[ 56.015206] x17: ffffffbf9ea59000 x16: ffffffc080018000 x15: 0000000000000000 +[ 56.022342] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000001 +[ 56.029478] x11: ffffffc081471008 x10: ffffffc081575a98 x9 : 0000000000000000 +[ 56.036614] x8 : ffffffc08167fd40 x7 : ffffffc08069e104 x6 : ffffff8007f86000 +[ 56.043748] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000001 +[ 56.050884] x2 : 0000000000000000 x1 : 0000000000000250 x0 : ffffff800222c000 +[ 56.058020] Call trace: +[ 56.060459] 0xffffffc0798695a4 +[ 56.063618] 0xffffffc079869a20 +[ 56.066777] __qdisc_destroy+0x40/0xa0 +[ 56.070528] qdisc_put+0x54/0x6c +[ 56.073748] qdisc_graft+0x41c/0x648 +[ 56.077324] tc_get_qdisc+0x168/0x2f8 +[ 56.080978] rtnetlink_rcv_msg+0x230/0x330 +[ 56.085076] netlink_rcv_skb+0x5c/0x128 +[ 56.088913] rtnetlink_rcv+0x14/0x1c +[ 56.092490] netlink_unicast+0x1e0/0x2c8 +[ 56.096413] netlink_sendmsg+0x198/0x3c8 +[ 56.100337] ____sys_sendmsg+0x1c4/0x274 +[ 56.104261] ___sys_sendmsg+0x7c/0xc0 +[ 56.107924] __sys_sendmsg+0x44/0x98 +[ 56.111492] __arm64_sys_sendmsg+0x20/0x28 +[ 56.115580] invoke_syscall.constprop.0+0x58/0xfc +[ 56.120285] do_el0_svc+0x3c/0xbc +[ 56.123592] el0_svc+0x18/0x4c +[ 56.126647] el0t_64_sync_handler+0x118/0x124 +[ 56.131005] el0t_64_sync+0x150/0x154 +[ 56.134660] ---[ end trace 0000000000000000 ]--- + +Fixes: ef1ca9271313b ("net: airoha: Add sched HTB offload support") +Signed-off-by: Lorenzo Bianconi +Acked-by: Paolo Abeni +Link: https://patch.msgid.link/20250331-airoha-htb-qdisc-offload-del-fix-v1-1-4ea429c2c968@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2376,7 +2376,7 @@ static int airoha_tc_get_htb_get_leaf_qu + return -EINVAL; + } + +- opt->qid = channel; ++ opt->qid = AIROHA_NUM_TX_RING + channel; + + return 0; + } diff --git a/target/linux/airoha/patches-6.12/066-02-v6.15-net-airoha-Fix-ETS-priomap-validation.patch b/target/linux/airoha/patches-6.12/066-02-v6.15-net-airoha-Fix-ETS-priomap-validation.patch new file mode 100644 index 00000000000..915fcf6a2b7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/066-02-v6.15-net-airoha-Fix-ETS-priomap-validation.patch @@ -0,0 +1,58 @@ +From 367579274f60cb23c570ae5348966ab51e1509a4 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 31 Mar 2025 18:17:31 +0200 +Subject: [PATCH 2/2] net: airoha: Fix ETS priomap validation + +ETS Qdisc schedules SP bands in a priority order assigning band-0 the +highest priority (band-0 > band-1 > .. > band-n) while EN7581 arranges +SP bands in a priority order assigning band-7 the highest priority +(band-7 > band-6, .. > band-n). +Fix priomap check in airoha_qdma_set_tx_ets_sched routine in order to +align ETS Qdisc and airoha_eth driver SP priority ordering. + +Fixes: b56e4d660a96 ("net: airoha: Enforce ETS Qdisc priomap") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Reviewed-by: Davide Caratti +Link: https://patch.msgid.link/20250331-airoha-ets-validate-priomap-v1-1-60a524488672@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2049,7 +2049,7 @@ static int airoha_qdma_set_tx_ets_sched( + struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; + enum tx_sched_mode mode = TC_SCH_SP; + u16 w[AIROHA_NUM_QOS_QUEUES] = {}; +- int i, nstrict = 0, nwrr, qidx; ++ int i, nstrict = 0; + + if (p->bands > AIROHA_NUM_QOS_QUEUES) + return -EINVAL; +@@ -2067,17 +2067,17 @@ static int airoha_qdma_set_tx_ets_sched( + * lowest priorities with respect to SP ones. + * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn + */ +- nwrr = p->bands - nstrict; +- qidx = nstrict && nwrr ? nstrict : 0; +- for (i = 1; i <= p->bands; i++) { +- if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx) ++ for (i = 0; i < nstrict; i++) { ++ if (p->priomap[p->bands - i - 1] != i) + return -EINVAL; +- +- qidx = i == nwrr ? 0 : qidx + 1; + } + +- for (i = 0; i < nwrr; i++) ++ for (i = 0; i < p->bands - nstrict; i++) { ++ if (p->priomap[i] != nstrict + i) ++ return -EINVAL; ++ + w[i] = p->weights[nstrict + i]; ++ } + + if (!nstrict) + mode = TC_SCH_WRR8; diff --git a/target/linux/airoha/patches-6.12/067-v6.15-net-airoha-Validate-egress-gdm-port-in-airoha_ppe_fo.patch b/target/linux/airoha/patches-6.12/067-v6.15-net-airoha-Validate-egress-gdm-port-in-airoha_ppe_fo.patch new file mode 100644 index 00000000000..2138366d72d --- /dev/null +++ b/target/linux/airoha/patches-6.12/067-v6.15-net-airoha-Validate-egress-gdm-port-in-airoha_ppe_fo.patch @@ -0,0 +1,100 @@ +From 09bccf56db36501ccb1935d921dc24451e9f57dd Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 1 Apr 2025 11:42:30 +0200 +Subject: [PATCH] net: airoha: Validate egress gdm port in + airoha_ppe_foe_entry_prepare() + +Dev pointer in airoha_ppe_foe_entry_prepare routine is not strictly +a device allocated by airoha_eth driver since it is an egress device +and the flowtable can contain even wlan, pppoe or vlan devices. E.g: + +flowtable ft { + hook ingress priority filter + devices = { eth1, lan1, lan2, lan3, lan4, wlan0 } + flags offload ^ + | + "not allocated by airoha_eth" -- +} + +In this case airoha_get_dsa_port() will just return the original device +pointer and we can't assume netdev priv pointer points to an +airoha_gdm_port struct. +Fix the issue validating egress gdm port in airoha_ppe_foe_entry_prepare +routine before accessing net_device priv pointer. + +Fixes: 00a7678310fe ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250401-airoha-validate-egress-gdm-port-v4-1-c7315d33ce10@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 13 +++++++++++++ + drivers/net/ethernet/airoha/airoha_eth.h | 3 +++ + drivers/net/ethernet/airoha/airoha_ppe.c | 8 ++++++-- + 3 files changed, 22 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2472,6 +2472,19 @@ static void airoha_metadata_dst_free(str + } + } + ++bool airoha_is_valid_gdm_port(struct airoha_eth *eth, ++ struct airoha_gdm_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ if (eth->ports[i] == port) ++ return true; ++ } ++ ++ return false; ++} ++ + static int airoha_alloc_gdm_port(struct airoha_eth *eth, + struct device_node *np, int index) + { +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -532,6 +532,9 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + ++bool airoha_is_valid_gdm_port(struct airoha_eth *eth, ++ struct airoha_gdm_port *port); ++ + void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash); + int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -197,7 +197,8 @@ static int airoha_get_dsa_port(struct ne + #endif + } + +-static int airoha_ppe_foe_entry_prepare(struct airoha_foe_entry *hwe, ++static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, ++ struct airoha_foe_entry *hwe, + struct net_device *dev, int type, + struct airoha_flow_data *data, + int l4proto) +@@ -225,6 +226,9 @@ static int airoha_ppe_foe_entry_prepare( + struct airoha_gdm_port *port = netdev_priv(dev); + u8 pse_port; + ++ if (!airoha_is_valid_gdm_port(eth, port)) ++ return -EINVAL; ++ + if (dsa_port >= 0) + pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; + else +@@ -633,7 +637,7 @@ static int airoha_ppe_flow_offload_repla + !is_valid_ether_addr(data.eth.h_dest)) + return -EINVAL; + +- err = airoha_ppe_foe_entry_prepare(&hwe, odev, offload_type, ++ err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type, + &data, l4proto); + if (err) + return err; diff --git a/target/linux/airoha/patches-6.12/068-01-v6.16-net-airoha-Add-l2_flows-rhashtable.patch b/target/linux/airoha/patches-6.12/068-01-v6.16-net-airoha-Add-l2_flows-rhashtable.patch new file mode 100644 index 00000000000..95f83f53bdb --- /dev/null +++ b/target/linux/airoha/patches-6.12/068-01-v6.16-net-airoha-Add-l2_flows-rhashtable.patch @@ -0,0 +1,207 @@ +From b4916f67902e2ae1dc8e37dfa45e8894ad2f8921 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 9 Apr 2025 11:47:14 +0200 +Subject: [PATCH 1/2] net: airoha: Add l2_flows rhashtable + +Introduce l2_flows rhashtable in airoha_ppe struct in order to +store L2 flows committed by upper layers of the kernel. This is a +preliminary patch in order to offload L2 traffic rules. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Michal Kubiak +Link: https://patch.msgid.link/20250409-airoha-flowtable-l2b-v2-1-4a1e3935ea92@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.h | 15 +++- + drivers/net/ethernet/airoha/airoha_ppe.c | 103 ++++++++++++++++++----- + 2 files changed, 98 insertions(+), 20 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -422,12 +422,23 @@ struct airoha_flow_data { + } pppoe; + }; + ++enum airoha_flow_entry_type { ++ FLOW_TYPE_L4, ++ FLOW_TYPE_L2, ++ FLOW_TYPE_L2_SUBFLOW, ++}; ++ + struct airoha_flow_table_entry { +- struct hlist_node list; ++ union { ++ struct hlist_node list; /* PPE L3 flow entry */ ++ struct rhash_head l2_node; /* L2 flow entry */ ++ }; + + struct airoha_foe_entry data; + u32 hash; + ++ enum airoha_flow_entry_type type; ++ + struct rhash_head node; + unsigned long cookie; + }; +@@ -480,6 +491,8 @@ struct airoha_ppe { + void *foe; + dma_addr_t foe_dma; + ++ struct rhashtable l2_flows; ++ + struct hlist_head *foe_flow; + u16 foe_check_time[PPE_NUM_ENTRIES]; + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -24,6 +24,13 @@ static const struct rhashtable_params ai + .automatic_shrinking = true, + }; + ++static const struct rhashtable_params airoha_l2_flow_table_params = { ++ .head_offset = offsetof(struct airoha_flow_table_entry, l2_node), ++ .key_offset = offsetof(struct airoha_flow_table_entry, data.bridge), ++ .key_len = 2 * ETH_ALEN, ++ .automatic_shrinking = true, ++}; ++ + static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) + { + return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; +@@ -476,6 +483,43 @@ static int airoha_ppe_foe_commit_entry(s + return 0; + } + ++static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ lockdep_assert_held(&ppe_lock); ++ ++ hlist_del_init(&e->list); ++ if (e->hash != 0xffff) { ++ e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; ++ e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, ++ AIROHA_FOE_STATE_INVALID); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); ++ e->hash = 0xffff; ++ } ++} ++ ++static void airoha_ppe_foe_remove_l2_flow(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ lockdep_assert_held(&ppe_lock); ++ ++ rhashtable_remove_fast(&ppe->l2_flows, &e->l2_node, ++ airoha_l2_flow_table_params); ++} ++ ++static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ spin_lock_bh(&ppe_lock); ++ ++ if (e->type == FLOW_TYPE_L2) ++ airoha_ppe_foe_remove_l2_flow(ppe, e); ++ else ++ airoha_ppe_foe_remove_flow(ppe, e); ++ ++ spin_unlock_bh(&ppe_lock); ++} ++ + static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash) + { + struct airoha_flow_table_entry *e; +@@ -505,11 +549,37 @@ unlock: + spin_unlock_bh(&ppe_lock); + } + ++static int ++airoha_ppe_foe_l2_flow_commit_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ struct airoha_flow_table_entry *prev; ++ ++ e->type = FLOW_TYPE_L2; ++ prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &e->l2_node, ++ airoha_l2_flow_table_params); ++ if (!prev) ++ return 0; ++ ++ if (IS_ERR(prev)) ++ return PTR_ERR(prev); ++ ++ return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node, ++ &e->l2_node, ++ airoha_l2_flow_table_params); ++} ++ + static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e) + { +- u32 hash = airoha_ppe_foe_get_entry_hash(&e->data); ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1); ++ u32 hash; + ++ if (type == PPE_PKT_TYPE_BRIDGE) ++ return airoha_ppe_foe_l2_flow_commit_entry(ppe, e); ++ ++ hash = airoha_ppe_foe_get_entry_hash(&e->data); ++ e->type = FLOW_TYPE_L4; + e->hash = 0xffff; + + spin_lock_bh(&ppe_lock); +@@ -519,23 +589,6 @@ static int airoha_ppe_foe_flow_commit_en + return 0; + } + +-static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, +- struct airoha_flow_table_entry *e) +-{ +- spin_lock_bh(&ppe_lock); +- +- hlist_del_init(&e->list); +- if (e->hash != 0xffff) { +- e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; +- e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, +- AIROHA_FOE_STATE_INVALID); +- airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); +- e->hash = 0xffff; +- } +- +- spin_unlock_bh(&ppe_lock); +-} +- + static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, + struct flow_cls_offload *f) + { +@@ -890,9 +943,20 @@ int airoha_ppe_init(struct airoha_eth *e + if (err) + return err; + ++ err = rhashtable_init(&ppe->l2_flows, &airoha_l2_flow_table_params); ++ if (err) ++ goto error_flow_table_destroy; ++ + err = airoha_ppe_debugfs_init(ppe); + if (err) +- rhashtable_destroy(ð->flow_table); ++ goto error_l2_flow_table_destroy; ++ ++ return 0; ++ ++error_l2_flow_table_destroy: ++ rhashtable_destroy(&ppe->l2_flows); ++error_flow_table_destroy: ++ rhashtable_destroy(ð->flow_table); + + return err; + } +@@ -909,6 +973,7 @@ void airoha_ppe_deinit(struct airoha_eth + } + rcu_read_unlock(); + ++ rhashtable_destroy(ð->ppe->l2_flows); + rhashtable_destroy(ð->flow_table); + debugfs_remove(eth->ppe->debugfs_dir); + } diff --git a/target/linux/airoha/patches-6.12/068-02-v6.16-net-airoha-Add-L2-hw-acceleration-support.patch b/target/linux/airoha/patches-6.12/068-02-v6.16-net-airoha-Add-L2-hw-acceleration-support.patch new file mode 100644 index 00000000000..2375962338b --- /dev/null +++ b/target/linux/airoha/patches-6.12/068-02-v6.16-net-airoha-Add-L2-hw-acceleration-support.patch @@ -0,0 +1,253 @@ +From cd53f622611f9a6dd83b858c85448dd3568b67ec Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 9 Apr 2025 11:47:15 +0200 +Subject: [PATCH 2/2] net: airoha: Add L2 hw acceleration support + +Similar to mtk driver, introduce the capability to offload L2 traffic +defining flower rules in the PSE/PPE engine available on EN7581 SoC. +Since the hw always reports L2/L3/L4 flower rules, link all L2 rules +sharing the same L2 info (with different L3/L4 info) in the L2 subflows +list of a given L2 PPE entry. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Michal Kubiak +Link: https://patch.msgid.link/20250409-airoha-flowtable-l2b-v2-2-4a1e3935ea92@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + drivers/net/ethernet/airoha/airoha_eth.h | 9 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 121 ++++++++++++++++++++--- + 3 files changed, 115 insertions(+), 17 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -694,7 +694,7 @@ static int airoha_qdma_rx_process(struct + + reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) +- airoha_ppe_check_skb(eth->ppe, hash); ++ airoha_ppe_check_skb(eth->ppe, q->skb, hash); + + done++; + napi_gro_receive(&q->napi, q->skb); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -431,10 +431,14 @@ enum airoha_flow_entry_type { + struct airoha_flow_table_entry { + union { + struct hlist_node list; /* PPE L3 flow entry */ +- struct rhash_head l2_node; /* L2 flow entry */ ++ struct { ++ struct rhash_head l2_node; /* L2 flow entry */ ++ struct hlist_head l2_flows; /* PPE L2 subflows list */ ++ }; + }; + + struct airoha_foe_entry data; ++ struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ + u32 hash; + + enum airoha_flow_entry_type type; +@@ -548,7 +552,8 @@ u32 airoha_rmw(void __iomem *base, u32 o + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash); ++void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, ++ u16 hash); + int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv); + int airoha_ppe_init(struct airoha_eth *eth); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -204,6 +204,15 @@ static int airoha_get_dsa_port(struct ne + #endif + } + ++static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br, ++ struct ethhdr *eh) ++{ ++ br->dest_mac_hi = get_unaligned_be32(eh->h_dest); ++ br->dest_mac_lo = get_unaligned_be16(eh->h_dest + 4); ++ br->src_mac_hi = get_unaligned_be16(eh->h_source); ++ br->src_mac_lo = get_unaligned_be32(eh->h_source + 2); ++} ++ + static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, + struct airoha_foe_entry *hwe, + struct net_device *dev, int type, +@@ -254,13 +263,7 @@ static int airoha_ppe_foe_entry_prepare( + + qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); + if (type == PPE_PKT_TYPE_BRIDGE) { +- hwe->bridge.dest_mac_hi = get_unaligned_be32(data->eth.h_dest); +- hwe->bridge.dest_mac_lo = +- get_unaligned_be16(data->eth.h_dest + 4); +- hwe->bridge.src_mac_hi = +- get_unaligned_be16(data->eth.h_source); +- hwe->bridge.src_mac_lo = +- get_unaligned_be32(data->eth.h_source + 2); ++ airoha_ppe_foe_set_bridge_addrs(&hwe->bridge, &data->eth); + hwe->bridge.data = qdata; + hwe->bridge.ib2 = val; + l2 = &hwe->bridge.l2.common; +@@ -385,6 +388,19 @@ static u32 airoha_ppe_foe_get_entry_hash + hv3 = hwe->ipv6.src_ip[1] ^ hwe->ipv6.dest_ip[1]; + hv3 ^= hwe->ipv6.src_ip[0]; + break; ++ case PPE_PKT_TYPE_BRIDGE: { ++ struct airoha_foe_mac_info *l2 = &hwe->bridge.l2; ++ ++ hv1 = l2->common.src_mac_hi & 0xffff; ++ hv1 = hv1 << 16 | l2->src_mac_lo; ++ ++ hv2 = l2->common.dest_mac_lo; ++ hv2 = hv2 << 16; ++ hv2 = hv2 | ((l2->common.src_mac_hi & 0xffff0000) >> 16); ++ ++ hv3 = l2->common.dest_mac_hi; ++ break; ++ } + case PPE_PKT_TYPE_IPV4_DSLITE: + case PPE_PKT_TYPE_IPV6_6RD: + default: +@@ -496,15 +512,24 @@ static void airoha_ppe_foe_remove_flow(s + airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); + e->hash = 0xffff; + } ++ if (e->type == FLOW_TYPE_L2_SUBFLOW) { ++ hlist_del_init(&e->l2_subflow_node); ++ kfree(e); ++ } + } + + static void airoha_ppe_foe_remove_l2_flow(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e) + { ++ struct hlist_head *head = &e->l2_flows; ++ struct hlist_node *n; ++ + lockdep_assert_held(&ppe_lock); + + rhashtable_remove_fast(&ppe->l2_flows, &e->l2_node, + airoha_l2_flow_table_params); ++ hlist_for_each_entry_safe(e, n, head, l2_subflow_node) ++ airoha_ppe_foe_remove_flow(ppe, e); + } + + static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, +@@ -520,10 +545,56 @@ static void airoha_ppe_foe_flow_remove_e + spin_unlock_bh(&ppe_lock); + } + +-static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash) ++static int ++airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e, ++ u32 hash) ++{ ++ u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; ++ struct airoha_foe_entry *hwe_p, hwe; ++ struct airoha_flow_table_entry *f; ++ struct airoha_foe_mac_info *l2; ++ int type; ++ ++ hwe_p = airoha_ppe_foe_get_entry(ppe, hash); ++ if (!hwe_p) ++ return -EINVAL; ++ ++ f = kzalloc(sizeof(*f), GFP_ATOMIC); ++ if (!f) ++ return -ENOMEM; ++ ++ hlist_add_head(&f->l2_subflow_node, &e->l2_flows); ++ f->type = FLOW_TYPE_L2_SUBFLOW; ++ f->hash = hash; ++ ++ memcpy(&hwe, hwe_p, sizeof(*hwe_p)); ++ hwe.ib1 = (hwe.ib1 & mask) | (e->data.ib1 & ~mask); ++ l2 = &hwe.bridge.l2; ++ memcpy(l2, &e->data.bridge.l2, sizeof(*l2)); ++ ++ type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); ++ if (type == PPE_PKT_TYPE_IPV4_HNAPT) ++ memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, ++ sizeof(hwe.ipv4.new_tuple)); ++ else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T && ++ l2->common.etype == ETH_P_IP) ++ l2->common.etype = ETH_P_IPV6; ++ ++ hwe.bridge.ib2 = e->data.bridge.ib2; ++ airoha_ppe_foe_commit_entry(ppe, &hwe, hash); ++ ++ return 0; ++} ++ ++static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, ++ struct sk_buff *skb, ++ u32 hash) + { + struct airoha_flow_table_entry *e; ++ struct airoha_foe_bridge br = {}; + struct airoha_foe_entry *hwe; ++ bool commit_done = false; + struct hlist_node *n; + u32 index, state; + +@@ -539,12 +610,33 @@ static void airoha_ppe_foe_insert_entry( + + index = airoha_ppe_foe_get_entry_hash(hwe); + hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) { +- if (airoha_ppe_foe_compare_entry(e, hwe)) { +- airoha_ppe_foe_commit_entry(ppe, &e->data, hash); +- e->hash = hash; +- break; ++ if (e->type == FLOW_TYPE_L2_SUBFLOW) { ++ state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); ++ if (state != AIROHA_FOE_STATE_BIND) { ++ e->hash = 0xffff; ++ airoha_ppe_foe_remove_flow(ppe, e); ++ } ++ continue; ++ } ++ ++ if (commit_done || !airoha_ppe_foe_compare_entry(e, hwe)) { ++ e->hash = 0xffff; ++ continue; + } ++ ++ airoha_ppe_foe_commit_entry(ppe, &e->data, hash); ++ commit_done = true; ++ e->hash = hash; + } ++ ++ if (commit_done) ++ goto unlock; ++ ++ airoha_ppe_foe_set_bridge_addrs(&br, eth_hdr(skb)); ++ e = rhashtable_lookup_fast(&ppe->l2_flows, &br, ++ airoha_l2_flow_table_params); ++ if (e) ++ airoha_ppe_foe_commit_subflow_entry(ppe, e, hash); + unlock: + spin_unlock_bh(&ppe_lock); + } +@@ -899,7 +991,8 @@ int airoha_ppe_setup_tc_block_cb(enum tc + return err; + } + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash) ++void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, ++ u16 hash) + { + u16 now, diff; + +@@ -912,7 +1005,7 @@ void airoha_ppe_check_skb(struct airoha_ + return; + + ppe->foe_check_time[hash] = now; +- airoha_ppe_foe_insert_entry(ppe, hash); ++ airoha_ppe_foe_insert_entry(ppe, skb, hash); + } + + int airoha_ppe_init(struct airoha_eth *eth) diff --git a/target/linux/airoha/patches-6.12/069-v6.16-net-airoha-Add-matchall-filter-offload-support.patch b/target/linux/airoha/patches-6.12/069-v6.16-net-airoha-Add-matchall-filter-offload-support.patch new file mode 100644 index 00000000000..632ac8fd4f6 --- /dev/null +++ b/target/linux/airoha/patches-6.12/069-v6.16-net-airoha-Add-matchall-filter-offload-support.patch @@ -0,0 +1,405 @@ +From df8398fb7bb7a0e509200af56b79343aa133b7d6 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 15 Apr 2025 09:14:34 +0200 +Subject: [PATCH] net: airoha: Add matchall filter offload support + +Introduce tc matchall filter offload support in airoha_eth driver. +Matchall hw filter is used to implement hw rate policing via tc action +police: + +$tc qdisc add dev eth0 handle ffff: ingress +$tc filter add dev eth0 parent ffff: matchall action police \ + rate 100mbit burst 1000k drop + +The current implementation supports just drop/accept as exceed/notexceed +actions. Moreover, rate and burst are the only supported configuration +parameters. + +Reviewed-by: Davide Caratti +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250415-airoha-hw-rx-ratelimit-v4-1-03458784fbc3@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 273 +++++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_eth.h | 8 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 9 +- + drivers/net/ethernet/airoha/airoha_regs.h | 7 + + 4 files changed, 286 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -527,6 +527,25 @@ static int airoha_fe_init(struct airoha_ + /* disable IFC by default */ + airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); + ++ airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), ++ FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1)); ++ airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1), ++ FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2)); ++ + /* enable 1:N vlan action, init vlan table */ + airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); + +@@ -1652,7 +1671,6 @@ static void airhoha_set_gdm2_loopback(st + + if (port->id == 3) { + /* FIXME: handle XSI_PCE1_PORT */ +- airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), 0x5500); + airoha_fe_rmw(eth, REG_FE_WAN_PORT, + WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, + FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT)); +@@ -2127,6 +2145,125 @@ static int airoha_tc_setup_qdisc_ets(str + } + } + ++static int airoha_qdma_get_rl_param(struct airoha_qdma *qdma, int queue_id, ++ u32 addr, enum trtcm_param_type param, ++ u32 *val_low, u32 *val_high) ++{ ++ u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); ++ u32 val, config = FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | ++ FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ if (read_poll_timeout(airoha_qdma_rr, val, ++ val & RATE_LIMIT_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, qdma, ++ REG_TRTCM_CFG_PARAM(addr))) ++ return -ETIMEDOUT; ++ ++ *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); ++ if (val_high) ++ *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_set_rl_param(struct airoha_qdma *qdma, int queue_id, ++ u32 addr, enum trtcm_param_type param, ++ u32 val) ++{ ++ u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); ++ u32 config = RATE_LIMIT_PARAM_RW_MASK | ++ FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | ++ FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ ++ return read_poll_timeout(airoha_qdma_rr, val, ++ val & RATE_LIMIT_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, ++ qdma, REG_TRTCM_CFG_PARAM(addr)); ++} ++ ++static int airoha_qdma_set_rl_config(struct airoha_qdma *qdma, int queue_id, ++ u32 addr, bool enable, u32 enable_mask) ++{ ++ u32 val; ++ int err; ++ ++ err = airoha_qdma_get_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, ++ &val, NULL); ++ if (err) ++ return err; ++ ++ val = enable ? val | enable_mask : val & ~enable_mask; ++ ++ return airoha_qdma_set_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, ++ val); ++} ++ ++static int airoha_qdma_set_rl_token_bucket(struct airoha_qdma *qdma, ++ int queue_id, u32 rate_val, ++ u32 bucket_size) ++{ ++ u32 val, config, tick, unit, rate, rate_frac; ++ int err; ++ ++ err = airoha_qdma_get_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ TRTCM_MISC_MODE, &config, NULL); ++ if (err) ++ return err; ++ ++ val = airoha_qdma_rr(qdma, REG_INGRESS_TRTCM_CFG); ++ tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); ++ if (config & TRTCM_TICK_SEL) ++ tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); ++ if (!tick) ++ return -EINVAL; ++ ++ unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; ++ if (!unit) ++ return -EINVAL; ++ ++ rate = rate_val / unit; ++ rate_frac = rate_val % unit; ++ rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; ++ rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | ++ FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); ++ ++ err = airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ TRTCM_TOKEN_RATE_MODE, rate); ++ if (err) ++ return err; ++ ++ val = bucket_size; ++ if (!(config & TRTCM_PKT_MODE)) ++ val = max_t(u32, val, MIN_TOKEN_SIZE); ++ val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); ++ ++ return airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, val); ++} ++ ++static int airoha_qdma_init_rl_config(struct airoha_qdma *qdma, int queue_id, ++ bool enable, enum trtcm_unit_type unit) ++{ ++ bool tick_sel = queue_id == 0 || queue_id == 2 || queue_id == 8; ++ enum trtcm_param mode = TRTCM_METER_MODE; ++ int err; ++ ++ mode |= unit == TRTCM_PACKET_UNIT ? TRTCM_PKT_MODE : 0; ++ err = airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ enable, mode); ++ if (err) ++ return err; ++ ++ return airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ tick_sel, TRTCM_TICK_SEL); ++} ++ + static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, + u32 addr, enum trtcm_param_type param, + enum trtcm_mode_type mode, +@@ -2291,10 +2428,142 @@ static int airoha_tc_htb_alloc_leaf_queu + return 0; + } + ++static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port, ++ u32 rate, u32 bucket_size, ++ enum trtcm_unit_type unit_type) ++{ ++ struct airoha_qdma *qdma = port->qdma; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ int err; ++ ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ err = airoha_qdma_init_rl_config(qdma, i, !!rate, unit_type); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_set_rl_token_bucket(qdma, i, rate, ++ bucket_size); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f) ++{ ++ const struct flow_action *actions = &f->rule->action; ++ const struct flow_action_entry *act; ++ ++ if (!flow_action_has_entries(actions)) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "filter run with no actions"); ++ return -EINVAL; ++ } ++ ++ if (!flow_offload_has_one_action(actions)) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "only once action per filter is supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ act = &actions->entries[0]; ++ if (act->id != FLOW_ACTION_POLICE) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, "unsupported action"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.exceed.act_id != FLOW_ACTION_DROP) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "invalid exceed action id"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "invalid notexceed action id"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && ++ !flow_action_is_last_entry(actions, act)) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "action accept must be last"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.peakrate_bytes_ps || act->police.avrate || ++ act->police.overhead || act->police.mtu) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "peakrate/avrate/overhead/mtu unsupported"); ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int airoha_dev_tc_matchall(struct net_device *dev, ++ struct tc_cls_matchall_offload *f) ++{ ++ enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u32 rate = 0, bucket_size = 0; ++ ++ switch (f->command) { ++ case TC_CLSMATCHALL_REPLACE: { ++ const struct flow_action_entry *act; ++ int err; ++ ++ err = airoha_tc_matchall_act_validate(f); ++ if (err) ++ return err; ++ ++ act = &f->rule->action.entries[0]; ++ if (act->police.rate_pkt_ps) { ++ rate = act->police.rate_pkt_ps; ++ bucket_size = act->police.burst_pkt; ++ unit_type = TRTCM_PACKET_UNIT; ++ } else { ++ rate = div_u64(act->police.rate_bytes_ps, 1000); ++ rate = rate << 3; /* Kbps */ ++ bucket_size = act->police.burst; ++ } ++ fallthrough; ++ } ++ case TC_CLSMATCHALL_DESTROY: ++ return airoha_qdma_set_rx_meter(port, rate, bucket_size, ++ unit_type); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type, ++ void *type_data, void *cb_priv) ++{ ++ struct net_device *dev = cb_priv; ++ ++ if (!tc_can_offload(dev)) ++ return -EOPNOTSUPP; ++ ++ switch (type) { ++ case TC_SETUP_CLSFLOWER: ++ return airoha_ppe_setup_tc_block_cb(dev, type_data); ++ case TC_SETUP_CLSMATCHALL: ++ return airoha_dev_tc_matchall(dev, type_data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ + static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port, + struct flow_block_offload *f) + { +- flow_setup_cb_t *cb = airoha_ppe_setup_tc_block_cb; ++ flow_setup_cb_t *cb = airoha_dev_setup_tc_block_cb; + static LIST_HEAD(block_cb_list); + struct flow_block_cb *block_cb; + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -127,6 +127,11 @@ enum tx_sched_mode { + TC_SCH_WRR2, + }; + ++enum trtcm_unit_type { ++ TRTCM_BYTE_UNIT, ++ TRTCM_PACKET_UNIT, ++}; ++ + enum trtcm_param_type { + TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ + TRTCM_TOKEN_RATE_MODE, +@@ -554,8 +559,7 @@ bool airoha_is_valid_gdm_port(struct air + + void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, + u16 hash); +-int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, +- void *cb_priv); ++int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -967,18 +967,13 @@ error_npu_put: + return err; + } + +-int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, +- void *cb_priv) ++int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) + { +- struct flow_cls_offload *cls = type_data; +- struct net_device *dev = cb_priv; + struct airoha_gdm_port *port = netdev_priv(dev); ++ struct flow_cls_offload *cls = type_data; + struct airoha_eth *eth = port->qdma->eth; + int err = 0; + +- if (!tc_can_offload(dev) || type != TC_SETUP_CLSFLOWER) +- return -EOPNOTSUPP; +- + mutex_lock(&flow_offload_mutex); + + if (!eth->npu) +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -283,6 +283,7 @@ + #define PPE_HASH_SEED 0x12345678 + + #define REG_PPE_DFT_CPORT0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248) ++#define DFT_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2)) + + #define REG_PPE_DFT_CPORT1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x24c) + +@@ -691,6 +692,12 @@ + #define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) + #define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) + ++#define RATE_LIMIT_PARAM_RW_MASK BIT(31) ++#define RATE_LIMIT_PARAM_RW_DONE_MASK BIT(30) ++#define RATE_LIMIT_PARAM_TYPE_MASK GENMASK(29, 28) ++#define RATE_LIMIT_METER_GROUP_MASK GENMASK(27, 26) ++#define RATE_LIMIT_PARAM_INDEX_MASK GENMASK(23, 16) ++ + #define REG_TXWRR_MODE_CFG 0x1020 + #define TWRR_WEIGHT_SCALE_MASK BIT(31) + #define TWRR_WEIGHT_BASE_MASK BIT(3) diff --git a/target/linux/airoha/patches-6.12/070-01-v6.16-net-airoha-Introduce-airoha_irq_bank-struct.patch b/target/linux/airoha/patches-6.12/070-01-v6.16-net-airoha-Introduce-airoha_irq_bank-struct.patch new file mode 100644 index 00000000000..773cb12f7e1 --- /dev/null +++ b/target/linux/airoha/patches-6.12/070-01-v6.16-net-airoha-Introduce-airoha_irq_bank-struct.patch @@ -0,0 +1,292 @@ +From 9439db26d3ee4a897e5cd108864172531f31ce07 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 18 Apr 2025 12:40:49 +0200 +Subject: [PATCH 1/2] net: airoha: Introduce airoha_irq_bank struct + +EN7581 ethernet SoC supports 4 programmable IRQ lines each one composed +by 4 IRQ configuration registers. Add airoha_irq_bank struct as a +container for independent IRQ lines info (e.g. IRQ number, enabled source +interrupts, ecc). This is a preliminary patch to support multiple IRQ lines +in airoha_eth driver. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250418-airoha-eth-multi-irq-v1-1-1ab0083ca3c1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 106 ++++++++++++++-------- + drivers/net/ethernet/airoha/airoha_eth.h | 13 ++- + drivers/net/ethernet/airoha/airoha_regs.h | 11 ++- + 3 files changed, 86 insertions(+), 44 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -34,37 +34,40 @@ u32 airoha_rmw(void __iomem *base, u32 o + return val; + } + +-static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index, +- u32 clear, u32 set) ++static void airoha_qdma_set_irqmask(struct airoha_irq_bank *irq_bank, ++ int index, u32 clear, u32 set) + { ++ struct airoha_qdma *qdma = irq_bank->qdma; ++ int bank = irq_bank - &qdma->irq_banks[0]; + unsigned long flags; + +- if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask))) ++ if (WARN_ON_ONCE(index >= ARRAY_SIZE(irq_bank->irqmask))) + return; + +- spin_lock_irqsave(&qdma->irq_lock, flags); ++ spin_lock_irqsave(&irq_bank->irq_lock, flags); + +- qdma->irqmask[index] &= ~clear; +- qdma->irqmask[index] |= set; +- airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]); ++ irq_bank->irqmask[index] &= ~clear; ++ irq_bank->irqmask[index] |= set; ++ airoha_qdma_wr(qdma, REG_INT_ENABLE(bank, index), ++ irq_bank->irqmask[index]); + /* Read irq_enable register in order to guarantee the update above + * completes in the spinlock critical section. + */ +- airoha_qdma_rr(qdma, REG_INT_ENABLE(index)); ++ airoha_qdma_rr(qdma, REG_INT_ENABLE(bank, index)); + +- spin_unlock_irqrestore(&qdma->irq_lock, flags); ++ spin_unlock_irqrestore(&irq_bank->irq_lock, flags); + } + +-static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index, +- u32 mask) ++static void airoha_qdma_irq_enable(struct airoha_irq_bank *irq_bank, ++ int index, u32 mask) + { +- airoha_qdma_set_irqmask(qdma, index, 0, mask); ++ airoha_qdma_set_irqmask(irq_bank, index, 0, mask); + } + +-static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index, +- u32 mask) ++static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank, ++ int index, u32 mask) + { +- airoha_qdma_set_irqmask(qdma, index, mask, 0); ++ airoha_qdma_set_irqmask(irq_bank, index, mask, 0); + } + + static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) +@@ -732,6 +735,7 @@ free_frag: + static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); ++ struct airoha_irq_bank *irq_bank = &q->qdma->irq_banks[0]; + int cur, done = 0; + + do { +@@ -740,7 +744,7 @@ static int airoha_qdma_rx_napi_poll(stru + } while (cur && done < budget); + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_enable(irq_bank, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + return done; +@@ -965,7 +969,7 @@ unlock: + } + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(id)); + + return done; +@@ -1196,13 +1200,16 @@ static int airoha_qdma_hw_init(struct ai + int i; + + /* clear pending irqs */ +- for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks[0].irqmask); i++) + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); + + /* setup irqs */ +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK); +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK); +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK); ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, ++ INT_IDX0_MASK); ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX1, ++ INT_IDX1_MASK); ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX4, ++ INT_IDX4_MASK); + + /* setup irq binding */ + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +@@ -1247,13 +1254,14 @@ static int airoha_qdma_hw_init(struct ai + + static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) + { +- struct airoha_qdma *qdma = dev_instance; +- u32 intr[ARRAY_SIZE(qdma->irqmask)]; ++ struct airoha_irq_bank *irq_bank = dev_instance; ++ struct airoha_qdma *qdma = irq_bank->qdma; ++ u32 intr[ARRAY_SIZE(irq_bank->irqmask)]; + int i; + +- for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) { ++ for (i = 0; i < ARRAY_SIZE(intr); i++) { + intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); +- intr[i] &= qdma->irqmask[i]; ++ intr[i] &= irq_bank->irqmask[i]; + airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); + } + +@@ -1261,7 +1269,7 @@ static irqreturn_t airoha_irq_handler(in + return IRQ_NONE; + + if (intr[1] & RX_DONE_INT_MASK) { +- airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +@@ -1278,7 +1286,7 @@ static irqreturn_t airoha_irq_handler(in + if (!(intr[0] & TX_DONE_INT_MASK(i))) + continue; + +- airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0, ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(i)); + napi_schedule(&qdma->q_tx_irq[i].napi); + } +@@ -1287,6 +1295,39 @@ static irqreturn_t airoha_irq_handler(in + return IRQ_HANDLED; + } + ++static int airoha_qdma_init_irq_banks(struct platform_device *pdev, ++ struct airoha_qdma *qdma) ++{ ++ struct airoha_eth *eth = qdma->eth; ++ int i, id = qdma - ð->qdma[0]; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { ++ struct airoha_irq_bank *irq_bank = &qdma->irq_banks[i]; ++ int err, irq_index = 4 * id + i; ++ const char *name; ++ ++ spin_lock_init(&irq_bank->irq_lock); ++ irq_bank->qdma = qdma; ++ ++ irq_bank->irq = platform_get_irq(pdev, irq_index); ++ if (irq_bank->irq < 0) ++ return irq_bank->irq; ++ ++ name = devm_kasprintf(eth->dev, GFP_KERNEL, ++ KBUILD_MODNAME ".%d", irq_index); ++ if (!name) ++ return -ENOMEM; ++ ++ err = devm_request_irq(eth->dev, irq_bank->irq, ++ airoha_irq_handler, IRQF_SHARED, name, ++ irq_bank); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ + static int airoha_qdma_init(struct platform_device *pdev, + struct airoha_eth *eth, + struct airoha_qdma *qdma) +@@ -1294,9 +1335,7 @@ static int airoha_qdma_init(struct platf + int err, id = qdma - ð->qdma[0]; + const char *res; + +- spin_lock_init(&qdma->irq_lock); + qdma->eth = eth; +- + res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id); + if (!res) + return -ENOMEM; +@@ -1306,12 +1345,7 @@ static int airoha_qdma_init(struct platf + return dev_err_probe(eth->dev, PTR_ERR(qdma->regs), + "failed to iomap qdma%d regs\n", id); + +- qdma->irq = platform_get_irq(pdev, 4 * id); +- if (qdma->irq < 0) +- return qdma->irq; +- +- err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler, +- IRQF_SHARED, KBUILD_MODNAME, qdma); ++ err = airoha_qdma_init_irq_banks(pdev, qdma); + if (err) + return err; + +@@ -2802,7 +2836,7 @@ static int airoha_alloc_gdm_port(struct + dev->features |= dev->hw_features; + dev->vlan_features = dev->hw_features; + dev->dev.of_node = np; +- dev->irq = qdma->irq; ++ dev->irq = qdma->irq_banks[0].irq; + SET_NETDEV_DEV(dev, eth->dev); + + /* reserve hw queues for HTB offloading */ +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -17,6 +17,7 @@ + + #define AIROHA_MAX_NUM_GDM_PORTS 4 + #define AIROHA_MAX_NUM_QDMA 2 ++#define AIROHA_MAX_NUM_IRQ_BANKS 1 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 +@@ -452,17 +453,23 @@ struct airoha_flow_table_entry { + unsigned long cookie; + }; + +-struct airoha_qdma { +- struct airoha_eth *eth; +- void __iomem *regs; ++struct airoha_irq_bank { ++ struct airoha_qdma *qdma; + + /* protect concurrent irqmask accesses */ + spinlock_t irq_lock; + u32 irqmask[QDMA_INT_REG_MAX]; + int irq; ++}; ++ ++struct airoha_qdma { ++ struct airoha_eth *eth; ++ void __iomem *regs; + + atomic_t users; + ++ struct airoha_irq_bank irq_banks[AIROHA_MAX_NUM_IRQ_BANKS]; ++ + struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -423,11 +423,12 @@ + ((_n) == 2) ? 0x0720 : \ + ((_n) == 1) ? 0x0024 : 0x0020) + +-#define REG_INT_ENABLE(_n) \ +- (((_n) == 4) ? 0x0750 : \ +- ((_n) == 3) ? 0x0744 : \ +- ((_n) == 2) ? 0x0740 : \ +- ((_n) == 1) ? 0x002c : 0x0028) ++#define REG_INT_ENABLE(_b, _n) \ ++ (((_n) == 4) ? 0x0750 + ((_b) << 5) : \ ++ ((_n) == 3) ? 0x0744 + ((_b) << 5) : \ ++ ((_n) == 2) ? 0x0740 + ((_b) << 5) : \ ++ ((_n) == 1) ? 0x002c + ((_b) << 3) : \ ++ 0x0028 + ((_b) << 3)) + + /* QDMA_CSR_INT_ENABLE1 */ + #define RX15_COHERENT_INT_MASK BIT(31) diff --git a/target/linux/airoha/patches-6.12/070-02-v6.16-net-airoha-Enable-multiple-IRQ-lines-support-in-airo.patch b/target/linux/airoha/patches-6.12/070-02-v6.16-net-airoha-Enable-multiple-IRQ-lines-support-in-airo.patch new file mode 100644 index 00000000000..2d1ad281e54 --- /dev/null +++ b/target/linux/airoha/patches-6.12/070-02-v6.16-net-airoha-Enable-multiple-IRQ-lines-support-in-airo.patch @@ -0,0 +1,379 @@ +From f252493e1835366fc25ce631c3056f900977dd11 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 18 Apr 2025 12:40:50 +0200 +Subject: [PATCH 2/2] net: airoha: Enable multiple IRQ lines support in + airoha_eth driver. + +EN7581 ethernet SoC supports 4 programmable IRQ lines for Tx and Rx +interrupts. Enable multiple IRQ lines support. Map Rx/Tx queues to the +available IRQ lines using the default scheme used in the vendor SDK: + +- IRQ0: rx queues [0-4],[7-9],15 +- IRQ1: rx queues [21-30] +- IRQ2: rx queues 5 +- IRQ3: rx queues 6 + +Tx queues interrupts are managed by IRQ0. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250418-airoha-eth-multi-irq-v1-2-1ab0083ca3c1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 67 +++++--- + drivers/net/ethernet/airoha/airoha_eth.h | 13 +- + drivers/net/ethernet/airoha/airoha_regs.h | 185 +++++++++++++++++----- + 3 files changed, 206 insertions(+), 59 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -735,7 +735,6 @@ free_frag: + static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); +- struct airoha_irq_bank *irq_bank = &q->qdma->irq_banks[0]; + int cur, done = 0; + + do { +@@ -743,9 +742,20 @@ static int airoha_qdma_rx_napi_poll(stru + done += cur; + } while (cur && done < budget); + +- if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(irq_bank, QDMA_INT_REG_IDX1, +- RX_DONE_INT_MASK); ++ if (done < budget && napi_complete(napi)) { ++ struct airoha_qdma *qdma = q->qdma; ++ int i, qid = q - &qdma->q_rx[0]; ++ int intr_reg = qid < RX_DONE_HIGH_OFFSET ? QDMA_INT_REG_IDX1 ++ : QDMA_INT_REG_IDX2; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { ++ if (!(BIT(qid) & RX_IRQ_BANK_PIN_MASK(i))) ++ continue; ++ ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], intr_reg, ++ BIT(qid % RX_DONE_HIGH_OFFSET)); ++ } ++ } + + return done; + } +@@ -1199,17 +1209,24 @@ static int airoha_qdma_hw_init(struct ai + { + int i; + +- /* clear pending irqs */ +- for (i = 0; i < ARRAY_SIZE(qdma->irq_banks[0].irqmask); i++) ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { ++ /* clear pending irqs */ + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); +- +- /* setup irqs */ ++ /* setup rx irqs */ ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX0, ++ INT_RX0_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX1, ++ INT_RX1_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX2, ++ INT_RX2_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX3, ++ INT_RX3_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ } ++ /* setup tx irqs */ + airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, +- INT_IDX0_MASK); +- airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX1, +- INT_IDX1_MASK); ++ TX_COHERENT_LOW_INT_MASK | INT_TX_MASK); + airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX4, +- INT_IDX4_MASK); ++ TX_COHERENT_HIGH_INT_MASK); + + /* setup irq binding */ + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +@@ -1256,6 +1273,7 @@ static irqreturn_t airoha_irq_handler(in + { + struct airoha_irq_bank *irq_bank = dev_instance; + struct airoha_qdma *qdma = irq_bank->qdma; ++ u32 rx_intr_mask = 0, rx_intr1, rx_intr2; + u32 intr[ARRAY_SIZE(irq_bank->irqmask)]; + int i; + +@@ -1268,17 +1286,24 @@ static irqreturn_t airoha_irq_handler(in + if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) + return IRQ_NONE; + +- if (intr[1] & RX_DONE_INT_MASK) { +- airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, +- RX_DONE_INT_MASK); ++ rx_intr1 = intr[1] & RX_DONE_LOW_INT_MASK; ++ if (rx_intr1) { ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, rx_intr1); ++ rx_intr_mask |= rx_intr1; ++ } + +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; ++ rx_intr2 = intr[2] & RX_DONE_HIGH_INT_MASK; ++ if (rx_intr2) { ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX2, rx_intr2); ++ rx_intr_mask |= (rx_intr2 << 16); ++ } + +- if (intr[1] & BIT(i)) +- napi_schedule(&qdma->q_rx[i].napi); +- } ++ for (i = 0; rx_intr_mask && i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ if (rx_intr_mask & BIT(i)) ++ napi_schedule(&qdma->q_rx[i].napi); + } + + if (intr[0] & INT_TX_MASK) { +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -17,7 +17,7 @@ + + #define AIROHA_MAX_NUM_GDM_PORTS 4 + #define AIROHA_MAX_NUM_QDMA 2 +-#define AIROHA_MAX_NUM_IRQ_BANKS 1 ++#define AIROHA_MAX_NUM_IRQ_BANKS 4 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 +@@ -453,6 +453,17 @@ struct airoha_flow_table_entry { + unsigned long cookie; + }; + ++/* RX queue to IRQ mapping: BIT(q) in IRQ(n) */ ++#define RX_IRQ0_BANK_PIN_MASK 0x839f ++#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000 ++#define RX_IRQ2_BANK_PIN_MASK 0x20 ++#define RX_IRQ3_BANK_PIN_MASK 0x40 ++#define RX_IRQ_BANK_PIN_MASK(_n) \ ++ (((_n) == 3) ? RX_IRQ3_BANK_PIN_MASK : \ ++ ((_n) == 2) ? RX_IRQ2_BANK_PIN_MASK : \ ++ ((_n) == 1) ? RX_IRQ1_BANK_PIN_MASK : \ ++ RX_IRQ0_BANK_PIN_MASK) ++ + struct airoha_irq_bank { + struct airoha_qdma *qdma; + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -463,6 +463,26 @@ + #define IRQ0_FULL_INT_MASK BIT(1) + #define IRQ0_INT_MASK BIT(0) + ++#define RX_COHERENT_LOW_INT_MASK \ ++ (RX15_COHERENT_INT_MASK | RX14_COHERENT_INT_MASK | \ ++ RX13_COHERENT_INT_MASK | RX12_COHERENT_INT_MASK | \ ++ RX11_COHERENT_INT_MASK | RX10_COHERENT_INT_MASK | \ ++ RX9_COHERENT_INT_MASK | RX8_COHERENT_INT_MASK | \ ++ RX7_COHERENT_INT_MASK | RX6_COHERENT_INT_MASK | \ ++ RX5_COHERENT_INT_MASK | RX4_COHERENT_INT_MASK | \ ++ RX3_COHERENT_INT_MASK | RX2_COHERENT_INT_MASK | \ ++ RX1_COHERENT_INT_MASK | RX0_COHERENT_INT_MASK) ++ ++#define RX_COHERENT_LOW_OFFSET __ffs(RX_COHERENT_LOW_INT_MASK) ++#define INT_RX0_MASK(_n) \ ++ (((_n) << RX_COHERENT_LOW_OFFSET) & RX_COHERENT_LOW_INT_MASK) ++ ++#define TX_COHERENT_LOW_INT_MASK \ ++ (TX7_COHERENT_INT_MASK | TX6_COHERENT_INT_MASK | \ ++ TX5_COHERENT_INT_MASK | TX4_COHERENT_INT_MASK | \ ++ TX3_COHERENT_INT_MASK | TX2_COHERENT_INT_MASK | \ ++ TX1_COHERENT_INT_MASK | TX0_COHERENT_INT_MASK) ++ + #define TX_DONE_INT_MASK(_n) \ + ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ + : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) +@@ -471,17 +491,6 @@ + (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ + IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) + +-#define INT_IDX0_MASK \ +- (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ +- TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ +- TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ +- TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ +- RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ +- RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ +- RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ +- RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ +- RX15_COHERENT_INT_MASK | INT_TX_MASK) +- + /* QDMA_CSR_INT_ENABLE2 */ + #define RX15_NO_CPU_DSCP_INT_MASK BIT(31) + #define RX14_NO_CPU_DSCP_INT_MASK BIT(30) +@@ -516,19 +525,121 @@ + #define RX1_DONE_INT_MASK BIT(1) + #define RX0_DONE_INT_MASK BIT(0) + +-#define RX_DONE_INT_MASK \ +- (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ +- RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ +- RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ +- RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ +- RX15_DONE_INT_MASK) +-#define INT_IDX1_MASK \ +- (RX_DONE_INT_MASK | \ +- RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ +- RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ +- RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ +- RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ +- RX15_NO_CPU_DSCP_INT_MASK) ++#define RX_NO_CPU_DSCP_LOW_INT_MASK \ ++ (RX15_NO_CPU_DSCP_INT_MASK | RX14_NO_CPU_DSCP_INT_MASK | \ ++ RX13_NO_CPU_DSCP_INT_MASK | RX12_NO_CPU_DSCP_INT_MASK | \ ++ RX11_NO_CPU_DSCP_INT_MASK | RX10_NO_CPU_DSCP_INT_MASK | \ ++ RX9_NO_CPU_DSCP_INT_MASK | RX8_NO_CPU_DSCP_INT_MASK | \ ++ RX7_NO_CPU_DSCP_INT_MASK | RX6_NO_CPU_DSCP_INT_MASK | \ ++ RX5_NO_CPU_DSCP_INT_MASK | RX4_NO_CPU_DSCP_INT_MASK | \ ++ RX3_NO_CPU_DSCP_INT_MASK | RX2_NO_CPU_DSCP_INT_MASK | \ ++ RX1_NO_CPU_DSCP_INT_MASK | RX0_NO_CPU_DSCP_INT_MASK) ++ ++#define RX_DONE_LOW_INT_MASK \ ++ (RX15_DONE_INT_MASK | RX14_DONE_INT_MASK | \ ++ RX13_DONE_INT_MASK | RX12_DONE_INT_MASK | \ ++ RX11_DONE_INT_MASK | RX10_DONE_INT_MASK | \ ++ RX9_DONE_INT_MASK | RX8_DONE_INT_MASK | \ ++ RX7_DONE_INT_MASK | RX6_DONE_INT_MASK | \ ++ RX5_DONE_INT_MASK | RX4_DONE_INT_MASK | \ ++ RX3_DONE_INT_MASK | RX2_DONE_INT_MASK | \ ++ RX1_DONE_INT_MASK | RX0_DONE_INT_MASK) ++ ++#define RX_NO_CPU_DSCP_LOW_OFFSET __ffs(RX_NO_CPU_DSCP_LOW_INT_MASK) ++#define INT_RX1_MASK(_n) \ ++ ((((_n) << RX_NO_CPU_DSCP_LOW_OFFSET) & RX_NO_CPU_DSCP_LOW_INT_MASK) | \ ++ (RX_DONE_LOW_INT_MASK & (_n))) ++ ++/* QDMA_CSR_INT_ENABLE3 */ ++#define RX31_NO_CPU_DSCP_INT_MASK BIT(31) ++#define RX30_NO_CPU_DSCP_INT_MASK BIT(30) ++#define RX29_NO_CPU_DSCP_INT_MASK BIT(29) ++#define RX28_NO_CPU_DSCP_INT_MASK BIT(28) ++#define RX27_NO_CPU_DSCP_INT_MASK BIT(27) ++#define RX26_NO_CPU_DSCP_INT_MASK BIT(26) ++#define RX25_NO_CPU_DSCP_INT_MASK BIT(25) ++#define RX24_NO_CPU_DSCP_INT_MASK BIT(24) ++#define RX23_NO_CPU_DSCP_INT_MASK BIT(23) ++#define RX22_NO_CPU_DSCP_INT_MASK BIT(22) ++#define RX21_NO_CPU_DSCP_INT_MASK BIT(21) ++#define RX20_NO_CPU_DSCP_INT_MASK BIT(20) ++#define RX19_NO_CPU_DSCP_INT_MASK BIT(19) ++#define RX18_NO_CPU_DSCP_INT_MASK BIT(18) ++#define RX17_NO_CPU_DSCP_INT_MASK BIT(17) ++#define RX16_NO_CPU_DSCP_INT_MASK BIT(16) ++#define RX31_DONE_INT_MASK BIT(15) ++#define RX30_DONE_INT_MASK BIT(14) ++#define RX29_DONE_INT_MASK BIT(13) ++#define RX28_DONE_INT_MASK BIT(12) ++#define RX27_DONE_INT_MASK BIT(11) ++#define RX26_DONE_INT_MASK BIT(10) ++#define RX25_DONE_INT_MASK BIT(9) ++#define RX24_DONE_INT_MASK BIT(8) ++#define RX23_DONE_INT_MASK BIT(7) ++#define RX22_DONE_INT_MASK BIT(6) ++#define RX21_DONE_INT_MASK BIT(5) ++#define RX20_DONE_INT_MASK BIT(4) ++#define RX19_DONE_INT_MASK BIT(3) ++#define RX18_DONE_INT_MASK BIT(2) ++#define RX17_DONE_INT_MASK BIT(1) ++#define RX16_DONE_INT_MASK BIT(0) ++ ++#define RX_NO_CPU_DSCP_HIGH_INT_MASK \ ++ (RX31_NO_CPU_DSCP_INT_MASK | RX30_NO_CPU_DSCP_INT_MASK | \ ++ RX29_NO_CPU_DSCP_INT_MASK | RX28_NO_CPU_DSCP_INT_MASK | \ ++ RX27_NO_CPU_DSCP_INT_MASK | RX26_NO_CPU_DSCP_INT_MASK | \ ++ RX25_NO_CPU_DSCP_INT_MASK | RX24_NO_CPU_DSCP_INT_MASK | \ ++ RX23_NO_CPU_DSCP_INT_MASK | RX22_NO_CPU_DSCP_INT_MASK | \ ++ RX21_NO_CPU_DSCP_INT_MASK | RX20_NO_CPU_DSCP_INT_MASK | \ ++ RX19_NO_CPU_DSCP_INT_MASK | RX18_NO_CPU_DSCP_INT_MASK | \ ++ RX17_NO_CPU_DSCP_INT_MASK | RX16_NO_CPU_DSCP_INT_MASK) ++ ++#define RX_DONE_HIGH_INT_MASK \ ++ (RX31_DONE_INT_MASK | RX30_DONE_INT_MASK | \ ++ RX29_DONE_INT_MASK | RX28_DONE_INT_MASK | \ ++ RX27_DONE_INT_MASK | RX26_DONE_INT_MASK | \ ++ RX25_DONE_INT_MASK | RX24_DONE_INT_MASK | \ ++ RX23_DONE_INT_MASK | RX22_DONE_INT_MASK | \ ++ RX21_DONE_INT_MASK | RX20_DONE_INT_MASK | \ ++ RX19_DONE_INT_MASK | RX18_DONE_INT_MASK | \ ++ RX17_DONE_INT_MASK | RX16_DONE_INT_MASK) ++ ++#define RX_DONE_INT_MASK (RX_DONE_HIGH_INT_MASK | RX_DONE_LOW_INT_MASK) ++#define RX_DONE_HIGH_OFFSET fls(RX_DONE_HIGH_INT_MASK) ++ ++#define INT_RX2_MASK(_n) \ ++ ((RX_NO_CPU_DSCP_HIGH_INT_MASK & (_n)) | \ ++ (((_n) >> RX_DONE_HIGH_OFFSET) & RX_DONE_HIGH_INT_MASK)) ++ ++/* QDMA_CSR_INT_ENABLE4 */ ++#define RX31_COHERENT_INT_MASK BIT(31) ++#define RX30_COHERENT_INT_MASK BIT(30) ++#define RX29_COHERENT_INT_MASK BIT(29) ++#define RX28_COHERENT_INT_MASK BIT(28) ++#define RX27_COHERENT_INT_MASK BIT(27) ++#define RX26_COHERENT_INT_MASK BIT(26) ++#define RX25_COHERENT_INT_MASK BIT(25) ++#define RX24_COHERENT_INT_MASK BIT(24) ++#define RX23_COHERENT_INT_MASK BIT(23) ++#define RX22_COHERENT_INT_MASK BIT(22) ++#define RX21_COHERENT_INT_MASK BIT(21) ++#define RX20_COHERENT_INT_MASK BIT(20) ++#define RX19_COHERENT_INT_MASK BIT(19) ++#define RX18_COHERENT_INT_MASK BIT(18) ++#define RX17_COHERENT_INT_MASK BIT(17) ++#define RX16_COHERENT_INT_MASK BIT(16) ++ ++#define RX_COHERENT_HIGH_INT_MASK \ ++ (RX31_COHERENT_INT_MASK | RX30_COHERENT_INT_MASK | \ ++ RX29_COHERENT_INT_MASK | RX28_COHERENT_INT_MASK | \ ++ RX27_COHERENT_INT_MASK | RX26_COHERENT_INT_MASK | \ ++ RX25_COHERENT_INT_MASK | RX24_COHERENT_INT_MASK | \ ++ RX23_COHERENT_INT_MASK | RX22_COHERENT_INT_MASK | \ ++ RX21_COHERENT_INT_MASK | RX20_COHERENT_INT_MASK | \ ++ RX19_COHERENT_INT_MASK | RX18_COHERENT_INT_MASK | \ ++ RX17_COHERENT_INT_MASK | RX16_COHERENT_INT_MASK) ++ ++#define INT_RX3_MASK(_n) (RX_COHERENT_HIGH_INT_MASK & (_n)) + + /* QDMA_CSR_INT_ENABLE5 */ + #define TX31_COHERENT_INT_MASK BIT(31) +@@ -556,19 +667,19 @@ + #define TX9_COHERENT_INT_MASK BIT(9) + #define TX8_COHERENT_INT_MASK BIT(8) + +-#define INT_IDX4_MASK \ +- (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ +- TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ +- TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ +- TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ +- TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ +- TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ +- TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ +- TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ +- TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ +- TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ +- TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ +- TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) ++#define TX_COHERENT_HIGH_INT_MASK \ ++ (TX31_COHERENT_INT_MASK | TX30_COHERENT_INT_MASK | \ ++ TX29_COHERENT_INT_MASK | TX28_COHERENT_INT_MASK | \ ++ TX27_COHERENT_INT_MASK | TX26_COHERENT_INT_MASK | \ ++ TX25_COHERENT_INT_MASK | TX24_COHERENT_INT_MASK | \ ++ TX23_COHERENT_INT_MASK | TX22_COHERENT_INT_MASK | \ ++ TX21_COHERENT_INT_MASK | TX20_COHERENT_INT_MASK | \ ++ TX19_COHERENT_INT_MASK | TX18_COHERENT_INT_MASK | \ ++ TX17_COHERENT_INT_MASK | TX16_COHERENT_INT_MASK | \ ++ TX15_COHERENT_INT_MASK | TX14_COHERENT_INT_MASK | \ ++ TX13_COHERENT_INT_MASK | TX12_COHERENT_INT_MASK | \ ++ TX11_COHERENT_INT_MASK | TX10_COHERENT_INT_MASK | \ ++ TX9_COHERENT_INT_MASK | TX8_COHERENT_INT_MASK) + + #define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) + diff --git a/target/linux/airoha/patches-6.12/071-v6.15-net-airoha-Add-missing-field-to-ppe_mbox_data-struct.patch b/target/linux/airoha/patches-6.12/071-v6.15-net-airoha-Add-missing-field-to-ppe_mbox_data-struct.patch new file mode 100644 index 00000000000..2fb90b6c3b7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/071-v6.15-net-airoha-Add-missing-field-to-ppe_mbox_data-struct.patch @@ -0,0 +1,48 @@ +From 4a7843cc8a41b9612becccc07715ed017770eb89 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 6 May 2025 18:56:47 +0200 +Subject: [PATCH] net: airoha: Add missing field to ppe_mbox_data struct + +The official Airoha EN7581 firmware requires adding max_packet field in +ppe_mbox_data struct while the unofficial one used to develop the Airoha +EN7581 flowtable support does not require this field. +This patch does not introduce any real backwards compatible issue since +EN7581 fw is not publicly available in linux-firmware or other +repositories (e.g. OpenWrt) yet and the official fw version will use this +new layout. For this reason this change needs to be backported. +Moreover, make explicit the padding added by the compiler introducing +the rsv array in init_info struct. +At the same time use u32 instead of int for init_info and set_info +struct definitions in ppe_mbox_data struct. + +Fixes: 23290c7bc190d ("net: airoha: Introduce Airoha NPU support") +Reviewed-by: Simon Horman +Reviewed-by: Jacob Keller +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250506-airoha-en7581-fix-ppe_mbox_data-v5-1-29cabed6864d@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -104,12 +104,14 @@ struct ppe_mbox_data { + u8 xpon_hal_api; + u8 wan_xsi; + u8 ct_joyme4; +- int ppe_type; +- int wan_mode; +- int wan_sel; ++ u8 max_packet; ++ u8 rsv[3]; ++ u32 ppe_type; ++ u32 wan_mode; ++ u32 wan_sel; + } init_info; + struct { +- int func_id; ++ u32 func_id; + u32 size; + u32 data; + } set_info; diff --git a/target/linux/airoha/patches-6.12/072-v6.15-net-airoha-Fix-page-recycling-in-airoha_qdma_rx_proc.patch b/target/linux/airoha/patches-6.12/072-v6.15-net-airoha-Fix-page-recycling-in-airoha_qdma_rx_proc.patch new file mode 100644 index 00000000000..bcf60ce8dd8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/072-v6.15-net-airoha-Fix-page-recycling-in-airoha_qdma_rx_proc.patch @@ -0,0 +1,72 @@ +From d6d2b0e1538d5c381ec0ca95afaf772c096ea5dc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 15 May 2025 08:33:06 +0200 +Subject: [PATCH] net: airoha: Fix page recycling in airoha_qdma_rx_process() + +Do not recycle the page twice in airoha_qdma_rx_process routine in case +of error. Just run dev_kfree_skb() if the skb has been allocated and marked +for recycling. Run page_pool_put_full_page() directly if the skb has not +been allocated yet. +Moreover, rely on DMA address from queue entry element instead of reading +it from the DMA descriptor for DMA syncing in airoha_qdma_rx_process(). + +Fixes: e12182ddb6e71 ("net: airoha: Enable Rx Scatter-Gather") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250515-airoha-fix-rx-process-error-condition-v2-1-657e92c894b9@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -636,7 +636,6 @@ static int airoha_qdma_rx_process(struct + struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); +- dma_addr_t dma_addr = le32_to_cpu(desc->addr); + struct page *page = virt_to_head_page(e->buf); + u32 desc_ctrl = le32_to_cpu(desc->ctrl); + struct airoha_gdm_port *port; +@@ -645,22 +644,16 @@ static int airoha_qdma_rx_process(struct + if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) + break; + +- if (!dma_addr) +- break; +- +- len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); +- if (!len) +- break; +- + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + +- dma_sync_single_for_cpu(eth->dev, dma_addr, ++ dma_sync_single_for_cpu(eth->dev, e->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), dir); + ++ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); + data_len = q->skb ? q->buf_size + : SKB_WITH_OVERHEAD(q->buf_size); +- if (data_len < len) ++ if (!len || data_len < len) + goto free_frag; + + p = airoha_qdma_get_gdm_port(eth, desc); +@@ -723,9 +716,12 @@ static int airoha_qdma_rx_process(struct + q->skb = NULL; + continue; + free_frag: +- page_pool_put_full_page(q->page_pool, page, true); +- dev_kfree_skb(q->skb); +- q->skb = NULL; ++ if (q->skb) { ++ dev_kfree_skb(q->skb); ++ q->skb = NULL; ++ } else { ++ page_pool_put_full_page(q->page_pool, page, true); ++ } + } + airoha_qdma_fill_rx_queue(q); + diff --git a/target/linux/airoha/patches-6.12/073-01-v6.16-net-airoha-npu-Move-memory-allocation-in-airoha_npu_.patch b/target/linux/airoha/patches-6.12/073-01-v6.16-net-airoha-npu-Move-memory-allocation-in-airoha_npu_.patch new file mode 100644 index 00000000000..f0c41d5dc4e --- /dev/null +++ b/target/linux/airoha/patches-6.12/073-01-v6.16-net-airoha-npu-Move-memory-allocation-in-airoha_npu_.patch @@ -0,0 +1,196 @@ +From c52918744ee1e49cea86622a2633b9782446428f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 16 May 2025 09:59:59 +0200 +Subject: [PATCH 1/3] net: airoha: npu: Move memory allocation in + airoha_npu_send_msg() caller + +Move ppe_mbox_data struct memory allocation from airoha_npu_send_msg +routine to the caller one. This is a preliminary patch to enable wlan NPU +offloading and flow counter stats support. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-1-06d5fbf28984@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 126 +++++++++++++---------- + 1 file changed, 72 insertions(+), 54 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -124,17 +124,12 @@ static int airoha_npu_send_msg(struct ai + u16 core = 0; /* FIXME */ + u32 val, offset = core << 4; + dma_addr_t dma_addr; +- void *addr; + int ret; + +- addr = kmemdup(p, size, GFP_ATOMIC); +- if (!addr) +- return -ENOMEM; +- +- dma_addr = dma_map_single(npu->dev, addr, size, DMA_TO_DEVICE); ++ dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE); + ret = dma_mapping_error(npu->dev, dma_addr); + if (ret) +- goto out; ++ return ret; + + spin_lock_bh(&npu->cores[core].lock); + +@@ -155,8 +150,6 @@ static int airoha_npu_send_msg(struct ai + spin_unlock_bh(&npu->cores[core].lock); + + dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); +-out: +- kfree(addr); + + return ret; + } +@@ -261,76 +254,101 @@ static irqreturn_t airoha_npu_wdt_handle + + static int airoha_npu_ppe_init(struct airoha_npu *npu) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT, +- .init_info = { +- .ppe_type = PPE_TYPE_L2B_IPV4_IPV6, +- .wan_mode = QDMA_WAN_ETHER, +- }, +- }; ++ struct ppe_mbox_data *ppe_data; ++ int err; + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT; ++ ppe_data->init_info.ppe_type = PPE_TYPE_L2B_IPV4_IPV6; ++ ppe_data->init_info.wan_mode = QDMA_WAN_ETHER; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ kfree(ppe_data); ++ ++ return err; + } + + static int airoha_npu_ppe_deinit(struct airoha_npu *npu) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT, +- }; ++ struct ppe_mbox_data *ppe_data; ++ int err; ++ ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); ++ if (!ppe_data) ++ return -ENOMEM; + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ kfree(ppe_data); ++ ++ return err; + } + + static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu, + dma_addr_t foe_addr, + int sram_num_entries) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_API, +- .set_info = { +- .func_id = PPE_SRAM_RESET_VAL, +- .data = foe_addr, +- .size = sram_num_entries, +- }, +- }; ++ struct ppe_mbox_data *ppe_data; ++ int err; ++ ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_API; ++ ppe_data->set_info.func_id = PPE_SRAM_RESET_VAL; ++ ppe_data->set_info.data = foe_addr; ++ ppe_data->set_info.size = sram_num_entries; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ kfree(ppe_data); + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ return err; + } + + static int airoha_npu_foe_commit_entry(struct airoha_npu *npu, + dma_addr_t foe_addr, + u32 entry_size, u32 hash, bool ppe2) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_API, +- .set_info = { +- .data = foe_addr, +- .size = entry_size, +- }, +- }; ++ struct ppe_mbox_data *ppe_data; + int err; + +- ppe_data.set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY +- : PPE_SRAM_SET_ENTRY; ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_API; ++ ppe_data->set_info.data = foe_addr; ++ ppe_data->set_info.size = entry_size; ++ ppe_data->set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY ++ : PPE_SRAM_SET_ENTRY; + +- err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); + if (err) +- return err; ++ goto out; + +- ppe_data.set_info.func_id = PPE_SRAM_SET_VAL; +- ppe_data.set_info.data = hash; +- ppe_data.set_info.size = sizeof(u32); ++ ppe_data->set_info.func_id = PPE_SRAM_SET_VAL; ++ ppe_data->set_info.data = hash; ++ ppe_data->set_info.size = sizeof(u32); ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++out: ++ kfree(ppe_data); + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ return err; + } + + struct airoha_npu *airoha_npu_get(struct device *dev) diff --git a/target/linux/airoha/patches-6.12/073-02-v6.16-net-airoha-Add-FLOW_CLS_STATS-callback-support.patch b/target/linux/airoha/patches-6.12/073-02-v6.16-net-airoha-Add-FLOW_CLS_STATS-callback-support.patch new file mode 100644 index 00000000000..584ddb1da8f --- /dev/null +++ b/target/linux/airoha/patches-6.12/073-02-v6.16-net-airoha-Add-FLOW_CLS_STATS-callback-support.patch @@ -0,0 +1,633 @@ +From b81e0f2b58be37628b2e12f8dffdd63c84573e75 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 16 May 2025 10:00:00 +0200 +Subject: [PATCH 2/3] net: airoha: Add FLOW_CLS_STATS callback support + +Introduce per-flow stats accounting to the flowtable hw offload in +the airoha_eth driver. Flow stats are split in the PPE and NPU modules: +- PPE: accounts for high 32bit of per-flow stats +- NPU: accounts for low 32bit of per-flow stats + +FLOW_CLS_STATS can be enabled or disabled at compile time. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-2-06d5fbf28984@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/Kconfig | 7 + + drivers/net/ethernet/airoha/airoha_eth.h | 33 +++ + drivers/net/ethernet/airoha/airoha_npu.c | 52 +++- + drivers/net/ethernet/airoha/airoha_npu.h | 4 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 269 ++++++++++++++++-- + .../net/ethernet/airoha/airoha_ppe_debugfs.c | 9 +- + 6 files changed, 354 insertions(+), 20 deletions(-) + +--- a/drivers/net/ethernet/airoha/Kconfig ++++ b/drivers/net/ethernet/airoha/Kconfig +@@ -24,4 +24,11 @@ config NET_AIROHA + This driver supports the gigabit ethernet MACs in the + Airoha SoC family. + ++config NET_AIROHA_FLOW_STATS ++ default y ++ bool "Airoha flow stats" ++ depends on NET_AIROHA && NET_AIROHA_NPU ++ help ++ Enable Aiorha flowtable statistic counters. ++ + endif #NET_VENDOR_AIROHA +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -50,6 +50,14 @@ + #define PPE_NUM 2 + #define PPE1_SRAM_NUM_ENTRIES (8 * 1024) + #define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES) ++#ifdef CONFIG_NET_AIROHA_FLOW_STATS ++#define PPE1_STATS_NUM_ENTRIES (4 * 1024) ++#else ++#define PPE1_STATS_NUM_ENTRIES 0 ++#endif /* CONFIG_NET_AIROHA_FLOW_STATS */ ++#define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES) ++#define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES) ++#define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES) + #define PPE_DRAM_NUM_ENTRIES (16 * 1024) + #define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) + #define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1) +@@ -261,6 +269,8 @@ struct airoha_foe_mac_info { + + u16 pppoe_id; + u16 src_mac_lo; ++ ++ u32 meter; + }; + + #define AIROHA_FOE_IB1_UNBIND_PREBIND BIT(24) +@@ -296,6 +306,11 @@ struct airoha_foe_mac_info { + #define AIROHA_FOE_TUNNEL BIT(6) + #define AIROHA_FOE_TUNNEL_ID GENMASK(5, 0) + ++#define AIROHA_FOE_TUNNEL_MTU GENMASK(31, 16) ++#define AIROHA_FOE_ACNT_GRP3 GENMASK(15, 9) ++#define AIROHA_FOE_METER_GRP3 GENMASK(8, 5) ++#define AIROHA_FOE_METER_GRP2 GENMASK(4, 0) ++ + struct airoha_foe_bridge { + u32 dest_mac_hi; + +@@ -379,6 +394,8 @@ struct airoha_foe_ipv6 { + u32 ib2; + + struct airoha_foe_mac_info_common l2; ++ ++ u32 meter; + }; + + struct airoha_foe_entry { +@@ -397,6 +414,16 @@ struct airoha_foe_entry { + }; + }; + ++struct airoha_foe_stats { ++ u32 bytes; ++ u32 packets; ++}; ++ ++struct airoha_foe_stats64 { ++ u64 bytes; ++ u64 packets; ++}; ++ + struct airoha_flow_data { + struct ethhdr eth; + +@@ -447,6 +474,7 @@ struct airoha_flow_table_entry { + struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ + u32 hash; + ++ struct airoha_foe_stats64 stats; + enum airoha_flow_entry_type type; + + struct rhash_head node; +@@ -523,6 +551,9 @@ struct airoha_ppe { + struct hlist_head *foe_flow; + u16 foe_check_time[PPE_NUM_ENTRIES]; + ++ struct airoha_foe_stats *foe_stats; ++ dma_addr_t foe_stats_dma; ++ + struct dentry *debugfs_dir; + }; + +@@ -582,6 +613,8 @@ int airoha_ppe_init(struct airoha_eth *e + void airoha_ppe_deinit(struct airoha_eth *eth); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); ++void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, ++ struct airoha_foe_stats64 *stats); + + #ifdef CONFIG_DEBUG_FS + int airoha_ppe_debugfs_init(struct airoha_ppe *ppe); +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -12,6 +12,7 @@ + #include + #include + ++#include "airoha_eth.h" + #include "airoha_npu.h" + + #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" +@@ -72,6 +73,7 @@ enum { + PPE_FUNC_SET_WAIT_HWNAT_INIT, + PPE_FUNC_SET_WAIT_HWNAT_DEINIT, + PPE_FUNC_SET_WAIT_API, ++ PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP, + }; + + enum { +@@ -115,6 +117,10 @@ struct ppe_mbox_data { + u32 size; + u32 data; + } set_info; ++ struct { ++ u32 npu_stats_addr; ++ u32 foe_stats_addr; ++ } stats_info; + }; + }; + +@@ -351,7 +357,40 @@ out: + return err; + } + +-struct airoha_npu *airoha_npu_get(struct device *dev) ++static int airoha_npu_stats_setup(struct airoha_npu *npu, ++ dma_addr_t foe_stats_addr) ++{ ++ int err, size = PPE_STATS_NUM_ENTRIES * sizeof(*npu->stats); ++ struct ppe_mbox_data *ppe_data; ++ ++ if (!size) /* flow stats are disabled */ ++ return 0; ++ ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP; ++ ppe_data->stats_info.foe_stats_addr = foe_stats_addr; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ if (err) ++ goto out; ++ ++ npu->stats = devm_ioremap(npu->dev, ++ ppe_data->stats_info.npu_stats_addr, ++ size); ++ if (!npu->stats) ++ err = -ENOMEM; ++out: ++ kfree(ppe_data); ++ ++ return err; ++} ++ ++struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; + struct device_node *np; +@@ -389,6 +428,17 @@ struct airoha_npu *airoha_npu_get(struct + goto error_module_put; + } + ++ if (stats_addr) { ++ int err; ++ ++ err = airoha_npu_stats_setup(npu, *stats_addr); ++ if (err) { ++ dev_err(dev, "failed to allocate npu stats buffer\n"); ++ npu = ERR_PTR(err); ++ goto error_module_put; ++ } ++ } ++ + return npu; + + error_module_put: +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -17,6 +17,8 @@ struct airoha_npu { + struct work_struct wdt_work; + } cores[NPU_NUM_CORES]; + ++ struct airoha_foe_stats __iomem *stats; ++ + struct { + int (*ppe_init)(struct airoha_npu *npu); + int (*ppe_deinit)(struct airoha_npu *npu); +@@ -30,5 +32,5 @@ struct airoha_npu { + } ops; + }; + +-struct airoha_npu *airoha_npu_get(struct device *dev); ++struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); + void airoha_npu_put(struct airoha_npu *npu); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -102,7 +102,7 @@ static void airoha_ppe_hw_init(struct ai + + if (airoha_ppe2_is_enabled(eth)) { + sram_num_entries = +- PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_ENTRIES); ++ PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES); + airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), + PPE_SRAM_TB_NUM_ENTRY_MASK | + PPE_DRAM_TB_NUM_ENTRY_MASK, +@@ -119,7 +119,7 @@ static void airoha_ppe_hw_init(struct ai + dram_num_entries)); + } else { + sram_num_entries = +- PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_ENTRIES); ++ PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES); + airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), + PPE_SRAM_TB_NUM_ENTRY_MASK | + PPE_DRAM_TB_NUM_ENTRY_MASK, +@@ -417,6 +417,77 @@ static u32 airoha_ppe_foe_get_entry_hash + return hash; + } + ++static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash) ++{ ++ if (!airoha_ppe2_is_enabled(ppe->eth)) ++ return hash; ++ ++ return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES ++ : hash; ++} ++ ++static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe, ++ struct airoha_npu *npu, ++ int index) ++{ ++ memset_io(&npu->stats[index], 0, sizeof(*npu->stats)); ++ memset(&ppe->foe_stats[index], 0, sizeof(*ppe->foe_stats)); ++} ++ ++static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe, ++ struct airoha_npu *npu) ++{ ++ int i; ++ ++ for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++) ++ airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i); ++} ++ ++static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe, ++ struct airoha_npu *npu, ++ struct airoha_foe_entry *hwe, ++ u32 hash) ++{ ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ u32 index, pse_port, val, *data, *ib2, *meter; ++ u8 nbq; ++ ++ index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); ++ if (index >= PPE_STATS_NUM_ENTRIES) ++ return; ++ ++ if (type == PPE_PKT_TYPE_BRIDGE) { ++ data = &hwe->bridge.data; ++ ib2 = &hwe->bridge.ib2; ++ meter = &hwe->bridge.l2.meter; ++ } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ data = &hwe->ipv6.data; ++ ib2 = &hwe->ipv6.ib2; ++ meter = &hwe->ipv6.meter; ++ } else { ++ data = &hwe->ipv4.data; ++ ib2 = &hwe->ipv4.ib2; ++ meter = &hwe->ipv4.l2.meter; ++ } ++ ++ airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index); ++ ++ val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data); ++ *data = (*data & ~AIROHA_FOE_ACTDP) | ++ FIELD_PREP(AIROHA_FOE_ACTDP, val); ++ ++ val = *ib2 & (AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | ++ AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH); ++ *meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val); ++ ++ pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); ++ nbq = pse_port == 1 ? 6 : 5; ++ *ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | ++ AIROHA_FOE_IB2_PSE_QOS); ++ *ib2 |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, 6) | ++ FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq); ++} ++ + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash) + { +@@ -470,6 +541,8 @@ static int airoha_ppe_foe_commit_entry(s + struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); + u32 ts = airoha_ppe_get_timestamp(ppe); + struct airoha_eth *eth = ppe->eth; ++ struct airoha_npu *npu; ++ int err = 0; + + memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1)); + wmb(); +@@ -478,25 +551,28 @@ static int airoha_ppe_foe_commit_entry(s + e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts); + hwe->ib1 = e->ib1; + ++ rcu_read_lock(); ++ ++ npu = rcu_dereference(eth->npu); ++ if (!npu) { ++ err = -ENODEV; ++ goto unlock; ++ } ++ ++ airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); ++ + if (hash < PPE_SRAM_NUM_ENTRIES) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); + bool ppe2 = airoha_ppe2_is_enabled(eth) && + hash >= PPE1_SRAM_NUM_ENTRIES; +- struct airoha_npu *npu; +- int err = -ENODEV; +- +- rcu_read_lock(); +- npu = rcu_dereference(eth->npu); +- if (npu) +- err = npu->ops.ppe_foe_commit_entry(npu, addr, +- sizeof(*hwe), hash, +- ppe2); +- rcu_read_unlock(); + +- return err; ++ err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), ++ hash, ppe2); + } ++unlock: ++ rcu_read_unlock(); + +- return 0; ++ return err; + } + + static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, +@@ -582,6 +658,7 @@ airoha_ppe_foe_commit_subflow_entry(stru + l2->common.etype = ETH_P_IPV6; + + hwe.bridge.ib2 = e->data.bridge.ib2; ++ hwe.bridge.data = e->data.bridge.data; + airoha_ppe_foe_commit_entry(ppe, &hwe, hash); + + return 0; +@@ -681,6 +758,98 @@ static int airoha_ppe_foe_flow_commit_en + return 0; + } + ++static int airoha_ppe_get_entry_idle_time(struct airoha_ppe *ppe, u32 ib1) ++{ ++ u32 state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); ++ u32 ts, ts_mask, now = airoha_ppe_get_timestamp(ppe); ++ int idle; ++ ++ if (state == AIROHA_FOE_STATE_BIND) { ++ ts = FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, ib1); ++ ts_mask = AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ } else { ++ ts = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, ib1); ++ now = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, now); ++ ts_mask = AIROHA_FOE_IB1_UNBIND_TIMESTAMP; ++ } ++ idle = now - ts; ++ ++ return idle < 0 ? idle + ts_mask + 1 : idle; ++} ++ ++static void ++airoha_ppe_foe_flow_l2_entry_update(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ int min_idle = airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); ++ struct airoha_flow_table_entry *iter; ++ struct hlist_node *n; ++ ++ lockdep_assert_held(&ppe_lock); ++ ++ hlist_for_each_entry_safe(iter, n, &e->l2_flows, l2_subflow_node) { ++ struct airoha_foe_entry *hwe; ++ u32 ib1, state; ++ int idle; ++ ++ hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); ++ ib1 = READ_ONCE(hwe->ib1); ++ ++ state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); ++ if (state != AIROHA_FOE_STATE_BIND) { ++ iter->hash = 0xffff; ++ airoha_ppe_foe_remove_flow(ppe, iter); ++ continue; ++ } ++ ++ idle = airoha_ppe_get_entry_idle_time(ppe, ib1); ++ if (idle >= min_idle) ++ continue; ++ ++ min_idle = idle; ++ e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ e->data.ib1 |= ib1 & AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ } ++} ++ ++static void airoha_ppe_foe_flow_entry_update(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ struct airoha_foe_entry *hwe_p, hwe = {}; ++ ++ spin_lock_bh(&ppe_lock); ++ ++ if (e->type == FLOW_TYPE_L2) { ++ airoha_ppe_foe_flow_l2_entry_update(ppe, e); ++ goto unlock; ++ } ++ ++ if (e->hash == 0xffff) ++ goto unlock; ++ ++ hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash); ++ if (!hwe_p) ++ goto unlock; ++ ++ memcpy(&hwe, hwe_p, sizeof(*hwe_p)); ++ if (!airoha_ppe_foe_compare_entry(e, &hwe)) { ++ e->hash = 0xffff; ++ goto unlock; ++ } ++ ++ e->data.ib1 = hwe.ib1; ++unlock: ++ spin_unlock_bh(&ppe_lock); ++} ++ ++static int airoha_ppe_entry_idle_time(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ airoha_ppe_foe_flow_entry_update(ppe, e); ++ ++ return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); ++} ++ + static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, + struct flow_cls_offload *f) + { +@@ -896,6 +1065,60 @@ static int airoha_ppe_flow_offload_destr + return 0; + } + ++void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, ++ struct airoha_foe_stats64 *stats) ++{ ++ u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); ++ struct airoha_eth *eth = ppe->eth; ++ struct airoha_npu *npu; ++ ++ if (index >= PPE_STATS_NUM_ENTRIES) ++ return; ++ ++ rcu_read_lock(); ++ ++ npu = rcu_dereference(eth->npu); ++ if (npu) { ++ u64 packets = ppe->foe_stats[index].packets; ++ u64 bytes = ppe->foe_stats[index].bytes; ++ struct airoha_foe_stats npu_stats; ++ ++ memcpy_fromio(&npu_stats, &npu->stats[index], ++ sizeof(*npu->stats)); ++ stats->packets = packets << 32 | npu_stats.packets; ++ stats->bytes = bytes << 32 | npu_stats.bytes; ++ } ++ ++ rcu_read_unlock(); ++} ++ ++static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port, ++ struct flow_cls_offload *f) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_flow_table_entry *e; ++ u32 idle; ++ ++ e = rhashtable_lookup(ð->flow_table, &f->cookie, ++ airoha_flow_table_params); ++ if (!e) ++ return -ENOENT; ++ ++ idle = airoha_ppe_entry_idle_time(eth->ppe, e); ++ f->stats.lastused = jiffies - idle * HZ; ++ ++ if (e->hash != 0xffff) { ++ struct airoha_foe_stats64 stats = {}; ++ ++ airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats); ++ f->stats.pkts += (stats.packets - e->stats.packets); ++ f->stats.bytes += (stats.bytes - e->stats.bytes); ++ e->stats = stats; ++ } ++ ++ return 0; ++} ++ + static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, + struct flow_cls_offload *f) + { +@@ -904,6 +1127,8 @@ static int airoha_ppe_flow_offload_cmd(s + return airoha_ppe_flow_offload_replace(port, f); + case FLOW_CLS_DESTROY: + return airoha_ppe_flow_offload_destroy(port, f); ++ case FLOW_CLS_STATS: ++ return airoha_ppe_flow_offload_stats(port, f); + default: + break; + } +@@ -929,11 +1154,12 @@ static int airoha_ppe_flush_sram_entries + + static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) + { +- struct airoha_npu *npu = airoha_npu_get(eth->dev); ++ struct airoha_npu *npu = airoha_npu_get(eth->dev, ++ ð->ppe->foe_stats_dma); + + if (IS_ERR(npu)) { + request_module("airoha-npu"); +- npu = airoha_npu_get(eth->dev); ++ npu = airoha_npu_get(eth->dev, ð->ppe->foe_stats_dma); + } + + return npu; +@@ -956,6 +1182,8 @@ static int airoha_ppe_offload_setup(stru + if (err) + goto error_npu_put; + ++ airoha_ppe_foe_flow_stats_reset(eth->ppe, npu); ++ + rcu_assign_pointer(eth->npu, npu); + synchronize_rcu(); + +@@ -1027,6 +1255,15 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe->foe_flow) + return -ENOMEM; + ++ foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats); ++ if (foe_size) { ++ ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size, ++ &ppe->foe_stats_dma, ++ GFP_KERNEL); ++ if (!ppe->foe_stats) ++ return -ENOMEM; ++ } ++ + err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); + if (err) + return err; +--- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +@@ -61,6 +61,7 @@ static int airoha_ppe_debugfs_foe_show(s + u16 *src_port = NULL, *dest_port = NULL; + struct airoha_foe_mac_info_common *l2; + unsigned char h_source[ETH_ALEN] = {}; ++ struct airoha_foe_stats64 stats = {}; + unsigned char h_dest[ETH_ALEN]; + struct airoha_foe_entry *hwe; + u32 type, state, ib2, data; +@@ -144,14 +145,18 @@ static int airoha_ppe_debugfs_foe_show(s + cpu_to_be16(hwe->ipv4.l2.src_mac_lo); + } + ++ airoha_ppe_foe_entry_get_stats(ppe, i, &stats); ++ + *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi); + *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo); + *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi); + + seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x" +- " vlan=%d,%d ib1=%08x ib2=%08x\n", ++ " vlan=%d,%d ib1=%08x ib2=%08x" ++ " packets=%llu bytes=%llu\n", + h_source, h_dest, l2->etype, data, +- l2->vlan1, l2->vlan2, hwe->ib1, ib2); ++ l2->vlan1, l2->vlan2, hwe->ib1, ib2, ++ stats.packets, stats.bytes); + } + + return 0; diff --git a/target/linux/airoha/patches-6.12/073-03-v6.16-net-airoha-ppe-Disable-packet-keepalive.patch b/target/linux/airoha/patches-6.12/073-03-v6.16-net-airoha-ppe-Disable-packet-keepalive.patch new file mode 100644 index 00000000000..30c74ddec64 --- /dev/null +++ b/target/linux/airoha/patches-6.12/073-03-v6.16-net-airoha-ppe-Disable-packet-keepalive.patch @@ -0,0 +1,28 @@ +From a98326c151ea3d92e9496858cc2dacccd0870941 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 16 May 2025 10:00:01 +0200 +Subject: [PATCH 3/3] net: airoha: ppe: Disable packet keepalive + +Since netfilter flowtable entries are now refreshed by flow-stats +polling, we can disable hw packet keepalive used to periodically send +packets belonging to offloaded flows to the kernel in order to refresh +flowtable entries. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-3-06d5fbf28984@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -84,6 +84,7 @@ static void airoha_ppe_hw_init(struct ai + + airoha_fe_rmw(eth, REG_PPE_TB_CFG(i), + PPE_TB_CFG_SEARCH_MISS_MASK | ++ PPE_TB_CFG_KEEPALIVE_MASK | + PPE_TB_ENTRY_SIZE_MASK, + FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) | + FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0)); diff --git a/target/linux/airoha/patches-6.12/074-01-v6.16-net-airoha-Do-not-store-hfwd-references-in-airoha_qd.patch b/target/linux/airoha/patches-6.12/074-01-v6.16-net-airoha-Do-not-store-hfwd-references-in-airoha_qd.patch new file mode 100644 index 00000000000..0a29bb8e00a --- /dev/null +++ b/target/linux/airoha/patches-6.12/074-01-v6.16-net-airoha-Do-not-store-hfwd-references-in-airoha_qd.patch @@ -0,0 +1,57 @@ +From 09aa788f98da3e2f41ce158cc691d6d52e808bc9 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 May 2025 09:16:37 +0200 +Subject: [PATCH 1/3] net: airoha: Do not store hfwd references in airoha_qdma + struct + +Since hfwd descriptor and buffer queues are allocated via +dmam_alloc_coherent() we do not need to store their references +in airoha_qdma struct. This patch does not introduce any logical changes, +just code clean-up. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-2-a6e9b085b4f0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 8 ++------ + drivers/net/ethernet/airoha/airoha_eth.h | 6 ------ + 2 files changed, 2 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1098,17 +1098,13 @@ static int airoha_qdma_init_hfwd_queues( + int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); +- qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!qdma->hfwd.desc) ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; +- qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!qdma->hfwd.q) ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -513,12 +513,6 @@ struct airoha_qdma { + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; + struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; +- +- /* descriptor and packet buffers for qdma hw forward */ +- struct { +- void *desc; +- void *q; +- } hfwd; + }; + + struct airoha_gdm_port { diff --git a/target/linux/airoha/patches-6.12/074-02-v6.16-net-airoha-Add-the-capability-to-allocate-hwfd-buffe.patch b/target/linux/airoha/patches-6.12/074-02-v6.16-net-airoha-Add-the-capability-to-allocate-hwfd-buffe.patch new file mode 100644 index 00000000000..e3282ced5f9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/074-02-v6.16-net-airoha-Add-the-capability-to-allocate-hwfd-buffe.patch @@ -0,0 +1,79 @@ +From 3a1ce9e3d01bbf3912c3e3f81cb554d558eb715b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 May 2025 09:16:38 +0200 +Subject: [PATCH 2/3] net: airoha: Add the capability to allocate hwfd buffers + via reserved-memory + +In some configurations QDMA blocks require a contiguous block of +system memory for hwfd buffers queue. Introduce the capability to allocate +hw buffers forwarding queue via the reserved-memory DTS property instead of +running dmam_alloc_coherent(). + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-3-a6e9b085b4f0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 33 +++++++++++++++++++++--- + 1 file changed, 30 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -5,6 +5,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -1093,9 +1094,11 @@ static void airoha_qdma_cleanup_tx_queue + static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) + { + struct airoha_eth *eth = qdma->eth; ++ int id = qdma - ð->qdma[0]; + dma_addr_t dma_addr; ++ const char *name; ++ int size, index; + u32 status; +- int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) +@@ -1103,10 +1106,34 @@ static int airoha_qdma_init_hfwd_queues( + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + +- size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; +- if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) ++ name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); ++ if (!name) + return -ENOMEM; + ++ index = of_property_match_string(eth->dev->of_node, ++ "memory-region-names", name); ++ if (index >= 0) { ++ struct reserved_mem *rmem; ++ struct device_node *np; ++ ++ /* Consume reserved memory for hw forwarding buffers queue if ++ * available in the DTS ++ */ ++ np = of_parse_phandle(eth->dev->of_node, "memory-region", ++ index); ++ if (!np) ++ return -ENODEV; ++ ++ rmem = of_reserved_mem_lookup(np); ++ of_node_put(np); ++ dma_addr = rmem->base; ++ } else { ++ size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL)) ++ return -ENOMEM; ++ } ++ + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, diff --git a/target/linux/airoha/patches-6.12/074-03-v6.16-net-airoha-Add-the-capability-to-allocate-hfwd-descr.patch b/target/linux/airoha/patches-6.12/074-03-v6.16-net-airoha-Add-the-capability-to-allocate-hfwd-descr.patch new file mode 100644 index 00000000000..40d57a48e0e --- /dev/null +++ b/target/linux/airoha/patches-6.12/074-03-v6.16-net-airoha-Add-the-capability-to-allocate-hfwd-descr.patch @@ -0,0 +1,82 @@ +From c683e378c0907e66cee939145edf936c254ff1e3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 May 2025 09:16:39 +0200 +Subject: [PATCH 3/3] net: airoha: Add the capability to allocate hfwd + descriptors in SRAM + +In order to improve packet processing and packet forwarding +performances, EN7581 SoC supports consuming SRAM instead of DRAM for +hw forwarding descriptors queue. +For downlink hw accelerated traffic request to consume SRAM memory +for hw forwarding descriptors queue. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-4-a6e9b085b4f0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 11 +---------- + drivers/net/ethernet/airoha/airoha_eth.h | 9 +++++++++ + drivers/net/ethernet/airoha/airoha_ppe.c | 6 ++++++ + 3 files changed, 16 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -71,15 +71,6 @@ static void airoha_qdma_irq_disable(stru + airoha_qdma_set_irqmask(irq_bank, index, mask, 0); + } + +-static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) +-{ +- /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. +- * GDM{2,3,4} can be used as wan port connected to an external +- * phy module. +- */ +- return port->id == 1; +-} +- + static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) + { + struct airoha_eth *eth = port->qdma->eth; +@@ -1145,7 +1136,7 @@ static int airoha_qdma_init_hfwd_queues( + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, + FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | +- LMGR_INIT_START); ++ LMGR_INIT_START | LMGR_SRAM_MODE_MASK); + + return read_poll_timeout(airoha_qdma_rr, status, + !(status & LMGR_INIT_START), USEC_PER_MSEC, +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -597,6 +597,15 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + ++static inline bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) ++{ ++ /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. ++ * GDM{2,3,4} can be used as wan port connected to an external ++ * phy module. ++ */ ++ return port->id == 1; ++} ++ + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -251,6 +251,12 @@ static int airoha_ppe_foe_entry_prepare( + else + pse_port = 2; /* uplink relies on GDM2 loopback */ + val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); ++ ++ /* For downlink traffic consume SRAM memory for hw forwarding ++ * descriptors queue. ++ */ ++ if (airhoa_is_lan_gdm_port(port)) ++ val |= AIROHA_FOE_IB2_FAST_PATH; + } + + if (is_multicast_ether_addr(data->eth.h_dest)) diff --git a/target/linux/airoha/patches-6.12/075-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_allo.patch b/target/linux/airoha/patches-6.12/075-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_allo.patch new file mode 100644 index 00000000000..4e7fbb72e50 --- /dev/null +++ b/target/linux/airoha/patches-6.12/075-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_allo.patch @@ -0,0 +1,42 @@ +From c59783780c8ad66f6076a9a7c74df3e006e29519 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sat, 24 May 2025 09:29:11 +0200 +Subject: [PATCH] net: airoha: Fix an error handling path in + airoha_alloc_gdm_port() + +If register_netdev() fails, the error handling path of the probe will not +free the memory allocated by the previous airoha_metadata_dst_alloc() call +because port->dev->reg_state will not be NETREG_REGISTERED. + +So, an explicit airoha_metadata_dst_free() call is needed in this case to +avoid a memory leak. + +Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor") +Signed-off-by: Christophe JAILLET +Acked-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/1b94b91345017429ed653e2f05d25620dc2823f9.1746715755.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2901,7 +2901,15 @@ static int airoha_alloc_gdm_port(struct + if (err) + return err; + +- return register_netdev(dev); ++ err = register_netdev(dev); ++ if (err) ++ goto free_metadata_dst; ++ ++ return 0; ++ ++free_metadata_dst: ++ airoha_metadata_dst_free(port); ++ return err; + } + + static int airoha_probe(struct platform_device *pdev) diff --git a/target/linux/airoha/patches-6.12/076-01-v6.16-net-airoha-Initialize-PPE-UPDMEM-source-mac-table.patch b/target/linux/airoha/patches-6.12/076-01-v6.16-net-airoha-Initialize-PPE-UPDMEM-source-mac-table.patch new file mode 100644 index 00000000000..334661dd964 --- /dev/null +++ b/target/linux/airoha/patches-6.12/076-01-v6.16-net-airoha-Initialize-PPE-UPDMEM-source-mac-table.patch @@ -0,0 +1,122 @@ +From a869d3a5eb011a9cf9bd864f31f5cf27362de8c7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 2 Jun 2025 12:55:37 +0200 +Subject: [PATCH 1/3] net: airoha: Initialize PPE UPDMEM source-mac table + +UPDMEM source-mac table is a key-value map used to store devices mac +addresses according to the port identifier. UPDMEM source mac table is +used during IPv6 traffic hw acceleration since PPE entries, for space +constraints, do not contain the full source mac address but just the +identifier in the UPDMEM source-mac table. +Configure UPDMEM source-mac table with device mac addresses and set +the source-mac ID field for PPE IPv6 entries in order to select the +proper device mac address as source mac for L3 IPv6 hw accelerated traffic. + +Fixes: 00a7678310fe ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250602-airoha-flowtable-ipv6-fix-v2-1-3287f8b55214@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 ++ + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_ppe.c | 26 ++++++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_regs.h | 10 +++++++++ + 4 files changed, 38 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -84,6 +84,8 @@ static void airoha_set_macaddr(struct ai + val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; + airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); + airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); ++ ++ airoha_ppe_init_upd_mem(port); + } + + static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -614,6 +614,7 @@ void airoha_ppe_check_skb(struct airoha_ + int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); ++void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); + void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -223,6 +223,7 @@ static int airoha_ppe_foe_entry_prepare( + int dsa_port = airoha_get_dsa_port(&dev); + struct airoha_foe_mac_info_common *l2; + u32 qdata, ports_pad, val; ++ u8 smac_id = 0xf; + + memset(hwe, 0, sizeof(*hwe)); + +@@ -257,6 +258,8 @@ static int airoha_ppe_foe_entry_prepare( + */ + if (airhoa_is_lan_gdm_port(port)) + val |= AIROHA_FOE_IB2_FAST_PATH; ++ ++ smac_id = port->id; + } + + if (is_multicast_ether_addr(data->eth.h_dest)) +@@ -291,7 +294,7 @@ static int airoha_ppe_foe_entry_prepare( + hwe->ipv4.l2.src_mac_lo = + get_unaligned_be16(data->eth.h_source + 4); + } else { +- l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, 0xf); ++ l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id); + } + + if (data->vlan.num) { +@@ -1238,6 +1241,27 @@ void airoha_ppe_check_skb(struct airoha_ + airoha_ppe_foe_insert_entry(ppe, skb, hash); + } + ++void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ struct net_device *dev = port->dev; ++ const u8 *addr = dev->dev_addr; ++ u32 val; ++ ++ val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ airoha_fe_wr(eth, REG_UPDMEM_DATA(0), val); ++ airoha_fe_wr(eth, REG_UPDMEM_CTRL(0), ++ FIELD_PREP(PPE_UPDMEM_ADDR_MASK, port->id) | ++ PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); ++ ++ val = (addr[0] << 8) | addr[1]; ++ airoha_fe_wr(eth, REG_UPDMEM_DATA(0), val); ++ airoha_fe_wr(eth, REG_UPDMEM_CTRL(0), ++ FIELD_PREP(PPE_UPDMEM_ADDR_MASK, port->id) | ++ FIELD_PREP(PPE_UPDMEM_OFFSET_MASK, 1) | ++ PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); ++} ++ + int airoha_ppe_init(struct airoha_eth *eth) + { + struct airoha_ppe *ppe; +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -313,6 +313,16 @@ + #define REG_PPE_RAM_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x320) + #define REG_PPE_RAM_ENTRY(_m, _n) (REG_PPE_RAM_BASE(_m) + ((_n) << 2)) + ++#define REG_UPDMEM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x370) ++#define PPE_UPDMEM_ACK_MASK BIT(31) ++#define PPE_UPDMEM_ADDR_MASK GENMASK(11, 8) ++#define PPE_UPDMEM_OFFSET_MASK GENMASK(7, 4) ++#define PPE_UPDMEM_SEL_MASK GENMASK(3, 2) ++#define PPE_UPDMEM_WR_MASK BIT(1) ++#define PPE_UPDMEM_REQ_MASK BIT(0) ++ ++#define REG_UPDMEM_DATA(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x374) ++ + #define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) + #define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) + #define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) diff --git a/target/linux/airoha/patches-6.12/076-02-v6.16-net-airoha-Fix-IPv6-hw-acceleration-in-bridge-mode.patch b/target/linux/airoha/patches-6.12/076-02-v6.16-net-airoha-Fix-IPv6-hw-acceleration-in-bridge-mode.patch new file mode 100644 index 00000000000..faa7669e0d4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/076-02-v6.16-net-airoha-Fix-IPv6-hw-acceleration-in-bridge-mode.patch @@ -0,0 +1,64 @@ +From 504a577c9b000f9e0e99e1b28616fb4eb369e1ef Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 2 Jun 2025 12:55:38 +0200 +Subject: [PATCH 2/3] net: airoha: Fix IPv6 hw acceleration in bridge mode + +ib2 and airoha_foe_mac_info_common have not the same offsets in +airoha_foe_bridge and airoha_foe_ipv6 structures. Current codebase does +not accelerate IPv6 traffic in bridge mode since ib2 and l2 info are not +set properly copying airoha_foe_bridge struct into airoha_foe_ipv6 one +in airoha_ppe_foe_commit_subflow_entry routine. +Fix IPv6 hw acceleration in bridge mode resolving ib2 and +airoha_foe_mac_info_common overwrite in +airoha_ppe_foe_commit_subflow_entry() and configuring them with proper +values. + +Fixes: cd53f622611f ("net: airoha: Add L2 hw acceleration support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250602-airoha-flowtable-ipv6-fix-v2-2-3287f8b55214@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -639,7 +639,6 @@ airoha_ppe_foe_commit_subflow_entry(stru + u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; + struct airoha_foe_entry *hwe_p, hwe; + struct airoha_flow_table_entry *f; +- struct airoha_foe_mac_info *l2; + int type; + + hwe_p = airoha_ppe_foe_get_entry(ppe, hash); +@@ -656,18 +655,20 @@ airoha_ppe_foe_commit_subflow_entry(stru + + memcpy(&hwe, hwe_p, sizeof(*hwe_p)); + hwe.ib1 = (hwe.ib1 & mask) | (e->data.ib1 & ~mask); +- l2 = &hwe.bridge.l2; +- memcpy(l2, &e->data.bridge.l2, sizeof(*l2)); + + type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); +- if (type == PPE_PKT_TYPE_IPV4_HNAPT) +- memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, +- sizeof(hwe.ipv4.new_tuple)); +- else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T && +- l2->common.etype == ETH_P_IP) +- l2->common.etype = ETH_P_IPV6; ++ if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ memcpy(&hwe.ipv6.l2, &e->data.bridge.l2, sizeof(hwe.ipv6.l2)); ++ hwe.ipv6.ib2 = e->data.bridge.ib2; ++ } else { ++ memcpy(&hwe.bridge.l2, &e->data.bridge.l2, ++ sizeof(hwe.bridge.l2)); ++ hwe.bridge.ib2 = e->data.bridge.ib2; ++ if (type == PPE_PKT_TYPE_IPV4_HNAPT) ++ memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, ++ sizeof(hwe.ipv4.new_tuple)); ++ } + +- hwe.bridge.ib2 = e->data.bridge.ib2; + hwe.bridge.data = e->data.bridge.data; + airoha_ppe_foe_commit_entry(ppe, &hwe, hash); + diff --git a/target/linux/airoha/patches-6.12/076-03-v6.16-net-airoha-Fix-smac_id-configuration-in-bridge-mode.patch b/target/linux/airoha/patches-6.12/076-03-v6.16-net-airoha-Fix-smac_id-configuration-in-bridge-mode.patch new file mode 100644 index 00000000000..f790d9d148c --- /dev/null +++ b/target/linux/airoha/patches-6.12/076-03-v6.16-net-airoha-Fix-smac_id-configuration-in-bridge-mode.patch @@ -0,0 +1,32 @@ +From c86fac5365d3a068422beeb508f2741f1a2d734d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 2 Jun 2025 12:55:39 +0200 +Subject: [PATCH 3/3] net: airoha: Fix smac_id configuration in bridge mode + +Set PPE entry smac_id field to 0xf in airoha_ppe_foe_commit_subflow_entry +routine for IPv6 traffic in order to instruct the hw to keep original +source mac address for IPv6 hw accelerated traffic in bridge mode. + +Fixes: cd53f622611f ("net: airoha: Add L2 hw acceleration support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250602-airoha-flowtable-ipv6-fix-v2-3-3287f8b55214@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -660,6 +660,11 @@ airoha_ppe_foe_commit_subflow_entry(stru + if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { + memcpy(&hwe.ipv6.l2, &e->data.bridge.l2, sizeof(hwe.ipv6.l2)); + hwe.ipv6.ib2 = e->data.bridge.ib2; ++ /* setting smac_id to 0xf instruct the hw to keep original ++ * source mac address ++ */ ++ hwe.ipv6.l2.src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, ++ 0xf); + } else { + memcpy(&hwe.bridge.l2, &e->data.bridge.l2, + sizeof(hwe.bridge.l2)); diff --git a/target/linux/airoha/patches-6.12/077-v6.17-net-airoha-Add-PPPoE-offload-support.patch b/target/linux/airoha/patches-6.12/077-v6.17-net-airoha-Add-PPPoE-offload-support.patch new file mode 100644 index 00000000000..6245f0d218d --- /dev/null +++ b/target/linux/airoha/patches-6.12/077-v6.17-net-airoha-Add-PPPoE-offload-support.patch @@ -0,0 +1,87 @@ +From 0097c4195b1d0ca57d15979626c769c74747b5a0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 9 Jun 2025 22:28:40 +0200 +Subject: [PATCH] net: airoha: Add PPPoE offload support + +Introduce flowtable hw acceleration for PPPoE traffic. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250609-b4-airoha-flowtable-pppoe-v1-1-1520fa7711b4@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 31 ++++++++++++++++++------ + 1 file changed, 23 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -232,6 +232,7 @@ static int airoha_ppe_foe_entry_prepare( + FIELD_PREP(AIROHA_FOE_IB1_BIND_UDP, l4proto == IPPROTO_UDP) | + FIELD_PREP(AIROHA_FOE_IB1_BIND_VLAN_LAYER, data->vlan.num) | + FIELD_PREP(AIROHA_FOE_IB1_BIND_VPM, data->vlan.num) | ++ FIELD_PREP(AIROHA_FOE_IB1_BIND_PPPOE, data->pppoe.num) | + AIROHA_FOE_IB1_BIND_TTL; + hwe->ib1 = val; + +@@ -281,33 +282,42 @@ static int airoha_ppe_foe_entry_prepare( + hwe->ipv6.data = qdata; + hwe->ipv6.ib2 = val; + l2 = &hwe->ipv6.l2; ++ l2->etype = ETH_P_IPV6; + } else { + hwe->ipv4.data = qdata; + hwe->ipv4.ib2 = val; + l2 = &hwe->ipv4.l2.common; ++ l2->etype = ETH_P_IP; + } + + l2->dest_mac_hi = get_unaligned_be32(data->eth.h_dest); + l2->dest_mac_lo = get_unaligned_be16(data->eth.h_dest + 4); + if (type <= PPE_PKT_TYPE_IPV4_DSLITE) { ++ struct airoha_foe_mac_info *mac_info; ++ + l2->src_mac_hi = get_unaligned_be32(data->eth.h_source); + hwe->ipv4.l2.src_mac_lo = + get_unaligned_be16(data->eth.h_source + 4); ++ ++ mac_info = (struct airoha_foe_mac_info *)l2; ++ mac_info->pppoe_id = data->pppoe.sid; + } else { +- l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id); ++ l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id) | ++ FIELD_PREP(AIROHA_FOE_MAC_PPPOE_ID, ++ data->pppoe.sid); + } + + if (data->vlan.num) { +- l2->etype = dsa_port >= 0 ? BIT(dsa_port) : 0; + l2->vlan1 = data->vlan.hdr[0].id; + if (data->vlan.num == 2) + l2->vlan2 = data->vlan.hdr[1].id; +- } else if (dsa_port >= 0) { +- l2->etype = BIT(15) | BIT(dsa_port); +- } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { +- l2->etype = ETH_P_IPV6; +- } else { +- l2->etype = ETH_P_IP; ++ } ++ ++ if (dsa_port >= 0) { ++ l2->etype = BIT(dsa_port); ++ l2->etype |= !data->vlan.num ? BIT(15) : 0; ++ } else if (data->pppoe.num) { ++ l2->etype = ETH_P_PPP_SES; + } + + return 0; +@@ -957,6 +967,11 @@ static int airoha_ppe_flow_offload_repla + case FLOW_ACTION_VLAN_POP: + break; + case FLOW_ACTION_PPPOE_PUSH: ++ if (data.pppoe.num == 1 || data.vlan.num == 2) ++ return -EOPNOTSUPP; ++ ++ data.pppoe.sid = act->pppoe.sid; ++ data.pppoe.num++; + break; + default: + return -EOPNOTSUPP; diff --git a/target/linux/airoha/patches-6.12/078-v6.16-net-airoha-Enable-RX-queues-16-31.patch b/target/linux/airoha/patches-6.12/078-v6.16-net-airoha-Enable-RX-queues-16-31.patch new file mode 100644 index 00000000000..1550c592612 --- /dev/null +++ b/target/linux/airoha/patches-6.12/078-v6.16-net-airoha-Enable-RX-queues-16-31.patch @@ -0,0 +1,28 @@ +From f478d68b653323b691280b40fbd3b8ca1ac75aa2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 9 Jun 2025 22:40:35 +0200 +Subject: [PATCH] net: airoha: Enable RX queues 16-31 + +Fix RX_DONE_INT_MASK definition in order to enable RX queues 16-31. + +Fixes: f252493e18353 ("net: airoha: Enable multiple IRQ lines support in airoha_eth driver.") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250609-aioha-fix-rx-queue-mask-v1-1-f33706a06fa2@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_regs.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -614,8 +614,9 @@ + RX19_DONE_INT_MASK | RX18_DONE_INT_MASK | \ + RX17_DONE_INT_MASK | RX16_DONE_INT_MASK) + +-#define RX_DONE_INT_MASK (RX_DONE_HIGH_INT_MASK | RX_DONE_LOW_INT_MASK) + #define RX_DONE_HIGH_OFFSET fls(RX_DONE_HIGH_INT_MASK) ++#define RX_DONE_INT_MASK \ ++ ((RX_DONE_HIGH_INT_MASK << RX_DONE_HIGH_OFFSET) | RX_DONE_LOW_INT_MASK) + + #define INT_RX2_MASK(_n) \ + ((RX_NO_CPU_DSCP_HIGH_INT_MASK & (_n)) | \ diff --git a/target/linux/airoha/patches-6.12/079-v6.16-net-airoha-Always-check-return-value-from-airoha_ppe.patch b/target/linux/airoha/patches-6.12/079-v6.16-net-airoha-Always-check-return-value-from-airoha_ppe.patch new file mode 100644 index 00000000000..551e8e3c9d8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/079-v6.16-net-airoha-Always-check-return-value-from-airoha_ppe.patch @@ -0,0 +1,32 @@ +From 78bd03ee1f20a267d2c218884b66041b3508ac9c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 18 Jun 2025 09:37:40 +0200 +Subject: [PATCH] net: airoha: Always check return value from + airoha_ppe_foe_get_entry() + +airoha_ppe_foe_get_entry routine can return NULL, so check the returned +pointer is not NULL in airoha_ppe_foe_flow_l2_entry_update() + +Fixes: b81e0f2b58be3 ("net: airoha: Add FLOW_CLS_STATS callback support") +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250618-check-ret-from-airoha_ppe_foe_get_entry-v2-1-068dcea3cc66@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -819,8 +819,10 @@ airoha_ppe_foe_flow_l2_entry_update(stru + int idle; + + hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); +- ib1 = READ_ONCE(hwe->ib1); ++ if (!hwe) ++ continue; + ++ ib1 = READ_ONCE(hwe->ib1); + state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); + if (state != AIROHA_FOE_STATE_BIND) { + iter->hash = 0xffff; diff --git a/target/linux/airoha/patches-6.12/080-01-v6.16-net-airoha-Compute-number-of-descriptors-according-t.patch b/target/linux/airoha/patches-6.12/080-01-v6.16-net-airoha-Compute-number-of-descriptors-according-t.patch new file mode 100644 index 00000000000..6744be00111 --- /dev/null +++ b/target/linux/airoha/patches-6.12/080-01-v6.16-net-airoha-Compute-number-of-descriptors-according-t.patch @@ -0,0 +1,77 @@ +From edf8afeecfbb0b8c1a2edb8c8892d2f759d35321 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 19 Jun 2025 09:07:24 +0200 +Subject: [PATCH 1/2] net: airoha: Compute number of descriptors according to + reserved memory size + +In order to not exceed the reserved memory size for hwfd buffers, +compute the number of hwfd buffers/descriptors according to the +reserved memory size and the size of each hwfd buffer (2KB). + +Fixes: 3a1ce9e3d01b ("net: airoha: Add the capability to allocate hwfd buffers via reserved-memory") +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250619-airoha-hw-num-desc-v4-1-49600a9b319a@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1086,19 +1086,13 @@ static void airoha_qdma_cleanup_tx_queue + + static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) + { ++ int size, index, num_desc = HW_DSCP_NUM; + struct airoha_eth *eth = qdma->eth; + int id = qdma - ð->qdma[0]; + dma_addr_t dma_addr; + const char *name; +- int size, index; + u32 status; + +- size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); +- if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) +- return -ENOMEM; +- +- airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); +- + name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); + if (!name) + return -ENOMEM; +@@ -1120,8 +1114,12 @@ static int airoha_qdma_init_hfwd_queues( + rmem = of_reserved_mem_lookup(np); + of_node_put(np); + dma_addr = rmem->base; ++ /* Compute the number of hw descriptors according to the ++ * reserved memory size and the payload buffer size ++ */ ++ num_desc = rmem->size / AIROHA_MAX_PACKET_SIZE; + } else { +- size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; ++ size = AIROHA_MAX_PACKET_SIZE * num_desc; + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, + GFP_KERNEL)) + return -ENOMEM; +@@ -1129,6 +1127,11 @@ static int airoha_qdma_init_hfwd_queues( + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + ++ size = num_desc * sizeof(struct airoha_qdma_fwd_desc); ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) ++ return -ENOMEM; ++ ++ airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK, + FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); +@@ -1137,7 +1140,7 @@ static int airoha_qdma_init_hfwd_queues( + airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, +- FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | ++ FIELD_PREP(HW_FWD_DESC_NUM_MASK, num_desc) | + LMGR_INIT_START | LMGR_SRAM_MODE_MASK); + + return read_poll_timeout(airoha_qdma_rr, status, diff --git a/target/linux/airoha/patches-6.12/080-02-v6.16-net-airoha-Differentiate-hwfd-buffer-size-for-QDMA0-.patch b/target/linux/airoha/patches-6.12/080-02-v6.16-net-airoha-Differentiate-hwfd-buffer-size-for-QDMA0-.patch new file mode 100644 index 00000000000..77199281446 --- /dev/null +++ b/target/linux/airoha/patches-6.12/080-02-v6.16-net-airoha-Differentiate-hwfd-buffer-size-for-QDMA0-.patch @@ -0,0 +1,64 @@ +From 7b46bdaec00a675f6fac9d0b01a2105b5746ebe9 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 19 Jun 2025 09:07:25 +0200 +Subject: [PATCH 2/2] net: airoha: Differentiate hwfd buffer size for QDMA0 and + QDMA1 + +EN7581 SoC allows configuring the size and the number of buffers in +hwfd payload queue for both QDMA0 and QDMA1. +In order to reduce the required DRAM used for hwfd buffers queues and +decrease the memory footprint, differentiate hwfd buffer size for QDMA0 +and QDMA1 and reduce hwfd buffer size to 1KB for QDMA1 (WAN) while +maintaining 2KB for QDMA0 (LAN). + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250619-airoha-hw-num-desc-v4-2-49600a9b319a@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1089,14 +1089,15 @@ static int airoha_qdma_init_hfwd_queues( + int size, index, num_desc = HW_DSCP_NUM; + struct airoha_eth *eth = qdma->eth; + int id = qdma - ð->qdma[0]; ++ u32 status, buf_size; + dma_addr_t dma_addr; + const char *name; +- u32 status; + + name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); + if (!name) + return -ENOMEM; + ++ buf_size = id ? AIROHA_MAX_PACKET_SIZE / 2 : AIROHA_MAX_PACKET_SIZE; + index = of_property_match_string(eth->dev->of_node, + "memory-region-names", name); + if (index >= 0) { +@@ -1117,9 +1118,9 @@ static int airoha_qdma_init_hfwd_queues( + /* Compute the number of hw descriptors according to the + * reserved memory size and the payload buffer size + */ +- num_desc = rmem->size / AIROHA_MAX_PACKET_SIZE; ++ num_desc = div_u64(rmem->size, buf_size); + } else { +- size = AIROHA_MAX_PACKET_SIZE * num_desc; ++ size = buf_size * num_desc; + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, + GFP_KERNEL)) + return -ENOMEM; +@@ -1132,9 +1133,10 @@ static int airoha_qdma_init_hfwd_queues( + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); ++ /* QDMA0: 2KB. QDMA1: 1KB */ + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK, +- FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); ++ FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, !!id)); + airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, + FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); + airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, diff --git a/target/linux/airoha/patches-6.12/081-v6.17-net-airoha-Fix-PPE-table-access-in-airoha_ppe_debugf.patch b/target/linux/airoha/patches-6.12/081-v6.17-net-airoha-Fix-PPE-table-access-in-airoha_ppe_debugf.patch new file mode 100644 index 00000000000..919b6b44da0 --- /dev/null +++ b/target/linux/airoha/patches-6.12/081-v6.17-net-airoha-Fix-PPE-table-access-in-airoha_ppe_debugf.patch @@ -0,0 +1,92 @@ +From 38358fa3cc8e16c6862a3e5c5c233f9f652e3a6d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 31 Jul 2025 12:29:08 +0200 +Subject: [PATCH] net: airoha: Fix PPE table access in + airoha_ppe_debugfs_foe_show() + +In order to avoid any possible race we need to hold the ppe_lock +spinlock accessing the hw PPE table. airoha_ppe_foe_get_entry routine is +always executed holding ppe_lock except in airoha_ppe_debugfs_foe_show +routine. Fix the problem introducing airoha_ppe_foe_get_entry_locked +routine. + +Fixes: 3fe15c640f380 ("net: airoha: Introduce PPE debugfs support") +Reviewed-by: Dawid Osuchowski +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250731-airoha_ppe_foe_get_entry_locked-v2-1-50efbd8c0fd6@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 26 ++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -508,9 +508,11 @@ static void airoha_ppe_foe_flow_stats_up + FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq); + } + +-struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, +- u32 hash) ++static struct airoha_foe_entry * ++airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash) + { ++ lockdep_assert_held(&ppe_lock); ++ + if (hash < PPE_SRAM_NUM_ENTRIES) { + u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); + struct airoha_eth *eth = ppe->eth; +@@ -537,6 +539,18 @@ struct airoha_foe_entry *airoha_ppe_foe_ + return ppe->foe + hash * sizeof(struct airoha_foe_entry); + } + ++struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, ++ u32 hash) ++{ ++ struct airoha_foe_entry *hwe; ++ ++ spin_lock_bh(&ppe_lock); ++ hwe = airoha_ppe_foe_get_entry_locked(ppe, hash); ++ spin_unlock_bh(&ppe_lock); ++ ++ return hwe; ++} ++ + static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e, + struct airoha_foe_entry *hwe) + { +@@ -651,7 +665,7 @@ airoha_ppe_foe_commit_subflow_entry(stru + struct airoha_flow_table_entry *f; + int type; + +- hwe_p = airoha_ppe_foe_get_entry(ppe, hash); ++ hwe_p = airoha_ppe_foe_get_entry_locked(ppe, hash); + if (!hwe_p) + return -EINVAL; + +@@ -703,7 +717,7 @@ static void airoha_ppe_foe_insert_entry( + + spin_lock_bh(&ppe_lock); + +- hwe = airoha_ppe_foe_get_entry(ppe, hash); ++ hwe = airoha_ppe_foe_get_entry_locked(ppe, hash); + if (!hwe) + goto unlock; + +@@ -818,7 +832,7 @@ airoha_ppe_foe_flow_l2_entry_update(stru + u32 ib1, state; + int idle; + +- hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); ++ hwe = airoha_ppe_foe_get_entry_locked(ppe, iter->hash); + if (!hwe) + continue; + +@@ -855,7 +869,7 @@ static void airoha_ppe_foe_flow_entry_up + if (e->hash == 0xffff) + goto unlock; + +- hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash); ++ hwe_p = airoha_ppe_foe_get_entry_locked(ppe, e->hash); + if (!hwe_p) + goto unlock; + diff --git a/target/linux/airoha/patches-6.12/082-v6.17-net-airoha-ppe-Do-not-invalid-PPE-entries-in-case-of.patch b/target/linux/airoha/patches-6.12/082-v6.17-net-airoha-ppe-Do-not-invalid-PPE-entries-in-case-of.patch new file mode 100644 index 00000000000..eda914aab71 --- /dev/null +++ b/target/linux/airoha/patches-6.12/082-v6.17-net-airoha-ppe-Do-not-invalid-PPE-entries-in-case-of.patch @@ -0,0 +1,42 @@ +From 9f6b606b6b37e61427412708411e8e04b1a858e8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 18 Aug 2025 11:58:25 +0200 +Subject: [PATCH] net: airoha: ppe: Do not invalid PPE entries in case of SW + hash collision + +SW hash computed by airoha_ppe_foe_get_entry_hash routine (used for +foe_flow hlist) can theoretically produce collisions between two +different HW PPE entries. +In airoha_ppe_foe_insert_entry() if the collision occurs we will mark +the second PPE entry in the list as stale (setting the hw hash to 0xffff). +Stale entries are no more updated in airoha_ppe_foe_flow_entry_update +routine and so they are removed by Netfilter. +Fix the problem not marking the second entry as stale in +airoha_ppe_foe_insert_entry routine if we have already inserted the +brand new entry in the PPE table and let Netfilter remove real stale +entries according to their timestamp. +Please note this is just a theoretical issue spotted reviewing the code +and not faced running the system. + +Fixes: cd53f622611f9 ("net: airoha: Add L2 hw acceleration support") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250818-airoha-en7581-hash-collision-fix-v1-1-d190c4b53d1c@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -736,10 +736,8 @@ static void airoha_ppe_foe_insert_entry( + continue; + } + +- if (commit_done || !airoha_ppe_foe_compare_entry(e, hwe)) { +- e->hash = 0xffff; ++ if (!airoha_ppe_foe_compare_entry(e, hwe)) + continue; +- } + + airoha_ppe_foe_commit_entry(ppe, &e->data, hash); + commit_done = true; diff --git a/target/linux/airoha/patches-6.12/084-01-v6.18-net-airoha-npu-Add-NPU-wlan-memory-initialization-co.patch b/target/linux/airoha/patches-6.12/084-01-v6.18-net-airoha-npu-Add-NPU-wlan-memory-initialization-co.patch new file mode 100644 index 00000000000..7e9e9423ec2 --- /dev/null +++ b/target/linux/airoha/patches-6.12/084-01-v6.18-net-airoha-npu-Add-NPU-wlan-memory-initialization-co.patch @@ -0,0 +1,179 @@ +From 564923b02c1d2fe02ee789f9849ff79979b63b9f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:37 +0200 +Subject: [PATCH 1/6] net: airoha: npu: Add NPU wlan memory initialization + commands + +Introduce wlan_init_reserved_memory callback used by MT76 driver during +NPU wlan offloading setup. +This is a preliminary patch to enable wlan flowtable offload for EN7581 +SoC with MT76 driver. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-2-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 82 ++++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 38 +++++++++++ + 2 files changed, 120 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -124,6 +124,13 @@ struct ppe_mbox_data { + }; + }; + ++struct wlan_mbox_data { ++ u32 ifindex:4; ++ u32 func_type:4; ++ u32 func_id; ++ DECLARE_FLEX_ARRAY(u8, d); ++}; ++ + static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, + void *p, int size) + { +@@ -390,6 +397,80 @@ out: + return err; + } + ++static int airoha_npu_wlan_msg_send(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_set_cmd func_id, ++ void *data, int data_len, gfp_t gfp) ++{ ++ struct wlan_mbox_data *wlan_data; ++ int err, len; ++ ++ len = sizeof(*wlan_data) + data_len; ++ wlan_data = kzalloc(len, gfp); ++ if (!wlan_data) ++ return -ENOMEM; ++ ++ wlan_data->ifindex = ifindex; ++ wlan_data->func_type = NPU_OP_SET; ++ wlan_data->func_id = func_id; ++ memcpy(wlan_data->d, data, data_len); ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_WIFI, wlan_data, len); ++ kfree(wlan_data); ++ ++ return err; ++} ++ ++static int ++airoha_npu_wlan_set_reserved_memory(struct airoha_npu *npu, ++ int ifindex, const char *name, ++ enum airoha_npu_wlan_set_cmd func_id) ++{ ++ struct device *dev = npu->dev; ++ struct resource res; ++ int err; ++ u32 val; ++ ++ err = of_reserved_mem_region_to_resource_byname(dev->of_node, name, ++ &res); ++ if (err) ++ return err; ++ ++ val = res.start; ++ return airoha_npu_wlan_msg_send(npu, ifindex, func_id, &val, ++ sizeof(val), GFP_KERNEL); ++} ++ ++static int airoha_npu_wlan_init_memory(struct airoha_npu *npu) ++{ ++ enum airoha_npu_wlan_set_cmd cmd = WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU; ++ u32 val = 0; ++ int err; ++ ++ err = airoha_npu_wlan_msg_send(npu, 1, cmd, &val, sizeof(val), ++ GFP_KERNEL); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "tx-bufid", cmd); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "pkt", cmd); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "tx-pkt", cmd); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU; ++ return airoha_npu_wlan_msg_send(npu, 0, cmd, &val, sizeof(val), ++ GFP_KERNEL); ++} ++ + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; +@@ -493,6 +574,7 @@ static int airoha_npu_probe(struct platf + npu->ops.ppe_deinit = airoha_npu_ppe_deinit; + npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries; + npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry; ++ npu->ops.wlan_init_reserved_memory = airoha_npu_wlan_init_memory; + + npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(npu->regmap)) +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -6,6 +6,43 @@ + + #define NPU_NUM_CORES 8 + ++enum airoha_npu_wlan_set_cmd { ++ WLAN_FUNC_SET_WAIT_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_DESC, ++ WLAN_FUNC_SET_WAIT_NPU_INIT_DONE, ++ WLAN_FUNC_SET_WAIT_TRAN_TO_CPU, ++ WLAN_FUNC_SET_WAIT_BA_WIN_SIZE, ++ WLAN_FUNC_SET_WAIT_DRIVER_MODEL, ++ WLAN_FUNC_SET_WAIT_DEL_STA, ++ WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR, ++ WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_IS_TEST_NOBA, ++ WLAN_FUNC_SET_WAIT_FLUSHONE_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_FLUSHALL_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU, ++ WLAN_FUNC_SET_WAIT_PCIE_STATE, ++ WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, ++ WLAN_FUNC_SET_WAIT_ERROR_RETRY_TIMES, ++ WLAN_FUNC_SET_WAIT_BAR_INFO, ++ WLAN_FUNC_SET_WAIT_FAST_FLAG, ++ WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU, ++ WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_TX_DESC_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_DEBUG_FLAG, ++ WLAN_FUNC_SET_WAIT_INODE_HW_CFG_INFO, ++ WLAN_FUNC_SET_WAIT_INODE_STOP_ACTION, ++ WLAN_FUNC_SET_WAIT_INODE_PCIE_SWAP, ++ WLAN_FUNC_SET_WAIT_RATELIMIT_CTRL, ++ WLAN_FUNC_SET_WAIT_HWNAT_INIT, ++ WLAN_FUNC_SET_WAIT_ARHT_CHIP_INFO, ++ WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR, ++ WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, ++}; ++ + struct airoha_npu { + struct device *dev; + struct regmap *regmap; +@@ -29,6 +66,7 @@ struct airoha_npu { + dma_addr_t foe_addr, + u32 entry_size, u32 hash, + bool ppe2); ++ int (*wlan_init_reserved_memory)(struct airoha_npu *npu); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.12/084-02-v6.18-net-airoha-npu-Add-wlan_-send-get-_msg-NPU-callbacks.patch b/target/linux/airoha/patches-6.12/084-02-v6.18-net-airoha-npu-Add-wlan_-send-get-_msg-NPU-callbacks.patch new file mode 100644 index 00000000000..5ff820d0be9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/084-02-v6.18-net-airoha-npu-Add-wlan_-send-get-_msg-NPU-callbacks.patch @@ -0,0 +1,139 @@ +From f97fc66185b2004ad5f393f78b3e645009ddd1d0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:38 +0200 +Subject: [PATCH 2/6] net: airoha: npu: Add wlan_{send,get}_msg NPU callbacks + +Introduce wlan_send_msg() and wlan_get_msg() NPU wlan callbacks used +by the wlan driver (MT76) to initialize NPU module registers in order to +offload wireless-wired traffic. +This is a preliminary patch to enable wlan flowtable offload for EN7581 +SoC with MT76 driver. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-3-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 52 ++++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 22 ++++++++++ + 2 files changed, 74 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -42,6 +42,22 @@ + #define REG_CR_MBQ8_CTRL(_n) (NPU_MBOX_BASE_ADDR + 0x0b0 + ((_n) << 2)) + #define REG_CR_NPU_MIB(_n) (NPU_MBOX_BASE_ADDR + 0x140 + ((_n) << 2)) + ++#define NPU_WLAN_BASE_ADDR 0x30d000 ++ ++#define REG_IRQ_STATUS (NPU_WLAN_BASE_ADDR + 0x030) ++#define REG_IRQ_RXDONE(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 2) + 0x034) ++#define NPU_IRQ_RX_MASK(_n) ((_n) == 1 ? BIT(17) : BIT(16)) ++ ++#define REG_TX_BASE(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x080) ++#define REG_TX_DSCP_NUM(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x084) ++#define REG_TX_CPU_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x088) ++#define REG_TX_DMA_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x08c) ++ ++#define REG_RX_BASE(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x180) ++#define REG_RX_DSCP_NUM(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x184) ++#define REG_RX_CPU_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x188) ++#define REG_RX_DMA_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x18c) ++ + #define NPU_TIMER_BASE_ADDR 0x310100 + #define REG_WDT_TIMER_CTRL(_n) (NPU_TIMER_BASE_ADDR + ((_n) * 0x100)) + #define WDT_EN_MASK BIT(25) +@@ -420,6 +436,30 @@ static int airoha_npu_wlan_msg_send(stru + return err; + } + ++static int airoha_npu_wlan_msg_get(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd func_id, ++ void *data, int data_len, gfp_t gfp) ++{ ++ struct wlan_mbox_data *wlan_data; ++ int err, len; ++ ++ len = sizeof(*wlan_data) + data_len; ++ wlan_data = kzalloc(len, gfp); ++ if (!wlan_data) ++ return -ENOMEM; ++ ++ wlan_data->ifindex = ifindex; ++ wlan_data->func_type = NPU_OP_GET; ++ wlan_data->func_id = func_id; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_WIFI, wlan_data, len); ++ if (!err) ++ memcpy(data, wlan_data->d, data_len); ++ kfree(wlan_data); ++ ++ return err; ++} ++ + static int + airoha_npu_wlan_set_reserved_memory(struct airoha_npu *npu, + int ifindex, const char *name, +@@ -471,6 +511,15 @@ static int airoha_npu_wlan_init_memory(s + GFP_KERNEL); + } + ++static u32 airoha_npu_wlan_queue_addr_get(struct airoha_npu *npu, int qid, ++ bool xmit) ++{ ++ if (xmit) ++ return REG_TX_BASE(qid + 2); ++ ++ return REG_RX_BASE(qid); ++} ++ + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; +@@ -575,6 +624,9 @@ static int airoha_npu_probe(struct platf + npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries; + npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry; + npu->ops.wlan_init_reserved_memory = airoha_npu_wlan_init_memory; ++ npu->ops.wlan_send_msg = airoha_npu_wlan_msg_send; ++ npu->ops.wlan_get_msg = airoha_npu_wlan_msg_get; ++ npu->ops.wlan_get_queue_addr = airoha_npu_wlan_queue_addr_get; + + npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(npu->regmap)) +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -43,6 +43,20 @@ enum airoha_npu_wlan_set_cmd { + WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, + }; + ++enum airoha_npu_wlan_get_cmd { ++ WLAN_FUNC_GET_WAIT_NPU_INFO, ++ WLAN_FUNC_GET_WAIT_LAST_RATE, ++ WLAN_FUNC_GET_WAIT_COUNTER, ++ WLAN_FUNC_GET_WAIT_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_RXDESC_BASE, ++ WLAN_FUNC_GET_WAIT_WCID_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_DMA_ADDR, ++ WLAN_FUNC_GET_WAIT_RING_SIZE, ++ WLAN_FUNC_GET_WAIT_NPU_SUPPORT_MAP, ++ WLAN_FUNC_GET_WAIT_MDC_LOCK_ADDRESS, ++ WLAN_FUNC_GET_WAIT_NPU_VERSION, ++}; ++ + struct airoha_npu { + struct device *dev; + struct regmap *regmap; +@@ -67,6 +81,14 @@ struct airoha_npu { + u32 entry_size, u32 hash, + bool ppe2); + int (*wlan_init_reserved_memory)(struct airoha_npu *npu); ++ int (*wlan_send_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_set_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ int (*wlan_get_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, ++ bool xmit); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.12/084-03-v6.18-net-airoha-npu-Add-wlan-irq-management-callbacks.patch b/target/linux/airoha/patches-6.12/084-03-v6.18-net-airoha-npu-Add-wlan-irq-management-callbacks.patch new file mode 100644 index 00000000000..f05b947cecd --- /dev/null +++ b/target/linux/airoha/patches-6.12/084-03-v6.18-net-airoha-npu-Add-wlan-irq-management-callbacks.patch @@ -0,0 +1,74 @@ +From 03b7ca3ee5e1b700c462aed5b6cb88f616d6ba7f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:39 +0200 +Subject: [PATCH 3/6] net: airoha: npu: Add wlan irq management callbacks + +Introduce callbacks used by the MT76 driver to configure NPU SoC +interrupts. This is a preliminary patch to enable wlan flowtable +offload for EN7581 SoC with MT76 driver. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-4-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 27 ++++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 4 ++++ + 2 files changed, 31 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -520,6 +520,29 @@ static u32 airoha_npu_wlan_queue_addr_ge + return REG_RX_BASE(qid); + } + ++static void airoha_npu_wlan_irq_status_set(struct airoha_npu *npu, u32 val) ++{ ++ regmap_write(npu->regmap, REG_IRQ_STATUS, val); ++} ++ ++static u32 airoha_npu_wlan_irq_status_get(struct airoha_npu *npu, int q) ++{ ++ u32 val; ++ ++ regmap_read(npu->regmap, REG_IRQ_STATUS, &val); ++ return val; ++} ++ ++static void airoha_npu_wlan_irq_enable(struct airoha_npu *npu, int q) ++{ ++ regmap_set_bits(npu->regmap, REG_IRQ_RXDONE(q), NPU_IRQ_RX_MASK(q)); ++} ++ ++static void airoha_npu_wlan_irq_disable(struct airoha_npu *npu, int q) ++{ ++ regmap_clear_bits(npu->regmap, REG_IRQ_RXDONE(q), NPU_IRQ_RX_MASK(q)); ++} ++ + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; +@@ -627,6 +650,10 @@ static int airoha_npu_probe(struct platf + npu->ops.wlan_send_msg = airoha_npu_wlan_msg_send; + npu->ops.wlan_get_msg = airoha_npu_wlan_msg_get; + npu->ops.wlan_get_queue_addr = airoha_npu_wlan_queue_addr_get; ++ npu->ops.wlan_set_irq_status = airoha_npu_wlan_irq_status_set; ++ npu->ops.wlan_get_irq_status = airoha_npu_wlan_irq_status_get; ++ npu->ops.wlan_enable_irq = airoha_npu_wlan_irq_enable; ++ npu->ops.wlan_disable_irq = airoha_npu_wlan_irq_disable; + + npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(npu->regmap)) +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -89,6 +89,10 @@ struct airoha_npu { + void *data, int data_len, gfp_t gfp); + u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, + bool xmit); ++ void (*wlan_set_irq_status)(struct airoha_npu *npu, u32 val); ++ u32 (*wlan_get_irq_status)(struct airoha_npu *npu, int q); ++ void (*wlan_enable_irq)(struct airoha_npu *npu, int q); ++ void (*wlan_disable_irq)(struct airoha_npu *npu, int q); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.12/084-04-v6.18-net-airoha-npu-Read-NPU-wlan-interrupt-lines-from-th.patch b/target/linux/airoha/patches-6.12/084-04-v6.18-net-airoha-npu-Read-NPU-wlan-interrupt-lines-from-th.patch new file mode 100644 index 00000000000..234dc8b99ae --- /dev/null +++ b/target/linux/airoha/patches-6.12/084-04-v6.18-net-airoha-npu-Read-NPU-wlan-interrupt-lines-from-th.patch @@ -0,0 +1,58 @@ +From a1740b16c83729d908c760eaa821f27b51e58a13 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:40 +0200 +Subject: [PATCH 4/6] net: airoha: npu: Read NPU wlan interrupt lines from the + DTS + +Read all NPU wlan IRQ lines from the NPU device-tree node. +NPU module fires wlan irq lines when the traffic to/from the WiFi NIC is +not hw accelerated (these interrupts will be consumed by the MT76 driver +in subsequent patches). +This is a preliminary patch to enable wlan flowtable offload for EN7581 +SoC. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-5-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 9 +++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 3 +++ + 2 files changed, 12 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -696,6 +696,15 @@ static int airoha_npu_probe(struct platf + INIT_WORK(&core->wdt_work, airoha_npu_wdt_work); + } + ++ /* wlan IRQ lines */ ++ for (i = 0; i < ARRAY_SIZE(npu->irqs); i++) { ++ irq = platform_get_irq(pdev, i + ARRAY_SIZE(npu->cores) + 1); ++ if (irq < 0) ++ return irq; ++ ++ npu->irqs[i] = irq; ++ } ++ + err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (err) + return err; +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -5,6 +5,7 @@ + */ + + #define NPU_NUM_CORES 8 ++#define NPU_NUM_IRQ 6 + + enum airoha_npu_wlan_set_cmd { + WLAN_FUNC_SET_WAIT_PCIE_ADDR, +@@ -68,6 +69,8 @@ struct airoha_npu { + struct work_struct wdt_work; + } cores[NPU_NUM_CORES]; + ++ int irqs[NPU_NUM_IRQ]; ++ + struct airoha_foe_stats __iomem *stats; + + struct { diff --git a/target/linux/airoha/patches-6.12/084-05-v6.18-net-airoha-npu-Enable-core-3-for-WiFi-offloading.patch b/target/linux/airoha/patches-6.12/084-05-v6.18-net-airoha-npu-Enable-core-3-for-WiFi-offloading.patch new file mode 100644 index 00000000000..c285af23c32 --- /dev/null +++ b/target/linux/airoha/patches-6.12/084-05-v6.18-net-airoha-npu-Enable-core-3-for-WiFi-offloading.patch @@ -0,0 +1,28 @@ +From 29c4a3ce508961a02d185ead2d52699b16d82c6d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:41 +0200 +Subject: [PATCH 5/6] net: airoha: npu: Enable core 3 for WiFi offloading + +NPU core 3 is responsible for WiFi offloading so enable it during NPU +probe. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-6-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -726,8 +726,7 @@ static int airoha_npu_probe(struct platf + usleep_range(1000, 2000); + + /* enable NPU cores */ +- /* do not start core3 since it is used for WiFi offloading */ +- regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xf7); ++ regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xff); + regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1); + msleep(100); + diff --git a/target/linux/airoha/patches-6.12/084-06-v6.18-net-airoha-Add-airoha_offload.h-header.patch b/target/linux/airoha/patches-6.12/084-06-v6.18-net-airoha-Add-airoha_offload.h-header.patch new file mode 100644 index 00000000000..ef98c85c366 --- /dev/null +++ b/target/linux/airoha/patches-6.12/084-06-v6.18-net-airoha-Add-airoha_offload.h-header.patch @@ -0,0 +1,416 @@ +From b3ef7bdec66fb1813e865fd39d179a93cefd2015 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:42 +0200 +Subject: [PATCH 6/6] net: airoha: Add airoha_offload.h header + +Move NPU definitions to airoha_offload.h in include/linux/soc/airoha/ in +order to allow the MT76 driver to access the callback definitions. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-7-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 2 +- + drivers/net/ethernet/airoha/airoha_npu.h | 103 --------- + drivers/net/ethernet/airoha/airoha_ppe.c | 2 +- + include/linux/soc/airoha/airoha_offload.h | 260 ++++++++++++++++++++++ + 4 files changed, 262 insertions(+), 105 deletions(-) + delete mode 100644 drivers/net/ethernet/airoha/airoha_npu.h + create mode 100644 include/linux/soc/airoha/airoha_offload.h + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -11,9 +11,9 @@ + #include + #include + #include ++#include + + #include "airoha_eth.h" +-#include "airoha_npu.h" + + #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" + #define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin" +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ /dev/null +@@ -1,103 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Copyright (c) 2025 AIROHA Inc +- * Author: Lorenzo Bianconi +- */ +- +-#define NPU_NUM_CORES 8 +-#define NPU_NUM_IRQ 6 +- +-enum airoha_npu_wlan_set_cmd { +- WLAN_FUNC_SET_WAIT_PCIE_ADDR, +- WLAN_FUNC_SET_WAIT_DESC, +- WLAN_FUNC_SET_WAIT_NPU_INIT_DONE, +- WLAN_FUNC_SET_WAIT_TRAN_TO_CPU, +- WLAN_FUNC_SET_WAIT_BA_WIN_SIZE, +- WLAN_FUNC_SET_WAIT_DRIVER_MODEL, +- WLAN_FUNC_SET_WAIT_DEL_STA, +- WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR, +- WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR, +- WLAN_FUNC_SET_WAIT_IS_TEST_NOBA, +- WLAN_FUNC_SET_WAIT_FLUSHONE_TIMEOUT, +- WLAN_FUNC_SET_WAIT_FLUSHALL_TIMEOUT, +- WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU, +- WLAN_FUNC_SET_WAIT_PCIE_STATE, +- WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, +- WLAN_FUNC_SET_WAIT_ERROR_RETRY_TIMES, +- WLAN_FUNC_SET_WAIT_BAR_INFO, +- WLAN_FUNC_SET_WAIT_FAST_FLAG, +- WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU, +- WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, +- WLAN_FUNC_SET_WAIT_TX_DESC_HW_BASE, +- WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, +- WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, +- WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR, +- WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, +- WLAN_FUNC_SET_WAIT_INODE_DEBUG_FLAG, +- WLAN_FUNC_SET_WAIT_INODE_HW_CFG_INFO, +- WLAN_FUNC_SET_WAIT_INODE_STOP_ACTION, +- WLAN_FUNC_SET_WAIT_INODE_PCIE_SWAP, +- WLAN_FUNC_SET_WAIT_RATELIMIT_CTRL, +- WLAN_FUNC_SET_WAIT_HWNAT_INIT, +- WLAN_FUNC_SET_WAIT_ARHT_CHIP_INFO, +- WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR, +- WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, +-}; +- +-enum airoha_npu_wlan_get_cmd { +- WLAN_FUNC_GET_WAIT_NPU_INFO, +- WLAN_FUNC_GET_WAIT_LAST_RATE, +- WLAN_FUNC_GET_WAIT_COUNTER, +- WLAN_FUNC_GET_WAIT_DBG_COUNTER, +- WLAN_FUNC_GET_WAIT_RXDESC_BASE, +- WLAN_FUNC_GET_WAIT_WCID_DBG_COUNTER, +- WLAN_FUNC_GET_WAIT_DMA_ADDR, +- WLAN_FUNC_GET_WAIT_RING_SIZE, +- WLAN_FUNC_GET_WAIT_NPU_SUPPORT_MAP, +- WLAN_FUNC_GET_WAIT_MDC_LOCK_ADDRESS, +- WLAN_FUNC_GET_WAIT_NPU_VERSION, +-}; +- +-struct airoha_npu { +- struct device *dev; +- struct regmap *regmap; +- +- struct airoha_npu_core { +- struct airoha_npu *npu; +- /* protect concurrent npu memory accesses */ +- spinlock_t lock; +- struct work_struct wdt_work; +- } cores[NPU_NUM_CORES]; +- +- int irqs[NPU_NUM_IRQ]; +- +- struct airoha_foe_stats __iomem *stats; +- +- struct { +- int (*ppe_init)(struct airoha_npu *npu); +- int (*ppe_deinit)(struct airoha_npu *npu); +- int (*ppe_flush_sram_entries)(struct airoha_npu *npu, +- dma_addr_t foe_addr, +- int sram_num_entries); +- int (*ppe_foe_commit_entry)(struct airoha_npu *npu, +- dma_addr_t foe_addr, +- u32 entry_size, u32 hash, +- bool ppe2); +- int (*wlan_init_reserved_memory)(struct airoha_npu *npu); +- int (*wlan_send_msg)(struct airoha_npu *npu, int ifindex, +- enum airoha_npu_wlan_set_cmd func_id, +- void *data, int data_len, gfp_t gfp); +- int (*wlan_get_msg)(struct airoha_npu *npu, int ifindex, +- enum airoha_npu_wlan_get_cmd func_id, +- void *data, int data_len, gfp_t gfp); +- u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, +- bool xmit); +- void (*wlan_set_irq_status)(struct airoha_npu *npu, u32 val); +- u32 (*wlan_get_irq_status)(struct airoha_npu *npu, int q); +- void (*wlan_enable_irq)(struct airoha_npu *npu, int q); +- void (*wlan_disable_irq)(struct airoha_npu *npu, int q); +- } ops; +-}; +- +-struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); +-void airoha_npu_put(struct airoha_npu *npu); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -7,10 +7,10 @@ + #include + #include + #include ++#include + #include + #include + +-#include "airoha_npu.h" + #include "airoha_regs.h" + #include "airoha_eth.h" + +--- /dev/null ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -0,0 +1,260 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2025 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++#ifndef AIROHA_OFFLOAD_H ++#define AIROHA_OFFLOAD_H ++ ++#include ++#include ++ ++#define NPU_NUM_CORES 8 ++#define NPU_NUM_IRQ 6 ++#define NPU_RX0_DESC_NUM 512 ++#define NPU_RX1_DESC_NUM 512 ++ ++/* CTRL */ ++#define NPU_RX_DMA_DESC_LAST_MASK BIT(29) ++#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15) ++#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1) ++#define NPU_RX_DMA_DESC_DONE_MASK BIT(0) ++/* INFO */ ++#define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 28) ++#define NPU_RX_DMA_PKT_ID_MASK GENMASK(28, 26) ++#define NPU_RX_DMA_SRC_PORT_MASK GENMASK(25, 21) ++#define NPU_RX_DMA_CRSN_MASK GENMASK(20, 16) ++#define NPU_RX_DMA_FOE_ID_MASK GENMASK(15, 0) ++/* DATA */ ++#define NPU_RX_DMA_SID_MASK GENMASK(31, 16) ++#define NPU_RX_DMA_FRAG_TYPE_MASK GENMASK(15, 14) ++#define NPU_RX_DMA_PRIORITY_MASK GENMASK(13, 10) ++#define NPU_RX_DMA_RADIO_ID_MASK GENMASK(9, 6) ++#define NPU_RX_DMA_VAP_ID_MASK GENMASK(5, 2) ++#define NPU_RX_DMA_FRAME_TYPE_MASK GENMASK(1, 0) ++ ++struct airoha_npu_rx_dma_desc { ++ u32 ctrl; ++ u32 info; ++ u32 data; ++ u32 addr; ++ u64 rsv; ++} __packed; ++ ++/* CTRL */ ++#define NPU_TX_DMA_DESC_SCHED_MASK BIT(31) ++#define NPU_TX_DMA_DESC_LEN_MASK GENMASK(30, 18) ++#define NPU_TX_DMA_DESC_VEND_LEN_MASK GENMASK(17, 1) ++#define NPU_TX_DMA_DESC_DONE_MASK BIT(0) ++ ++#define NPU_TXWI_LEN 192 ++ ++struct airoha_npu_tx_dma_desc { ++ u32 ctrl; ++ u32 addr; ++ u64 rsv; ++ u8 txwi[NPU_TXWI_LEN]; ++} __packed; ++ ++enum airoha_npu_wlan_set_cmd { ++ WLAN_FUNC_SET_WAIT_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_DESC, ++ WLAN_FUNC_SET_WAIT_NPU_INIT_DONE, ++ WLAN_FUNC_SET_WAIT_TRAN_TO_CPU, ++ WLAN_FUNC_SET_WAIT_BA_WIN_SIZE, ++ WLAN_FUNC_SET_WAIT_DRIVER_MODEL, ++ WLAN_FUNC_SET_WAIT_DEL_STA, ++ WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR, ++ WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_IS_TEST_NOBA, ++ WLAN_FUNC_SET_WAIT_FLUSHONE_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_FLUSHALL_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU, ++ WLAN_FUNC_SET_WAIT_PCIE_STATE, ++ WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, ++ WLAN_FUNC_SET_WAIT_ERROR_RETRY_TIMES, ++ WLAN_FUNC_SET_WAIT_BAR_INFO, ++ WLAN_FUNC_SET_WAIT_FAST_FLAG, ++ WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU, ++ WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_TX_DESC_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_DEBUG_FLAG, ++ WLAN_FUNC_SET_WAIT_INODE_HW_CFG_INFO, ++ WLAN_FUNC_SET_WAIT_INODE_STOP_ACTION, ++ WLAN_FUNC_SET_WAIT_INODE_PCIE_SWAP, ++ WLAN_FUNC_SET_WAIT_RATELIMIT_CTRL, ++ WLAN_FUNC_SET_WAIT_HWNAT_INIT, ++ WLAN_FUNC_SET_WAIT_ARHT_CHIP_INFO, ++ WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR, ++ WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, ++}; ++ ++enum airoha_npu_wlan_get_cmd { ++ WLAN_FUNC_GET_WAIT_NPU_INFO, ++ WLAN_FUNC_GET_WAIT_LAST_RATE, ++ WLAN_FUNC_GET_WAIT_COUNTER, ++ WLAN_FUNC_GET_WAIT_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_RXDESC_BASE, ++ WLAN_FUNC_GET_WAIT_WCID_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_DMA_ADDR, ++ WLAN_FUNC_GET_WAIT_RING_SIZE, ++ WLAN_FUNC_GET_WAIT_NPU_SUPPORT_MAP, ++ WLAN_FUNC_GET_WAIT_MDC_LOCK_ADDRESS, ++ WLAN_FUNC_GET_WAIT_NPU_VERSION, ++}; ++ ++struct airoha_npu { ++#if (IS_BUILTIN(CONFIG_NET_AIROHA_NPU) || IS_MODULE(CONFIG_NET_AIROHA_NPU)) ++ struct device *dev; ++ struct regmap *regmap; ++ ++ struct airoha_npu_core { ++ struct airoha_npu *npu; ++ /* protect concurrent npu memory accesses */ ++ spinlock_t lock; ++ struct work_struct wdt_work; ++ } cores[NPU_NUM_CORES]; ++ ++ int irqs[NPU_NUM_IRQ]; ++ ++ struct airoha_foe_stats __iomem *stats; ++ ++ struct { ++ int (*ppe_init)(struct airoha_npu *npu); ++ int (*ppe_deinit)(struct airoha_npu *npu); ++ int (*ppe_flush_sram_entries)(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ int sram_num_entries); ++ int (*ppe_foe_commit_entry)(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ u32 entry_size, u32 hash, ++ bool ppe2); ++ int (*wlan_init_reserved_memory)(struct airoha_npu *npu); ++ int (*wlan_send_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_set_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ int (*wlan_get_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, ++ bool xmit); ++ void (*wlan_set_irq_status)(struct airoha_npu *npu, u32 val); ++ u32 (*wlan_get_irq_status)(struct airoha_npu *npu, int q); ++ void (*wlan_enable_irq)(struct airoha_npu *npu, int q); ++ void (*wlan_disable_irq)(struct airoha_npu *npu, int q); ++ } ops; ++#endif ++}; ++ ++#if (IS_BUILTIN(CONFIG_NET_AIROHA_NPU) || IS_MODULE(CONFIG_NET_AIROHA_NPU)) ++struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); ++void airoha_npu_put(struct airoha_npu *npu); ++ ++static inline int airoha_npu_wlan_init_reserved_memory(struct airoha_npu *npu) ++{ ++ return npu->ops.wlan_init_reserved_memory(npu); ++} ++ ++static inline int airoha_npu_wlan_send_msg(struct airoha_npu *npu, ++ int ifindex, ++ enum airoha_npu_wlan_set_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return npu->ops.wlan_send_msg(npu, ifindex, cmd, data, data_len, gfp); ++} ++ ++static inline int airoha_npu_wlan_get_msg(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return npu->ops.wlan_get_msg(npu, ifindex, cmd, data, data_len, gfp); ++} ++ ++static inline u32 airoha_npu_wlan_get_queue_addr(struct airoha_npu *npu, ++ int qid, bool xmit) ++{ ++ return npu->ops.wlan_get_queue_addr(npu, qid, xmit); ++} ++ ++static inline void airoha_npu_wlan_set_irq_status(struct airoha_npu *npu, ++ u32 val) ++{ ++ npu->ops.wlan_set_irq_status(npu, val); ++} ++ ++static inline u32 airoha_npu_wlan_get_irq_status(struct airoha_npu *npu, int q) ++{ ++ return npu->ops.wlan_get_irq_status(npu, q); ++} ++ ++static inline void airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q) ++{ ++ npu->ops.wlan_enable_irq(npu, q); ++} ++ ++static inline void airoha_npu_wlan_disable_irq(struct airoha_npu *npu, int q) ++{ ++ npu->ops.wlan_disable_irq(npu, q); ++} ++#else ++static inline struct airoha_npu *airoha_npu_get(struct device *dev, ++ dma_addr_t *foe_stats_addr) ++{ ++ return NULL; ++} ++ ++static inline void airoha_npu_put(struct airoha_npu *npu) ++{ ++} ++ ++static inline int airoha_npu_wlan_init_reserved_memory(struct airoha_npu *npu) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int airoha_npu_wlan_send_msg(struct airoha_npu *npu, ++ int ifindex, ++ enum airoha_npu_wlan_set_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int airoha_npu_wlan_get_msg(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline u32 airoha_npu_wlan_get_queue_addr(struct airoha_npu *npu, ++ int qid, bool xmit) ++{ ++ return 0; ++} ++ ++static inline void airoha_npu_wlan_set_irq_status(struct airoha_npu *npu, ++ u32 val) ++{ ++} ++ ++static inline u32 airoha_npu_wlan_get_irq_status(struct airoha_npu *npu, ++ int q) ++{ ++ return 0; ++} ++ ++static inline void airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q) ++{ ++} ++ ++static inline void airoha_npu_wlan_disable_irq(struct airoha_npu *npu, int q) ++{ ++} ++#endif ++ ++#endif /* AIROHA_OFFLOAD_H */ diff --git a/target/linux/airoha/patches-6.12/085-v6.18-net-airoha-Add-wlan-flowtable-TX-offload.patch b/target/linux/airoha/patches-6.12/085-v6.18-net-airoha-Add-wlan-flowtable-TX-offload.patch new file mode 100644 index 00000000000..ab9a3761a50 --- /dev/null +++ b/target/linux/airoha/patches-6.12/085-v6.18-net-airoha-Add-wlan-flowtable-TX-offload.patch @@ -0,0 +1,198 @@ +From a8bdd935d1ddb7186358fb60ffe84253e85340c8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 14 Aug 2025 09:51:16 +0200 +Subject: [PATCH] net: airoha: Add wlan flowtable TX offload + +Introduce support to offload the traffic received on the ethernet NIC +and forwarded to the wireless one using HW Packet Processor Engine (PPE) +capabilities. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250814-airoha-en7581-wlan-tx-offload-v1-1-72e0a312003e@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 11 +++ + drivers/net/ethernet/airoha/airoha_ppe.c | 103 ++++++++++++++++------- + 2 files changed, 85 insertions(+), 29 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -252,6 +252,10 @@ enum { + #define AIROHA_FOE_MAC_SMAC_ID GENMASK(20, 16) + #define AIROHA_FOE_MAC_PPPOE_ID GENMASK(15, 0) + ++#define AIROHA_FOE_MAC_WDMA_QOS GENMASK(15, 12) ++#define AIROHA_FOE_MAC_WDMA_BAND BIT(11) ++#define AIROHA_FOE_MAC_WDMA_WCID GENMASK(10, 0) ++ + struct airoha_foe_mac_info_common { + u16 vlan1; + u16 etype; +@@ -481,6 +485,13 @@ struct airoha_flow_table_entry { + unsigned long cookie; + }; + ++struct airoha_wdma_info { ++ u8 idx; ++ u8 queue; ++ u16 wcid; ++ u8 bss; ++}; ++ + /* RX queue to IRQ mapping: BIT(q) in IRQ(n) */ + #define RX_IRQ0_BANK_PIN_MASK 0x839f + #define RX_IRQ1_BANK_PIN_MASK 0x7fe00000 +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -190,6 +190,31 @@ static int airoha_ppe_flow_mangle_ipv4(c + return 0; + } + ++static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr, ++ struct airoha_wdma_info *info) ++{ ++ struct net_device_path_stack stack; ++ struct net_device_path *path; ++ int err; ++ ++ if (!dev) ++ return -ENODEV; ++ ++ err = dev_fill_forward_path(dev, addr, &stack); ++ if (err) ++ return err; ++ ++ path = &stack.path[stack.num_paths - 1]; ++ if (path->type != DEV_PATH_MTK_WDMA) ++ return -1; ++ ++ info->idx = path->mtk_wdma.wdma_idx; ++ info->bss = path->mtk_wdma.bss; ++ info->wcid = path->mtk_wdma.wcid; ++ ++ return 0; ++} ++ + static int airoha_get_dsa_port(struct net_device **dev) + { + #if IS_ENABLED(CONFIG_NET_DSA) +@@ -220,9 +245,9 @@ static int airoha_ppe_foe_entry_prepare( + struct airoha_flow_data *data, + int l4proto) + { +- int dsa_port = airoha_get_dsa_port(&dev); ++ u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val; ++ int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev); + struct airoha_foe_mac_info_common *l2; +- u32 qdata, ports_pad, val; + u8 smac_id = 0xf; + + memset(hwe, 0, sizeof(*hwe)); +@@ -236,31 +261,47 @@ static int airoha_ppe_foe_entry_prepare( + AIROHA_FOE_IB1_BIND_TTL; + hwe->ib1 = val; + +- val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f) | +- AIROHA_FOE_IB2_PSE_QOS; +- if (dsa_port >= 0) +- val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port); +- ++ val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f); + if (dev) { +- struct airoha_gdm_port *port = netdev_priv(dev); +- u8 pse_port; +- +- if (!airoha_is_valid_gdm_port(eth, port)) +- return -EINVAL; ++ struct airoha_wdma_info info = {}; + +- if (dsa_port >= 0) +- pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; +- else +- pse_port = 2; /* uplink relies on GDM2 loopback */ +- val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); +- +- /* For downlink traffic consume SRAM memory for hw forwarding +- * descriptors queue. +- */ +- if (airhoa_is_lan_gdm_port(port)) +- val |= AIROHA_FOE_IB2_FAST_PATH; ++ if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) { ++ val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) | ++ FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, ++ FE_PSE_PORT_CDM4); ++ qdata |= FIELD_PREP(AIROHA_FOE_ACTDP, info.bss); ++ wlan_etype = FIELD_PREP(AIROHA_FOE_MAC_WDMA_BAND, ++ info.idx) | ++ FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID, ++ info.wcid); ++ } else { ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u8 pse_port; ++ ++ if (!airoha_is_valid_gdm_port(eth, port)) ++ return -EINVAL; ++ ++ if (dsa_port >= 0) ++ pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 ++ : port->id; ++ else ++ pse_port = 2; /* uplink relies on GDM2 ++ * loopback ++ */ ++ ++ val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port) | ++ AIROHA_FOE_IB2_PSE_QOS; ++ /* For downlink traffic consume SRAM memory for hw ++ * forwarding descriptors queue. ++ */ ++ if (airhoa_is_lan_gdm_port(port)) ++ val |= AIROHA_FOE_IB2_FAST_PATH; ++ if (dsa_port >= 0) ++ val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, ++ dsa_port); + +- smac_id = port->id; ++ smac_id = port->id; ++ } + } + + if (is_multicast_ether_addr(data->eth.h_dest)) +@@ -272,7 +313,6 @@ static int airoha_ppe_foe_entry_prepare( + if (type == PPE_PKT_TYPE_IPV6_ROUTE_3T) + hwe->ipv6.ports = ports_pad; + +- qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); + if (type == PPE_PKT_TYPE_BRIDGE) { + airoha_ppe_foe_set_bridge_addrs(&hwe->bridge, &data->eth); + hwe->bridge.data = qdata; +@@ -313,7 +353,9 @@ static int airoha_ppe_foe_entry_prepare( + l2->vlan2 = data->vlan.hdr[1].id; + } + +- if (dsa_port >= 0) { ++ if (wlan_etype >= 0) { ++ l2->etype = wlan_etype; ++ } else if (dsa_port >= 0) { + l2->etype = BIT(dsa_port); + l2->etype |= !data->vlan.num ? BIT(15) : 0; + } else if (data->pppoe.num) { +@@ -490,6 +532,10 @@ static void airoha_ppe_foe_flow_stats_up + meter = &hwe->ipv4.l2.meter; + } + ++ pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); ++ if (pse_port == FE_PSE_PORT_CDM4) ++ return; ++ + airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index); + + val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data); +@@ -500,7 +546,6 @@ static void airoha_ppe_foe_flow_stats_up + AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH); + *meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val); + +- pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); + nbq = pse_port == 1 ? 6 : 5; + *ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | + AIROHA_FOE_IB2_PSE_QOS); diff --git a/target/linux/airoha/patches-6.12/086-01-v6.18-net-airoha-Rely-on-airoha_eth-struct-in-airoha_ppe_f.patch b/target/linux/airoha/patches-6.12/086-01-v6.18-net-airoha-Rely-on-airoha_eth-struct-in-airoha_ppe_f.patch new file mode 100644 index 00000000000..cef2922869d --- /dev/null +++ b/target/linux/airoha/patches-6.12/086-01-v6.18-net-airoha-Rely-on-airoha_eth-struct-in-airoha_ppe_f.patch @@ -0,0 +1,95 @@ +From 524a43c3a0c17fa0a1223eea36751dcba55e5530 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 23 Aug 2025 09:56:02 +0200 +Subject: [PATCH 1/3] net: airoha: Rely on airoha_eth struct in + airoha_ppe_flow_offload_cmd signature + +Rely on airoha_eth struct in airoha_ppe_flow_offload_cmd routine +signature and in all the called subroutines. +This is a preliminary patch to introduce flowtable offload for traffic +received by the wlan NIC and forwarded to the ethernet one. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250823-airoha-en7581-wlan-rx-offload-v3-1-f78600ec3ed8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 20 ++++++++------------ + 1 file changed, 8 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -935,11 +935,10 @@ static int airoha_ppe_entry_idle_time(st + return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); + } + +-static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth, + struct flow_cls_offload *f) + { + struct flow_rule *rule = flow_cls_offload_flow_rule(f); +- struct airoha_eth *eth = port->qdma->eth; + struct airoha_flow_table_entry *e; + struct airoha_flow_data data = {}; + struct net_device *odev = NULL; +@@ -1136,10 +1135,9 @@ free_entry: + return err; + } + +-static int airoha_ppe_flow_offload_destroy(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth, + struct flow_cls_offload *f) + { +- struct airoha_eth *eth = port->qdma->eth; + struct airoha_flow_table_entry *e; + + e = rhashtable_lookup(ð->flow_table, &f->cookie, +@@ -1182,10 +1180,9 @@ void airoha_ppe_foe_entry_get_stats(stru + rcu_read_unlock(); + } + +-static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_stats(struct airoha_eth *eth, + struct flow_cls_offload *f) + { +- struct airoha_eth *eth = port->qdma->eth; + struct airoha_flow_table_entry *e; + u32 idle; + +@@ -1209,16 +1206,16 @@ static int airoha_ppe_flow_offload_stats + return 0; + } + +-static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_cmd(struct airoha_eth *eth, + struct flow_cls_offload *f) + { + switch (f->command) { + case FLOW_CLS_REPLACE: +- return airoha_ppe_flow_offload_replace(port, f); ++ return airoha_ppe_flow_offload_replace(eth, f); + case FLOW_CLS_DESTROY: +- return airoha_ppe_flow_offload_destroy(port, f); ++ return airoha_ppe_flow_offload_destroy(eth, f); + case FLOW_CLS_STATS: +- return airoha_ppe_flow_offload_stats(port, f); ++ return airoha_ppe_flow_offload_stats(eth, f); + default: + break; + } +@@ -1288,7 +1285,6 @@ error_npu_put: + int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct flow_cls_offload *cls = type_data; + struct airoha_eth *eth = port->qdma->eth; + int err = 0; + +@@ -1297,7 +1293,7 @@ int airoha_ppe_setup_tc_block_cb(struct + if (!eth->npu) + err = airoha_ppe_offload_setup(eth); + if (!err) +- err = airoha_ppe_flow_offload_cmd(port, cls); ++ err = airoha_ppe_flow_offload_cmd(eth, type_data); + + mutex_unlock(&flow_offload_mutex); + diff --git a/target/linux/airoha/patches-6.12/086-02-v6.18-net-airoha-Add-airoha_ppe_dev-struct-definition.patch b/target/linux/airoha/patches-6.12/086-02-v6.18-net-airoha-Add-airoha_ppe_dev-struct-definition.patch new file mode 100644 index 00000000000..c10fc8bf351 --- /dev/null +++ b/target/linux/airoha/patches-6.12/086-02-v6.18-net-airoha-Add-airoha_ppe_dev-struct-definition.patch @@ -0,0 +1,223 @@ +From f45fc18b6de04483643e8aa2ab97737abfe03d59 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 23 Aug 2025 09:56:03 +0200 +Subject: [PATCH 2/3] net: airoha: Add airoha_ppe_dev struct definition + +Introduce airoha_ppe_dev struct as container for PPE offload callbacks +consumed by the MT76 driver during flowtable offload for traffic +received by the wlan NIC and forwarded to the wired one. +Add airoha_ppe_setup_tc_block_cb routine to PPE offload ops for MT76 +driver. +Rely on airoha_ppe_dev pointer in airoha_ppe_setup_tc_block_cb +signature. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250823-airoha-en7581-wlan-rx-offload-v3-2-f78600ec3ed8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 +- + drivers/net/ethernet/airoha/airoha_eth.h | 4 +- + drivers/net/ethernet/airoha/airoha_npu.c | 1 - + drivers/net/ethernet/airoha/airoha_ppe.c | 67 +++++++++++++++++++++-- + include/linux/soc/airoha/airoha_offload.h | 35 ++++++++++++ + 5 files changed, 104 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2622,13 +2622,15 @@ static int airoha_dev_setup_tc_block_cb( + void *type_data, void *cb_priv) + { + struct net_device *dev = cb_priv; ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; + + if (!tc_can_offload(dev)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: +- return airoha_ppe_setup_tc_block_cb(dev, type_data); ++ return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data); + case TC_SETUP_CLSMATCHALL: + return airoha_dev_tc_matchall(dev, type_data); + default: +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + + #define AIROHA_MAX_NUM_GDM_PORTS 4 +@@ -546,6 +547,7 @@ struct airoha_gdm_port { + #define AIROHA_RXD4_FOE_ENTRY GENMASK(15, 0) + + struct airoha_ppe { ++ struct airoha_ppe_dev dev; + struct airoha_eth *eth; + + void *foe; +@@ -622,7 +624,7 @@ bool airoha_is_valid_gdm_port(struct air + + void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, + u16 hash); +-int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); ++int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); + void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -11,7 +11,6 @@ + #include + #include + #include +-#include + + #include "airoha_eth.h" + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -6,8 +6,9 @@ + + #include + #include ++#include ++#include + #include +-#include + #include + #include + +@@ -1282,10 +1283,10 @@ error_npu_put: + return err; + } + +-int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) ++int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data) + { +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_ppe *ppe = dev->priv; ++ struct airoha_eth *eth = ppe->eth; + int err = 0; + + mutex_lock(&flow_offload_mutex); +@@ -1338,6 +1339,61 @@ void airoha_ppe_init_upd_mem(struct airo + PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); + } + ++struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev) ++{ ++ struct platform_device *pdev; ++ struct device_node *np; ++ struct airoha_eth *eth; ++ ++ np = of_parse_phandle(dev->of_node, "airoha,eth", 0); ++ if (!np) ++ return ERR_PTR(-ENODEV); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev) { ++ dev_err(dev, "cannot find device node %s\n", np->name); ++ of_node_put(np); ++ return ERR_PTR(-ENODEV); ++ } ++ of_node_put(np); ++ ++ if (!try_module_get(THIS_MODULE)) { ++ dev_err(dev, "failed to get the device driver module\n"); ++ goto error_pdev_put; ++ } ++ ++ eth = platform_get_drvdata(pdev); ++ if (!eth) ++ goto error_module_put; ++ ++ if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER)) { ++ dev_err(&pdev->dev, ++ "failed to create device link to consumer %s\n", ++ dev_name(dev)); ++ goto error_module_put; ++ } ++ ++ return ð->ppe->dev; ++ ++error_module_put: ++ module_put(THIS_MODULE); ++error_pdev_put: ++ platform_device_put(pdev); ++ ++ return ERR_PTR(-ENODEV); ++} ++EXPORT_SYMBOL_GPL(airoha_ppe_get_dev); ++ ++void airoha_ppe_put_dev(struct airoha_ppe_dev *dev) ++{ ++ struct airoha_ppe *ppe = dev->priv; ++ struct airoha_eth *eth = ppe->eth; ++ ++ module_put(THIS_MODULE); ++ put_device(eth->dev); ++} ++EXPORT_SYMBOL_GPL(airoha_ppe_put_dev); ++ + int airoha_ppe_init(struct airoha_eth *eth) + { + struct airoha_ppe *ppe; +@@ -1347,6 +1403,9 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe) + return -ENOMEM; + ++ ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb; ++ ppe->dev.priv = ppe; ++ + foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); + ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma, + GFP_KERNEL); +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -9,6 +9,41 @@ + #include + #include + ++struct airoha_ppe_dev { ++ struct { ++ int (*setup_tc_block_cb)(struct airoha_ppe_dev *dev, ++ void *type_data); ++ } ops; ++ ++ void *priv; ++}; ++ ++#if (IS_BUILTIN(CONFIG_NET_AIROHA) || IS_MODULE(CONFIG_NET_AIROHA)) ++struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev); ++void airoha_ppe_put_dev(struct airoha_ppe_dev *dev); ++ ++static inline int airoha_ppe_dev_setup_tc_block_cb(struct airoha_ppe_dev *dev, ++ void *type_data) ++{ ++ return dev->ops.setup_tc_block_cb(dev, type_data); ++} ++#else ++static inline struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev) ++{ ++ return NULL; ++} ++ ++static inline void airoha_ppe_put_dev(struct airoha_ppe_dev *dev) ++{ ++} ++ ++static inline int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, ++ void *type_data) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ + #define NPU_NUM_CORES 8 + #define NPU_NUM_IRQ 6 + #define NPU_RX0_DESC_NUM 512 diff --git a/target/linux/airoha/patches-6.12/086-03-v6.18-net-airoha-Introduce-check_skb-callback-in-ppe_dev-o.patch b/target/linux/airoha/patches-6.12/086-03-v6.18-net-airoha-Introduce-check_skb-callback-in-ppe_dev-o.patch new file mode 100644 index 00000000000..1edc2aa54c4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/086-03-v6.18-net-airoha-Introduce-check_skb-callback-in-ppe_dev-o.patch @@ -0,0 +1,207 @@ +From a7cc1aa151e3a9c0314b995f06102f7763d3bd71 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 23 Aug 2025 09:56:04 +0200 +Subject: [PATCH 3/3] net: airoha: Introduce check_skb callback in ppe_dev ops + +Export airoha_ppe_check_skb routine in ppe_dev ops. check_skb callback +will be used by the MT76 driver in order to offload the traffic received +by the wlan NIC and forwarded to the ethernet one. +Add rx_wlan parameter to airoha_ppe_check_skb routine signature. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250823-airoha-en7581-wlan-rx-offload-v3-3-f78600ec3ed8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 3 ++- + drivers/net/ethernet/airoha/airoha_eth.h | 8 ++------ + drivers/net/ethernet/airoha/airoha_ppe.c | 25 +++++++++++++---------- + include/linux/soc/airoha/airoha_offload.h | 20 ++++++++++++++++++ + 4 files changed, 38 insertions(+), 18 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -703,7 +703,8 @@ static int airoha_qdma_rx_process(struct + + reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) +- airoha_ppe_check_skb(eth->ppe, q->skb, hash); ++ airoha_ppe_check_skb(ð->ppe->dev, q->skb, hash, ++ false); + + done++; + napi_gro_receive(&q->napi, q->skb); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -230,10 +230,6 @@ struct airoha_hw_stats { + }; + + enum { +- PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f, +-}; +- +-enum { + AIROHA_FOE_STATE_INVALID, + AIROHA_FOE_STATE_UNBIND, + AIROHA_FOE_STATE_BIND, +@@ -622,8 +618,8 @@ static inline bool airhoa_is_lan_gdm_por + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, +- u16 hash); ++void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, ++ u16 hash, bool rx_wlan); + int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -616,7 +616,7 @@ static bool airoha_ppe_foe_compare_entry + + static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe, + struct airoha_foe_entry *e, +- u32 hash) ++ u32 hash, bool rx_wlan) + { + struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); + u32 ts = airoha_ppe_get_timestamp(ppe); +@@ -639,7 +639,8 @@ static int airoha_ppe_foe_commit_entry(s + goto unlock; + } + +- airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); ++ if (!rx_wlan) ++ airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); + + if (hash < PPE_SRAM_NUM_ENTRIES) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); +@@ -665,7 +666,7 @@ static void airoha_ppe_foe_remove_flow(s + e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; + e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, + AIROHA_FOE_STATE_INVALID); +- airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash, false); + e->hash = 0xffff; + } + if (e->type == FLOW_TYPE_L2_SUBFLOW) { +@@ -704,7 +705,7 @@ static void airoha_ppe_foe_flow_remove_e + static int + airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e, +- u32 hash) ++ u32 hash, bool rx_wlan) + { + u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; + struct airoha_foe_entry *hwe_p, hwe; +@@ -745,14 +746,14 @@ airoha_ppe_foe_commit_subflow_entry(stru + } + + hwe.bridge.data = e->data.bridge.data; +- airoha_ppe_foe_commit_entry(ppe, &hwe, hash); ++ airoha_ppe_foe_commit_entry(ppe, &hwe, hash, rx_wlan); + + return 0; + } + + static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, + struct sk_buff *skb, +- u32 hash) ++ u32 hash, bool rx_wlan) + { + struct airoha_flow_table_entry *e; + struct airoha_foe_bridge br = {}; +@@ -785,7 +786,7 @@ static void airoha_ppe_foe_insert_entry( + if (!airoha_ppe_foe_compare_entry(e, hwe)) + continue; + +- airoha_ppe_foe_commit_entry(ppe, &e->data, hash); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, hash, rx_wlan); + commit_done = true; + e->hash = hash; + } +@@ -797,7 +798,7 @@ static void airoha_ppe_foe_insert_entry( + e = rhashtable_lookup_fast(&ppe->l2_flows, &br, + airoha_l2_flow_table_params); + if (e) +- airoha_ppe_foe_commit_subflow_entry(ppe, e, hash); ++ airoha_ppe_foe_commit_subflow_entry(ppe, e, hash, rx_wlan); + unlock: + spin_unlock_bh(&ppe_lock); + } +@@ -1301,9 +1302,10 @@ int airoha_ppe_setup_tc_block_cb(struct + return err; + } + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, +- u16 hash) ++void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, ++ u16 hash, bool rx_wlan) + { ++ struct airoha_ppe *ppe = dev->priv; + u16 now, diff; + + if (hash > PPE_HASH_MASK) +@@ -1315,7 +1317,7 @@ void airoha_ppe_check_skb(struct airoha_ + return; + + ppe->foe_check_time[hash] = now; +- airoha_ppe_foe_insert_entry(ppe, skb, hash); ++ airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan); + } + + void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) +@@ -1404,6 +1406,7 @@ int airoha_ppe_init(struct airoha_eth *e + return -ENOMEM; + + ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb; ++ ppe->dev.ops.check_skb = airoha_ppe_check_skb; + ppe->dev.priv = ppe; + + foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -9,10 +9,17 @@ + #include + #include + ++enum { ++ PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f, ++}; ++ + struct airoha_ppe_dev { + struct { + int (*setup_tc_block_cb)(struct airoha_ppe_dev *dev, + void *type_data); ++ void (*check_skb)(struct airoha_ppe_dev *dev, ++ struct sk_buff *skb, u16 hash, ++ bool rx_wlan); + } ops; + + void *priv; +@@ -27,6 +34,13 @@ static inline int airoha_ppe_dev_setup_t + { + return dev->ops.setup_tc_block_cb(dev, type_data); + } ++ ++static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, ++ struct sk_buff *skb, ++ u16 hash, bool rx_wlan) ++{ ++ dev->ops.check_skb(dev, skb, hash, rx_wlan); ++} + #else + static inline struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev) + { +@@ -42,6 +56,12 @@ static inline int airoha_ppe_setup_tc_bl + { + return -EOPNOTSUPP; + } ++ ++static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, ++ struct sk_buff *skb, u16 hash, ++ bool rx_wlan) ++{ ++} + #endif + + #define NPU_NUM_CORES 8 diff --git a/target/linux/airoha/patches-6.12/087-v6.17-pinctrl-airoha-Fix-return-value-in-pinconf-callbacks.patch b/target/linux/airoha/patches-6.12/087-v6.17-pinctrl-airoha-Fix-return-value-in-pinconf-callbacks.patch new file mode 100644 index 00000000000..62390f8a6ca --- /dev/null +++ b/target/linux/airoha/patches-6.12/087-v6.17-pinctrl-airoha-Fix-return-value-in-pinconf-callbacks.patch @@ -0,0 +1,50 @@ +From 563fcd6475931c5c8c652a4dd548256314cc87ed Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Aug 2025 14:14:18 +0200 +Subject: [PATCH] pinctrl: airoha: Fix return value in pinconf callbacks + +Pinctrl stack requires ENOTSUPP error code if the parameter is not +supported by the pinctrl driver. Fix the returned error code in pinconf +callbacks if the operation is not supported. + +Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/20250822-airoha-pinconf-err-val-fix-v1-1-87b4f264ced2@kernel.org +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -2697,7 +2697,7 @@ static int airoha_pinconf_get(struct pin + arg = 1; + break; + default: +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); +@@ -2791,7 +2791,7 @@ static int airoha_pinconf_set(struct pin + break; + } + default: +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + } + } + +@@ -2808,10 +2808,10 @@ static int airoha_pinconf_group_get(stru + if (airoha_pinconf_get(pctrl_dev, + airoha_pinctrl_groups[group].pins[i], + config)) +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + + if (i && cur_config != *config) +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + + cur_config = *config; + } diff --git a/target/linux/airoha/patches-6.12/088-v6.18-pinctrl-airoha-replace-struct-function_desc-with-str.patch b/target/linux/airoha/patches-6.12/088-v6.18-pinctrl-airoha-replace-struct-function_desc-with-str.patch new file mode 100644 index 00000000000..6acc9106090 --- /dev/null +++ b/target/linux/airoha/patches-6.12/088-v6.18-pinctrl-airoha-replace-struct-function_desc-with-str.patch @@ -0,0 +1,66 @@ +From 17d4f2a9e6cb224012d85fed52e9794a84fa501d Mon Sep 17 00:00:00 2001 +From: Bartosz Golaszewski +Date: Tue, 2 Sep 2025 13:59:13 +0200 +Subject: [PATCH 1/1] pinctrl: airoha: replace struct function_desc with struct + pinfunction + +struct function_desc is a wrapper around struct pinfunction with an +additional void *data pointer. This driver doesn't use the data pointer. +We're also working towards reducing the usage of struct function_desc in +pinctrl drivers - they should only be created by pinmux core and +accessed by drivers using pinmux_generic_get_function(). Replace the +struct function_desc objects in this driver with smaller struct +pinfunction instances. + +Tested-by: Neil Armstrong +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 15 +++++---------- + 1 file changed, 5 insertions(+), 10 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -35,13 +35,8 @@ + + #define PINCTRL_FUNC_DESC(id) \ + { \ +- .desc = { \ +- .func = { \ +- .name = #id, \ +- .groups = id##_groups, \ +- .ngroups = ARRAY_SIZE(id##_groups), \ +- } \ +- }, \ ++ .desc = PINCTRL_PINFUNCTION(#id, id##_groups, \ ++ ARRAY_SIZE(id##_groups)), \ + .groups = id##_func_group, \ + .group_size = ARRAY_SIZE(id##_func_group), \ + } +@@ -334,7 +329,7 @@ struct airoha_pinctrl_func_group { + }; + + struct airoha_pinctrl_func { +- const struct function_desc desc; ++ const struct pinfunction desc; + const struct airoha_pinctrl_func_group *groups; + u8 group_size; + }; +@@ -2911,13 +2906,13 @@ static int airoha_pinctrl_probe(struct p + + func = &airoha_pinctrl_funcs[i]; + err = pinmux_generic_add_function(pinctrl->ctrl, +- func->desc.func.name, +- func->desc.func.groups, +- func->desc.func.ngroups, ++ func->desc.name, ++ func->desc.groups, ++ func->desc.ngroups, + (void *)func); + if (err < 0) { + dev_err(dev, "Failed to register function %s\n", +- func->desc.func.name); ++ func->desc.name); + return err; + } + } diff --git a/target/linux/airoha/patches-6.12/089-v6.14-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch b/target/linux/airoha/patches-6.12/089-v6.14-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch new file mode 100644 index 00000000000..4f322ec9341 --- /dev/null +++ b/target/linux/airoha/patches-6.12/089-v6.14-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch @@ -0,0 +1,36 @@ +From 7d0da8f862340c5f42f0062b8560b8d0971a6ac4 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Jan 2025 23:26:28 +0100 +Subject: [PATCH] net: airoha: Fix channel configuration for ETS Qdisc + +Limit ETS QoS channel to AIROHA_NUM_QOS_CHANNELS in +airoha_tc_setup_qdisc_ets() in order to align the configured channel to +the value set in airoha_dev_select_queue(). + +Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Michal Swiatkowski +Link: https://patch.msgid.link/20250107-airoha-ets-fix-chan-v1-1-97f66ed3a068@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2204,11 +2204,14 @@ static int airoha_qdma_get_tx_ets_stats( + static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, + struct tc_ets_qopt_offload *opt) + { +- int channel = TC_H_MAJ(opt->handle) >> 16; ++ int channel; + + if (opt->parent == TC_H_ROOT) + return -EINVAL; + ++ channel = TC_H_MAJ(opt->handle) >> 16; ++ channel = channel % AIROHA_NUM_QOS_CHANNELS; ++ + switch (opt->command) { + case TC_ETS_REPLACE: + return airoha_qdma_set_tx_ets_sched(port, channel, opt); diff --git a/target/linux/airoha/patches-6.12/090-v6.17-net-mdio-Add-MDIO-bus-controller-for-Airoha-AN7583.patch b/target/linux/airoha/patches-6.12/090-v6.17-net-mdio-Add-MDIO-bus-controller-for-Airoha-AN7583.patch new file mode 100644 index 00000000000..37b0ed5c78a --- /dev/null +++ b/target/linux/airoha/patches-6.12/090-v6.17-net-mdio-Add-MDIO-bus-controller-for-Airoha-AN7583.patch @@ -0,0 +1,342 @@ +From 67e3ba978361cb262f8f8981ab88ccb97f1e2bda Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 17 Jun 2025 11:16:53 +0200 +Subject: [PATCH] net: mdio: Add MDIO bus controller for Airoha AN7583 + +Airoha AN7583 SoC have 2 dedicated MDIO bus controller in the SCU +register map. To driver register an MDIO controller based on the DT +reg property and access the register by accessing the parent syscon. + +The MDIO bus logic is similar to the MT7530 internal MDIO bus but +deviates of some setting and some HW bug. + +On Airoha AN7583 the MDIO clock is set to 25MHz by default and needs to +be correctly setup to 2.5MHz to correctly work (by setting the divisor +to 10x). + +There seems to be Hardware bug where AN7583_MII_RWDATA +is not wiped in the context of unconnected PHY and the +previous read value is returned. + +Example: (only one PHY on the BUS at 0x1f) + - read at 0x1f report at 0x2 0x7500 + - read at 0x0 report 0x7500 on every address + +To workaround this, we reset the Mdio BUS at every read +to have consistent values on read operation. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/mdio/Kconfig | 7 + + drivers/net/mdio/Makefile | 1 + + drivers/net/mdio/mdio-airoha.c | 276 +++++++++++++++++++++++++++++++++ + 3 files changed, 284 insertions(+) + create mode 100644 drivers/net/mdio/mdio-airoha.c + +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -46,6 +46,13 @@ if MDIO_BUS + config MDIO_DEVRES + tristate + ++config MDIO_AIROHA ++ tristate "Airoha AN7583 MDIO bus controller" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ help ++ This module provides a driver for the MDIO busses found in the ++ Airoha AN7583 SoC's. ++ + config MDIO_SUN4I + tristate "Allwinner sun4i MDIO interface support" + depends on ARCH_SUNXI || COMPILE_TEST +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -5,6 +5,7 @@ obj-$(CONFIG_ACPI_MDIO) += acpi_mdio.o + obj-$(CONFIG_FWNODE_MDIO) += fwnode_mdio.o + obj-$(CONFIG_OF_MDIO) += of_mdio.o + ++obj-$(CONFIG_MDIO_AIROHA) += mdio-airoha.o + obj-$(CONFIG_MDIO_ASPEED) += mdio-aspeed.o + obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o + obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o +--- /dev/null ++++ b/drivers/net/mdio/mdio-airoha.c +@@ -0,0 +1,276 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Airoha AN7583 MDIO interface driver ++ * ++ * Copyright (C) 2025 Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* MII address register definitions */ ++#define AN7583_MII_BUSY BIT(31) ++#define AN7583_MII_RDY BIT(30) /* RO signal BUS is ready */ ++#define AN7583_MII_CL22_REG_ADDR GENMASK(29, 25) ++#define AN7583_MII_CL45_DEV_ADDR AN7583_MII_CL22_REG_ADDR ++#define AN7583_MII_PHY_ADDR GENMASK(24, 20) ++#define AN7583_MII_CMD GENMASK(19, 18) ++#define AN7583_MII_CMD_CL22_WRITE FIELD_PREP_CONST(AN7583_MII_CMD, 0x1) ++#define AN7583_MII_CMD_CL22_READ FIELD_PREP_CONST(AN7583_MII_CMD, 0x2) ++#define AN7583_MII_CMD_CL45_ADDR FIELD_PREP_CONST(AN7583_MII_CMD, 0x0) ++#define AN7583_MII_CMD_CL45_WRITE FIELD_PREP_CONST(AN7583_MII_CMD, 0x1) ++#define AN7583_MII_CMD_CL45_POSTREAD_INCADDR FIELD_PREP_CONST(AN7583_MII_CMD, 0x2) ++#define AN7583_MII_CMD_CL45_READ FIELD_PREP_CONST(AN7583_MII_CMD, 0x3) ++#define AN7583_MII_ST GENMASK(17, 16) ++#define AN7583_MII_ST_CL45 FIELD_PREP_CONST(AN7583_MII_ST, 0x0) ++#define AN7583_MII_ST_CL22 FIELD_PREP_CONST(AN7583_MII_ST, 0x1) ++#define AN7583_MII_RWDATA GENMASK(15, 0) ++#define AN7583_MII_CL45_REG_ADDR AN7583_MII_RWDATA ++ ++#define AN7583_MII_MDIO_DELAY_USEC 100 ++#define AN7583_MII_MDIO_RETRY_MSEC 100 ++ ++struct airoha_mdio_data { ++ u32 base_addr; ++ struct regmap *regmap; ++ struct clk *clk; ++ struct reset_control *reset; ++}; ++ ++static int airoha_mdio_wait_busy(struct airoha_mdio_data *priv) ++{ ++ u32 busy; ++ ++ return regmap_read_poll_timeout(priv->regmap, priv->base_addr, busy, ++ !(busy & AN7583_MII_BUSY), ++ AN7583_MII_MDIO_DELAY_USEC, ++ AN7583_MII_MDIO_RETRY_MSEC * USEC_PER_MSEC); ++} ++ ++static void airoha_mdio_reset(struct airoha_mdio_data *priv) ++{ ++ /* There seems to be Hardware bug where AN7583_MII_RWDATA ++ * is not wiped in the context of unconnected PHY and the ++ * previous read value is returned. ++ * ++ * Example: (only one PHY on the BUS at 0x1f) ++ * - read at 0x1f report at 0x2 0x7500 ++ * - read at 0x0 report 0x7500 on every address ++ * ++ * To workaround this, we reset the Mdio BUS at every read ++ * to have consistent values on read operation. ++ */ ++ reset_control_assert(priv->reset); ++ reset_control_deassert(priv->reset); ++} ++ ++static int airoha_mdio_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct airoha_mdio_data *priv = bus->priv; ++ u32 val; ++ int ret; ++ ++ airoha_mdio_reset(priv); ++ ++ val = AN7583_MII_BUSY | AN7583_MII_ST_CL22 | ++ AN7583_MII_CMD_CL22_READ; ++ val |= FIELD_PREP(AN7583_MII_PHY_ADDR, addr); ++ val |= FIELD_PREP(AN7583_MII_CL22_REG_ADDR, regnum); ++ ++ ret = regmap_write(priv->regmap, priv->base_addr, val); ++ if (ret) ++ return ret; ++ ++ ret = airoha_mdio_wait_busy(priv); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(priv->regmap, priv->base_addr, &val); ++ if (ret) ++ return ret; ++ ++ return FIELD_GET(AN7583_MII_RWDATA, val); ++} ++ ++static int airoha_mdio_write(struct mii_bus *bus, int addr, int regnum, ++ u16 value) ++{ ++ struct airoha_mdio_data *priv = bus->priv; ++ u32 val; ++ int ret; ++ ++ val = AN7583_MII_BUSY | AN7583_MII_ST_CL22 | ++ AN7583_MII_CMD_CL22_WRITE; ++ val |= FIELD_PREP(AN7583_MII_PHY_ADDR, addr); ++ val |= FIELD_PREP(AN7583_MII_CL22_REG_ADDR, regnum); ++ val |= FIELD_PREP(AN7583_MII_RWDATA, value); ++ ++ ret = regmap_write(priv->regmap, priv->base_addr, val); ++ if (ret) ++ return ret; ++ ++ ret = airoha_mdio_wait_busy(priv); ++ ++ return ret; ++} ++ ++static int airoha_mdio_cl45_read(struct mii_bus *bus, int addr, int devnum, ++ int regnum) ++{ ++ struct airoha_mdio_data *priv = bus->priv; ++ u32 val; ++ int ret; ++ ++ airoha_mdio_reset(priv); ++ ++ val = AN7583_MII_BUSY | AN7583_MII_ST_CL45 | ++ AN7583_MII_CMD_CL45_ADDR; ++ val |= FIELD_PREP(AN7583_MII_PHY_ADDR, addr); ++ val |= FIELD_PREP(AN7583_MII_CL45_DEV_ADDR, devnum); ++ val |= FIELD_PREP(AN7583_MII_CL45_REG_ADDR, regnum); ++ ++ ret = regmap_write(priv->regmap, priv->base_addr, val); ++ if (ret) ++ return ret; ++ ++ ret = airoha_mdio_wait_busy(priv); ++ if (ret) ++ return ret; ++ ++ val = AN7583_MII_BUSY | AN7583_MII_ST_CL45 | ++ AN7583_MII_CMD_CL45_READ; ++ val |= FIELD_PREP(AN7583_MII_PHY_ADDR, addr); ++ val |= FIELD_PREP(AN7583_MII_CL45_DEV_ADDR, devnum); ++ ++ ret = regmap_write(priv->regmap, priv->base_addr, val); ++ if (ret) ++ return ret; ++ ++ ret = airoha_mdio_wait_busy(priv); ++ if (ret) ++ return ret; ++ ++ ret = regmap_read(priv->regmap, priv->base_addr, &val); ++ if (ret) ++ return ret; ++ ++ return FIELD_GET(AN7583_MII_RWDATA, val); ++} ++ ++static int airoha_mdio_cl45_write(struct mii_bus *bus, int addr, int devnum, ++ int regnum, u16 value) ++{ ++ struct airoha_mdio_data *priv = bus->priv; ++ u32 val; ++ int ret; ++ ++ val = AN7583_MII_BUSY | AN7583_MII_ST_CL45 | ++ AN7583_MII_CMD_CL45_ADDR; ++ val |= FIELD_PREP(AN7583_MII_PHY_ADDR, addr); ++ val |= FIELD_PREP(AN7583_MII_CL45_DEV_ADDR, devnum); ++ val |= FIELD_PREP(AN7583_MII_CL45_REG_ADDR, regnum); ++ ++ ret = regmap_write(priv->regmap, priv->base_addr, val); ++ if (ret) ++ return ret; ++ ++ ret = airoha_mdio_wait_busy(priv); ++ if (ret) ++ return ret; ++ ++ val = AN7583_MII_BUSY | AN7583_MII_ST_CL45 | ++ AN7583_MII_CMD_CL45_WRITE; ++ val |= FIELD_PREP(AN7583_MII_PHY_ADDR, addr); ++ val |= FIELD_PREP(AN7583_MII_CL45_DEV_ADDR, devnum); ++ val |= FIELD_PREP(AN7583_MII_RWDATA, value); ++ ++ ret = regmap_write(priv->regmap, priv->base_addr, val); ++ if (ret) ++ return ret; ++ ++ ret = airoha_mdio_wait_busy(priv); ++ ++ return ret; ++} ++ ++static int airoha_mdio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_mdio_data *priv; ++ struct mii_bus *bus; ++ u32 addr, freq; ++ int ret; ++ ++ ret = of_property_read_u32(dev->of_node, "reg", &addr); ++ if (ret) ++ return ret; ++ ++ bus = devm_mdiobus_alloc_size(dev, sizeof(*priv)); ++ if (!bus) ++ return -ENOMEM; ++ ++ priv = bus->priv; ++ priv->base_addr = addr; ++ priv->regmap = device_node_to_regmap(dev->parent->of_node); ++ ++ priv->clk = devm_clk_get_enabled(dev, NULL); ++ if (IS_ERR(priv->clk)) ++ return PTR_ERR(priv->clk); ++ ++ priv->reset = devm_reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(priv->reset)) ++ return PTR_ERR(priv->reset); ++ ++ reset_control_deassert(priv->reset); ++ ++ bus->name = "airoha_mdio_bus"; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); ++ bus->parent = dev; ++ bus->read = airoha_mdio_read; ++ bus->write = airoha_mdio_write; ++ bus->read_c45 = airoha_mdio_cl45_read; ++ bus->write_c45 = airoha_mdio_cl45_write; ++ ++ /* Check if a custom frequency is defined in DT or default to 2.5 MHz */ ++ if (of_property_read_u32(dev->of_node, "clock-frequency", &freq)) ++ freq = 2500000; ++ ++ ret = clk_set_rate(priv->clk, freq); ++ if (ret) ++ return ret; ++ ++ ret = devm_of_mdiobus_register(dev, bus, dev->of_node); ++ if (ret) { ++ reset_control_assert(priv->reset); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_mdio_dt_ids[] = { ++ { .compatible = "airoha,an7583-mdio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, airoha_mdio_dt_ids); ++ ++static struct platform_driver airoha_mdio_driver = { ++ .probe = airoha_mdio_probe, ++ .driver = { ++ .name = "airoha-mdio", ++ .of_match_table = airoha_mdio_dt_ids, ++ }, ++}; ++ ++module_platform_driver(airoha_mdio_driver); ++ ++MODULE_DESCRIPTION("Airoha AN7583 MDIO interface driver"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/091-01-v6.18-pinctrl-airoha-fix-wrong-PHY-LED-mux-value-for-LED1-.patch b/target/linux/airoha/patches-6.12/091-01-v6.18-pinctrl-airoha-fix-wrong-PHY-LED-mux-value-for-LED1-.patch new file mode 100644 index 00000000000..cf16391b7be --- /dev/null +++ b/target/linux/airoha/patches-6.12/091-01-v6.18-pinctrl-airoha-fix-wrong-PHY-LED-mux-value-for-LED1-.patch @@ -0,0 +1,68 @@ +From af87d38c442c75a40c7d0a7d8c31557e2e6ccf98 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 25 May 2025 20:22:40 +0200 +Subject: [PATCH 1/2] pinctrl: airoha: fix wrong PHY LED mux value for LED1 + GPIO46 + +In all the MUX value for LED1 GPIO46 there is a Copy-Paste error where +the MUX value is set to LED0_MODE_MASK instead of LED1_MODE_MASK. + +This wasn't notice as there were no board that made use of the +secondary PHY LED but looking at the internal Documentation the actual +value should be LED1_MODE_MASK similar to the other GPIO entry. + +Fix the wrong value to apply the correct MUX configuration. + +Cc: stable@vger.kernel.org +Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") +Signed-off-by: Christian Marangi +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -1747,8 +1747,8 @@ static const struct airoha_pinctrl_func_ + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK ++ GPIO_LAN3_LED1_MODE_MASK, ++ GPIO_LAN3_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, +@@ -1811,8 +1811,8 @@ static const struct airoha_pinctrl_func_ + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK ++ GPIO_LAN3_LED1_MODE_MASK, ++ GPIO_LAN3_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, +@@ -1875,8 +1875,8 @@ static const struct airoha_pinctrl_func_ + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK ++ GPIO_LAN3_LED1_MODE_MASK, ++ GPIO_LAN3_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, +@@ -1939,8 +1939,8 @@ static const struct airoha_pinctrl_func_ + .regmap[0] = { + AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK ++ GPIO_LAN3_LED1_MODE_MASK, ++ GPIO_LAN3_LED1_MODE_MASK + }, + .regmap[1] = { + AIROHA_FUNC_MUX, diff --git a/target/linux/airoha/patches-6.12/091-02-v6.18-pinctrl-airoha-fix-wrong-MDIO-function-bitmaks.patch b/target/linux/airoha/patches-6.12/091-02-v6.18-pinctrl-airoha-fix-wrong-MDIO-function-bitmaks.patch new file mode 100644 index 00000000000..0f0a5ebe0f7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/091-02-v6.18-pinctrl-airoha-fix-wrong-MDIO-function-bitmaks.patch @@ -0,0 +1,58 @@ +From 110930eb12699b92f767fc599c7ab467dd42358a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 8 Jul 2025 14:49:56 +0200 +Subject: [PATCH 2/2] pinctrl: airoha: fix wrong MDIO function bitmaks + +With further testing with an attached Aeonsemi it was discovered that +the pinctrl MDIO function applied the wrong bitmask. The error was +probably caused by the confusing documentation related to these bits. + +Inspecting what the bootloader actually configure, the SGMII_MDIO_MODE +is never actually set but instead it's set force enable to the 2 GPIO +(gpio 1-2) for MDC and MDIO pin. + +Applying this configuration permits correct functionality of any +externally attached PHY. + +Cc: stable@vger.kernel.org +Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") +Signed-off-by: Christian Marangi +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -103,6 +103,9 @@ + #define JTAG_UDI_EN_MASK BIT(4) + #define JTAG_DFD_EN_MASK BIT(3) + ++#define REG_FORCE_GPIO_EN 0x0228 ++#define FORCE_GPIO_EN(n) BIT(n) ++ + /* LED MAP */ + #define REG_LAN_LED0_MAPPING 0x027c + #define REG_LAN_LED1_MAPPING 0x0280 +@@ -714,16 +717,16 @@ static const struct airoha_pinctrl_func_ + .name = "mdio", + .regmap[0] = { + AIROHA_FUNC_MUX, +- REG_GPIO_PON_MODE, +- GPIO_SGMII_MDIO_MODE_MASK, +- GPIO_SGMII_MDIO_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, + REG_GPIO_2ND_I2C_MODE, + GPIO_MDC_IO_MASTER_MODE_MODE, + GPIO_MDC_IO_MASTER_MODE_MODE + }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_FORCE_GPIO_EN, ++ FORCE_GPIO_EN(1) | FORCE_GPIO_EN(2), ++ FORCE_GPIO_EN(1) | FORCE_GPIO_EN(2) ++ }, + .regmap_size = 2, + }, + }; diff --git a/target/linux/airoha/patches-6.12/092-v6.18-net-airoha-Avoid-Wflex-array-member-not-at-end-warni.patch b/target/linux/airoha/patches-6.12/092-v6.18-net-airoha-Avoid-Wflex-array-member-not-at-end-warni.patch new file mode 100644 index 00000000000..5e52a3b429e --- /dev/null +++ b/target/linux/airoha/patches-6.12/092-v6.18-net-airoha-Avoid-Wflex-array-member-not-at-end-warni.patch @@ -0,0 +1,45 @@ +From 09630ab91d840416b0178f3660afa4eebce24286 Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Mon, 22 Sep 2025 16:08:21 +0200 +Subject: [PATCH] net: airoha: Avoid -Wflex-array-member-not-at-end warning + +-Wflex-array-member-not-at-end was introduced in GCC-14, and we are +getting ready to enable it, globally. + +Move the conflicting declaration to the end of the corresponding +structure. Notice that `struct airoha_foe_entry` is a flexible +structure, this is a structure that contains a flexible-array +member. + +Fix the following warning: + +drivers/net/ethernet/airoha/airoha_eth.h:474:33: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] + +Signed-off-by: Gustavo A. R. Silva +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/aNFYVYLXQDqm4yxb@kspp +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.h | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -471,7 +471,6 @@ struct airoha_flow_table_entry { + }; + }; + +- struct airoha_foe_entry data; + struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ + u32 hash; + +@@ -480,6 +479,9 @@ struct airoha_flow_table_entry { + + struct rhash_head node; + unsigned long cookie; ++ ++ /* Must be last --ends in a flexible-array member. */ ++ struct airoha_foe_entry data; + }; + + struct airoha_wdma_info { diff --git a/target/linux/airoha/patches-6.12/093-v6.18-net-airoha-Fix-PPE_IP_PROTO_CHK-register-definitions.patch b/target/linux/airoha/patches-6.12/093-v6.18-net-airoha-Fix-PPE_IP_PROTO_CHK-register-definitions.patch new file mode 100644 index 00000000000..31cd950437e --- /dev/null +++ b/target/linux/airoha/patches-6.12/093-v6.18-net-airoha-Fix-PPE_IP_PROTO_CHK-register-definitions.patch @@ -0,0 +1,29 @@ +From e156dd6b856fa462430d875b0d4cd281ecd66c23 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 18 Sep 2025 08:59:41 +0200 +Subject: [PATCH] net: airoha: Fix PPE_IP_PROTO_CHK register definitions + +Fix typo in PPE_IP_PROTO_CHK_IPV4_MASK and PPE_IP_PROTO_CHK_IPV6_MASK +register mask definitions. This is not a real problem since this +register is not actually used in the current codebase. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_regs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -237,8 +237,8 @@ + #define PPE_FLOW_CFG_IP4_TCP_FRAG_MASK BIT(6) + + #define REG_PPE_IP_PROTO_CHK(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x208) +-#define PPE_IP_PROTO_CHK_IPV4_MASK GENMASK(15, 0) +-#define PPE_IP_PROTO_CHK_IPV6_MASK GENMASK(31, 16) ++#define PPE_IP_PROTO_CHK_IPV4_MASK GENMASK(31, 16) ++#define PPE_IP_PROTO_CHK_IPV6_MASK GENMASK(15, 0) + + #define REG_PPE_TB_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x21c) + #define PPE_SRAM_TB_NUM_ENTRY_MASK GENMASK(26, 24) diff --git a/target/linux/airoha/patches-6.12/094-v6.18-net-airoha-npu-Add-a-NPU-callback-to-initialize-flow.patch b/target/linux/airoha/patches-6.12/094-v6.18-net-airoha-npu-Add-a-NPU-callback-to-initialize-flow.patch new file mode 100644 index 00000000000..021ec0c8df5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/094-v6.18-net-airoha-npu-Add-a-NPU-callback-to-initialize-flow.patch @@ -0,0 +1,159 @@ +From 105ce7ad57e492b75ab40f2dc591db645fadbaa2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 24 Sep 2025 23:14:53 +0200 +Subject: [PATCH] net: airoha: npu: Add a NPU callback to initialize flow stats + +Introduce a NPU callback to initialize flow stats and remove NPU stats +initialization from airoha_npu_get routine. Add num_stats_entries to +airoha_npu_ppe_stats_setup routine. +This patch makes the code more readable since NPU statistic are now +initialized on demand by the NPU consumer (at the moment NPU statistic +are configured just by the airoha_eth driver). +Moreover this patch allows the NPU consumer (PPE module) to explicitly +enable/disable NPU flow stats. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250924-airoha-npu-init-stats-callback-v1-1-88bdf3c941b2@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 24 ++++++----------------- + drivers/net/ethernet/airoha/airoha_ppe.c | 19 ++++++++++++------ + include/linux/soc/airoha/airoha_offload.h | 7 ++++--- + 3 files changed, 23 insertions(+), 27 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -379,15 +379,13 @@ out: + return err; + } + +-static int airoha_npu_stats_setup(struct airoha_npu *npu, +- dma_addr_t foe_stats_addr) ++static int airoha_npu_ppe_stats_setup(struct airoha_npu *npu, ++ dma_addr_t foe_stats_addr, ++ u32 num_stats_entries) + { +- int err, size = PPE_STATS_NUM_ENTRIES * sizeof(*npu->stats); ++ int err, size = num_stats_entries * sizeof(*npu->stats); + struct ppe_mbox_data *ppe_data; + +- if (!size) /* flow stats are disabled */ +- return 0; +- + ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); + if (!ppe_data) + return -ENOMEM; +@@ -542,7 +540,7 @@ static void airoha_npu_wlan_irq_disable( + regmap_clear_bits(npu->regmap, REG_IRQ_RXDONE(q), NPU_IRQ_RX_MASK(q)); + } + +-struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) ++struct airoha_npu *airoha_npu_get(struct device *dev) + { + struct platform_device *pdev; + struct device_node *np; +@@ -580,17 +578,6 @@ struct airoha_npu *airoha_npu_get(struct + goto error_module_put; + } + +- if (stats_addr) { +- int err; +- +- err = airoha_npu_stats_setup(npu, *stats_addr); +- if (err) { +- dev_err(dev, "failed to allocate npu stats buffer\n"); +- npu = ERR_PTR(err); +- goto error_module_put; +- } +- } +- + return npu; + + error_module_put: +@@ -643,6 +630,7 @@ static int airoha_npu_probe(struct platf + npu->dev = dev; + npu->ops.ppe_init = airoha_npu_ppe_init; + npu->ops.ppe_deinit = airoha_npu_ppe_deinit; ++ npu->ops.ppe_init_stats = airoha_npu_ppe_stats_setup; + npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries; + npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry; + npu->ops.wlan_init_reserved_memory = airoha_npu_wlan_init_memory; +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1243,12 +1243,11 @@ static int airoha_ppe_flush_sram_entries + + static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) + { +- struct airoha_npu *npu = airoha_npu_get(eth->dev, +- ð->ppe->foe_stats_dma); ++ struct airoha_npu *npu = airoha_npu_get(eth->dev); + + if (IS_ERR(npu)) { + request_module("airoha-npu"); +- npu = airoha_npu_get(eth->dev, ð->ppe->foe_stats_dma); ++ npu = airoha_npu_get(eth->dev); + } + + return npu; +@@ -1257,6 +1256,7 @@ static struct airoha_npu *airoha_ppe_npu + static int airoha_ppe_offload_setup(struct airoha_eth *eth) + { + struct airoha_npu *npu = airoha_ppe_npu_get(eth); ++ struct airoha_ppe *ppe = eth->ppe; + int err; + + if (IS_ERR(npu)) +@@ -1266,12 +1266,19 @@ static int airoha_ppe_offload_setup(stru + if (err) + goto error_npu_put; + +- airoha_ppe_hw_init(eth->ppe); +- err = airoha_ppe_flush_sram_entries(eth->ppe, npu); ++ if (PPE_STATS_NUM_ENTRIES) { ++ err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma, ++ PPE_STATS_NUM_ENTRIES); ++ if (err) ++ goto error_npu_put; ++ } ++ ++ airoha_ppe_hw_init(ppe); ++ err = airoha_ppe_flush_sram_entries(ppe, npu); + if (err) + goto error_npu_put; + +- airoha_ppe_foe_flow_stats_reset(eth->ppe, npu); ++ airoha_ppe_foe_flow_stats_reset(ppe, npu); + + rcu_assign_pointer(eth->npu, npu); + synchronize_rcu(); +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -181,6 +181,8 @@ struct airoha_npu { + struct { + int (*ppe_init)(struct airoha_npu *npu); + int (*ppe_deinit)(struct airoha_npu *npu); ++ int (*ppe_init_stats)(struct airoha_npu *npu, ++ dma_addr_t addr, u32 num_stats_entries); + int (*ppe_flush_sram_entries)(struct airoha_npu *npu, + dma_addr_t foe_addr, + int sram_num_entries); +@@ -206,7 +208,7 @@ struct airoha_npu { + }; + + #if (IS_BUILTIN(CONFIG_NET_AIROHA_NPU) || IS_MODULE(CONFIG_NET_AIROHA_NPU)) +-struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); ++struct airoha_npu *airoha_npu_get(struct device *dev); + void airoha_npu_put(struct airoha_npu *npu); + + static inline int airoha_npu_wlan_init_reserved_memory(struct airoha_npu *npu) +@@ -256,8 +258,7 @@ static inline void airoha_npu_wlan_disab + npu->ops.wlan_disable_irq(npu, q); + } + #else +-static inline struct airoha_npu *airoha_npu_get(struct device *dev, +- dma_addr_t *foe_stats_addr) ++static inline struct airoha_npu *airoha_npu_get(struct device *dev) + { + return NULL; + } diff --git a/target/linux/airoha/patches-6.12/095-v6.19-net-airoha-Fix-loopback-mode-configuration-for-GDM2-.patch b/target/linux/airoha/patches-6.12/095-v6.19-net-airoha-Fix-loopback-mode-configuration-for-GDM2-.patch new file mode 100644 index 00000000000..85d82e850bb --- /dev/null +++ b/target/linux/airoha/patches-6.12/095-v6.19-net-airoha-Fix-loopback-mode-configuration-for-GDM2-.patch @@ -0,0 +1,43 @@ +From fea8cdf6738a8b25fccbb7b109b440795a0892cb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Oct 2025 11:27:43 +0200 +Subject: [PATCH] net: airoha: Fix loopback mode configuration for GDM2 port + +Add missing configuration for loopback mode in airhoha_set_gdm2_loopback +routine. + +Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20251008-airoha-loopback-mode-fix-v2-1-045694fe7f60@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 +++- + drivers/net/ethernet/airoha/airoha_regs.h | 3 +++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1736,7 +1736,9 @@ static void airhoha_set_gdm2_loopback(st + airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff); + airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2), + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK, +- FIELD_PREP(LPBK_CHAN_MASK, chan) | LPBK_EN_MASK); ++ FIELD_PREP(LPBK_CHAN_MASK, chan) | ++ LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK | ++ LBK_CHAN_MODE_MASK | LPBK_EN_MASK); + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(2), + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -151,6 +151,9 @@ + #define LPBK_LEN_MASK GENMASK(23, 10) + #define LPBK_CHAN_MASK GENMASK(8, 4) + #define LPBK_MODE_MASK GENMASK(3, 1) ++#define LBK_GAP_MODE_MASK BIT(3) ++#define LBK_LEN_MODE_MASK BIT(2) ++#define LBK_CHAN_MODE_MASK BIT(1) + #define LPBK_EN_MASK BIT(0) + + #define REG_GDM_TXCHN_EN(_n) (GDM_BASE(_n) + 0x24) diff --git a/target/linux/airoha/patches-6.12/096-v6.19-net-airoha-Add-missing-stats-to-ethtool_eth_mac_stat.patch b/target/linux/airoha/patches-6.12/096-v6.19-net-airoha-Add-missing-stats-to-ethtool_eth_mac_stat.patch new file mode 100644 index 00000000000..c6b7541732c --- /dev/null +++ b/target/linux/airoha/patches-6.12/096-v6.19-net-airoha-Add-missing-stats-to-ethtool_eth_mac_stat.patch @@ -0,0 +1,34 @@ +From 331f8a8bea22aecf99437f3561453a85f40026de Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 13 Oct 2025 16:29:41 +0200 +Subject: [PATCH] net: airoha: Add missing stats to ethtool_eth_mac_stats + +Add the following stats to ethtool ethtool_eth_mac_stats stats: +- FramesTransmittedOK +- OctetsTransmittedOK +- FramesReceivedOK +- OctetsReceivedOK + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251013-airoha-ethtool-improvements-v1-1-fdd1c6fc9be1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2048,8 +2048,12 @@ static void airoha_ethtool_get_mac_stats + airoha_update_hw_stats(port); + do { + start = u64_stats_fetch_begin(&port->stats.syncp); ++ stats->FramesTransmittedOK = port->stats.tx_ok_pkts; ++ stats->OctetsTransmittedOK = port->stats.tx_ok_bytes; + stats->MulticastFramesXmittedOK = port->stats.tx_multicast; + stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; ++ stats->FramesReceivedOK = port->stats.rx_ok_pkts; ++ stats->OctetsReceivedOK = port->stats.rx_ok_bytes; + stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } diff --git a/target/linux/airoha/patches-6.12/097-v6.19-net-airoha-Add-get_link-ethtool-callback.patch b/target/linux/airoha/patches-6.12/097-v6.19-net-airoha-Add-get_link-ethtool-callback.patch new file mode 100644 index 00000000000..4754e2dfb43 --- /dev/null +++ b/target/linux/airoha/patches-6.12/097-v6.19-net-airoha-Add-get_link-ethtool-callback.patch @@ -0,0 +1,25 @@ +From fc4fed9054ef5b5269d4395dd9db36fe98fce9e3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 13 Oct 2025 16:29:42 +0200 +Subject: [PATCH] net: airoha: Add get_link ethtool callback + +Set get_link ethtool callback to ethtool_op_get_link routine. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20251013-airoha-ethtool-improvements-v1-2-fdd1c6fc9be1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2796,6 +2796,7 @@ static const struct ethtool_ops airoha_e + .get_drvinfo = airoha_ethtool_get_drvinfo, + .get_eth_mac_stats = airoha_ethtool_get_mac_stats, + .get_rmon_stats = airoha_ethtool_get_rmon_stats, ++ .get_link = ethtool_op_get_link, + }; + + static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port) diff --git a/target/linux/airoha/patches-6.12/098-v6.19-net-airoha-Take-into-account-out-of-order-tx-complet.patch b/target/linux/airoha/patches-6.12/098-v6.19-net-airoha-Take-into-account-out-of-order-tx-complet.patch new file mode 100644 index 00000000000..4fcae92d7a3 --- /dev/null +++ b/target/linux/airoha/patches-6.12/098-v6.19-net-airoha-Take-into-account-out-of-order-tx-complet.patch @@ -0,0 +1,54 @@ +From bd5afca115f181c85f992d42a57cd497bc823ccb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Oct 2025 11:19:44 +0200 +Subject: [PATCH] net: airoha: Take into account out-of-order tx completions in + airoha_dev_xmit() + +Completion napi can free out-of-order tx descriptors if hw QoS is +enabled and packets with different priority are queued to same DMA ring. +Take into account possible out-of-order reports checking if the tx queue +is full using circular buffer head/tail pointer instead of the number of +queued packets. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Suggested-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251012-airoha-tx-busy-queue-v2-1-a600b08bab2d@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1899,6 +1899,20 @@ static u32 airoha_get_dsa_tag(struct sk_ + #endif + } + ++static bool airoha_dev_tx_queue_busy(struct airoha_queue *q, u32 nr_frags) ++{ ++ u32 tail = q->tail <= q->head ? q->tail + q->ndesc : q->tail; ++ u32 index = q->head + nr_frags; ++ ++ /* completion napi can free out-of-order tx descriptors if hw QoS is ++ * enabled and packets with different priorities are queued to the same ++ * DMA ring. Take into account possible out-of-order reports checking ++ * if the tx queue is full using circular buffer head/tail pointers ++ * instead of the number of queued packets. ++ */ ++ return index >= tail; ++} ++ + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, + struct net_device *dev) + { +@@ -1952,7 +1966,7 @@ static netdev_tx_t airoha_dev_xmit(struc + txq = netdev_get_tx_queue(dev, qid); + nr_frags = 1 + skb_shinfo(skb)->nr_frags; + +- if (q->queued + nr_frags > q->ndesc) { ++ if (airoha_dev_tx_queue_busy(q, nr_frags)) { + /* not enough space in the queue */ + netif_tx_stop_queue(txq); + spin_unlock_bh(&q->lock); diff --git a/target/linux/airoha/patches-6.12/099-01-v6.19-net-airoha-ppe-Dynamically-allocate-foe_check_time-a.patch b/target/linux/airoha/patches-6.12/099-01-v6.19-net-airoha-ppe-Dynamically-allocate-foe_check_time-a.patch new file mode 100644 index 00000000000..3d4b0cfab06 --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-01-v6.19-net-airoha-ppe-Dynamically-allocate-foe_check_time-a.patch @@ -0,0 +1,43 @@ +From 6d5b601d52a27aafff555b480e538507901c672c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:11 +0200 +Subject: [PATCH 01/12] net: airoha: ppe: Dynamically allocate foe_check_time + array in airoha_ppe struct + +This is a preliminary patch to properly enable PPE support for AN7583 +SoC. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-2-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 5 +++++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -554,7 +554,7 @@ struct airoha_ppe { + struct rhashtable l2_flows; + + struct hlist_head *foe_flow; +- u16 foe_check_time[PPE_NUM_ENTRIES]; ++ u16 *foe_check_time; + + struct airoha_foe_stats *foe_stats; + dma_addr_t foe_stats_dma; +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1440,6 +1440,11 @@ int airoha_ppe_init(struct airoha_eth *e + return -ENOMEM; + } + ++ ppe->foe_check_time = devm_kzalloc(eth->dev, PPE_NUM_ENTRIES, ++ GFP_KERNEL); ++ if (!ppe->foe_check_time) ++ return -ENOMEM; ++ + err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); + if (err) + return err; diff --git a/target/linux/airoha/patches-6.12/099-02-v6.19-net-airoha-Add-airoha_ppe_get_num_stats_entries-and-.patch b/target/linux/airoha/patches-6.12/099-02-v6.19-net-airoha-Add-airoha_ppe_get_num_stats_entries-and-.patch new file mode 100644 index 00000000000..6733331f1f3 --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-02-v6.19-net-airoha-Add-airoha_ppe_get_num_stats_entries-and-.patch @@ -0,0 +1,239 @@ +From 15f357cd4581ce6e02e5e97719320600783140ec Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:12 +0200 +Subject: [PATCH 02/12] net: airoha: Add airoha_ppe_get_num_stats_entries() and + airoha_ppe_get_num_total_stats_entries() + +Introduce airoha_ppe_get_num_stats_entries and +airoha_ppe_get_num_total_stats_entries routines in order to make the +code more readable controlling if CONFIG_NET_AIROHA_FLOW_STATS is +enabled or disabled. +Modify airoha_ppe_foe_get_flow_stats_index routine signature relying on +airoha_ppe_get_num_total_stats_entries(). + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-3-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 10 +-- + drivers/net/ethernet/airoha/airoha_ppe.c | 101 ++++++++++++++++++----- + 2 files changed, 81 insertions(+), 30 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -50,15 +50,9 @@ + + #define PPE_NUM 2 + #define PPE1_SRAM_NUM_ENTRIES (8 * 1024) +-#define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES) +-#ifdef CONFIG_NET_AIROHA_FLOW_STATS ++#define PPE_SRAM_NUM_ENTRIES (PPE_NUM * PPE1_SRAM_NUM_ENTRIES) + #define PPE1_STATS_NUM_ENTRIES (4 * 1024) +-#else +-#define PPE1_STATS_NUM_ENTRIES 0 +-#endif /* CONFIG_NET_AIROHA_FLOW_STATS */ +-#define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES) +-#define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES) +-#define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES) ++#define PPE_STATS_NUM_ENTRIES (PPE_NUM * PPE1_STATS_NUM_ENTRIES) + #define PPE_DRAM_NUM_ENTRIES (16 * 1024) + #define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) + #define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1) +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -32,6 +32,24 @@ static const struct rhashtable_params ai + .automatic_shrinking = true, + }; + ++static int airoha_ppe_get_num_stats_entries(struct airoha_ppe *ppe) ++{ ++ if (!IS_ENABLED(CONFIG_NET_AIROHA_FLOW_STATS)) ++ return -EOPNOTSUPP; ++ ++ return PPE1_STATS_NUM_ENTRIES; ++} ++ ++static int airoha_ppe_get_total_num_stats_entries(struct airoha_ppe *ppe) ++{ ++ int num_stats = airoha_ppe_get_num_stats_entries(ppe); ++ ++ if (num_stats > 0) ++ num_stats = num_stats * PPE_NUM; ++ ++ return num_stats; ++} ++ + static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) + { + return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; +@@ -48,7 +66,7 @@ static void airoha_ppe_hw_init(struct ai + { + u32 sram_tb_size, sram_num_entries, dram_num_entries; + struct airoha_eth *eth = ppe->eth; +- int i; ++ int i, sram_num_stats_entries; + + sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry); + dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES); +@@ -103,8 +121,13 @@ static void airoha_ppe_hw_init(struct ai + } + + if (airoha_ppe2_is_enabled(eth)) { +- sram_num_entries = +- PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES); ++ sram_num_entries = PPE1_SRAM_NUM_ENTRIES; ++ sram_num_stats_entries = ++ airoha_ppe_get_num_stats_entries(ppe); ++ if (sram_num_stats_entries > 0) ++ sram_num_entries -= sram_num_stats_entries; ++ sram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(sram_num_entries); ++ + airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), + PPE_SRAM_TB_NUM_ENTRY_MASK | + PPE_DRAM_TB_NUM_ENTRY_MASK, +@@ -120,8 +143,13 @@ static void airoha_ppe_hw_init(struct ai + FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, + dram_num_entries)); + } else { +- sram_num_entries = +- PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES); ++ sram_num_entries = PPE_SRAM_NUM_ENTRIES; ++ sram_num_stats_entries = ++ airoha_ppe_get_total_num_stats_entries(ppe); ++ if (sram_num_stats_entries > 0) ++ sram_num_entries -= sram_num_stats_entries; ++ sram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(sram_num_entries); ++ + airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), + PPE_SRAM_TB_NUM_ENTRY_MASK | + PPE_DRAM_TB_NUM_ENTRY_MASK, +@@ -480,13 +508,21 @@ static u32 airoha_ppe_foe_get_entry_hash + return hash; + } + +-static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash) ++static int airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, ++ u32 hash, u32 *index) + { +- if (!airoha_ppe2_is_enabled(ppe->eth)) +- return hash; ++ int ppe_num_stats_entries; ++ ++ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); ++ if (ppe_num_stats_entries < 0) ++ return ppe_num_stats_entries; ++ ++ *index = hash; ++ if (airoha_ppe2_is_enabled(ppe->eth) && ++ hash >= ppe_num_stats_entries) ++ *index = *index - PPE_STATS_NUM_ENTRIES; + +- return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES +- : hash; ++ return 0; + } + + static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe, +@@ -500,9 +536,13 @@ static void airoha_ppe_foe_flow_stat_ent + static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe, + struct airoha_npu *npu) + { +- int i; ++ int i, ppe_num_stats_entries; ++ ++ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); ++ if (ppe_num_stats_entries < 0) ++ return; + +- for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++) ++ for (i = 0; i < ppe_num_stats_entries; i++) + airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i); + } + +@@ -513,10 +553,17 @@ static void airoha_ppe_foe_flow_stats_up + { + int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); + u32 index, pse_port, val, *data, *ib2, *meter; ++ int ppe_num_stats_entries; + u8 nbq; + +- index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); +- if (index >= PPE_STATS_NUM_ENTRIES) ++ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); ++ if (ppe_num_stats_entries < 0) ++ return; ++ ++ if (airoha_ppe_foe_get_flow_stats_index(ppe, hash, &index)) ++ return; ++ ++ if (index >= ppe_num_stats_entries) + return; + + if (type == PPE_PKT_TYPE_BRIDGE) { +@@ -1158,11 +1205,19 @@ static int airoha_ppe_flow_offload_destr + void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, + struct airoha_foe_stats64 *stats) + { +- u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); + struct airoha_eth *eth = ppe->eth; ++ int ppe_num_stats_entries; + struct airoha_npu *npu; ++ u32 index; ++ ++ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); ++ if (ppe_num_stats_entries < 0) ++ return; + +- if (index >= PPE_STATS_NUM_ENTRIES) ++ if (airoha_ppe_foe_get_flow_stats_index(ppe, hash, &index)) ++ return; ++ ++ if (index >= ppe_num_stats_entries) + return; + + rcu_read_lock(); +@@ -1257,7 +1312,7 @@ static int airoha_ppe_offload_setup(stru + { + struct airoha_npu *npu = airoha_ppe_npu_get(eth); + struct airoha_ppe *ppe = eth->ppe; +- int err; ++ int err, ppe_num_stats_entries; + + if (IS_ERR(npu)) + return PTR_ERR(npu); +@@ -1266,9 +1321,10 @@ static int airoha_ppe_offload_setup(stru + if (err) + goto error_npu_put; + +- if (PPE_STATS_NUM_ENTRIES) { ++ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); ++ if (ppe_num_stats_entries > 0) { + err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma, +- PPE_STATS_NUM_ENTRIES); ++ ppe_num_stats_entries); + if (err) + goto error_npu_put; + } +@@ -1405,8 +1461,8 @@ EXPORT_SYMBOL_GPL(airoha_ppe_put_dev); + + int airoha_ppe_init(struct airoha_eth *eth) + { ++ int foe_size, err, ppe_num_stats_entries; + struct airoha_ppe *ppe; +- int foe_size, err; + + ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL); + if (!ppe) +@@ -1431,8 +1487,9 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe->foe_flow) + return -ENOMEM; + +- foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats); +- if (foe_size) { ++ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); ++ if (ppe_num_stats_entries > 0) { ++ foe_size = ppe_num_stats_entries * sizeof(*ppe->foe_stats); + ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size, + &ppe->foe_stats_dma, + GFP_KERNEL); diff --git a/target/linux/airoha/patches-6.12/099-03-v6.19-net-airoha-Add-airoha_eth_soc_data-struct.patch b/target/linux/airoha/patches-6.12/099-03-v6.19-net-airoha-Add-airoha_eth_soc_data-struct.patch new file mode 100644 index 00000000000..75c630d2e94 --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-03-v6.19-net-airoha-Add-airoha_eth_soc_data-struct.patch @@ -0,0 +1,152 @@ +From 5863b4e065e2253ef05684f728a04e4972046bcb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:13 +0200 +Subject: [PATCH 03/12] net: airoha: Add airoha_eth_soc_data struct + +Introduce airoha_eth_soc_data struct to contain differences between +various SoC. Move XSI reset names in airoha_eth_soc_data. This is a +preliminary patch to enable AN7583 ethernet controller support in +airoha-eth driver. + +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-4-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 42 +++++++++++++++++++----- + drivers/net/ethernet/airoha/airoha_eth.h | 17 ++++++++-- + 2 files changed, 48 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1413,8 +1413,7 @@ static int airoha_hw_init(struct platfor + int err, i; + + /* disable xsi */ +- err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts), +- eth->xsi_rsts); ++ err = reset_control_bulk_assert(eth->soc->num_xsi_rsts, eth->xsi_rsts); + if (err) + return err; + +@@ -2948,6 +2947,7 @@ free_metadata_dst: + + static int airoha_probe(struct platform_device *pdev) + { ++ struct reset_control_bulk_data *xsi_rsts; + struct device_node *np; + struct airoha_eth *eth; + int i, err; +@@ -2956,6 +2956,10 @@ static int airoha_probe(struct platform_ + if (!eth) + return -ENOMEM; + ++ eth->soc = of_device_get_match_data(&pdev->dev); ++ if (!eth->soc) ++ return -EINVAL; ++ + eth->dev = &pdev->dev; + + err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32)); +@@ -2980,13 +2984,18 @@ static int airoha_probe(struct platform_ + return err; + } + +- eth->xsi_rsts[0].id = "xsi-mac"; +- eth->xsi_rsts[1].id = "hsi0-mac"; +- eth->xsi_rsts[2].id = "hsi1-mac"; +- eth->xsi_rsts[3].id = "hsi-mac"; +- eth->xsi_rsts[4].id = "xfp-mac"; ++ xsi_rsts = devm_kzalloc(eth->dev, ++ eth->soc->num_xsi_rsts * sizeof(*xsi_rsts), ++ GFP_KERNEL); ++ if (err) ++ return err; ++ ++ eth->xsi_rsts = xsi_rsts; ++ for (i = 0; i < eth->soc->num_xsi_rsts; i++) ++ eth->xsi_rsts[i].id = eth->soc->xsi_rsts_names[i]; ++ + err = devm_reset_control_bulk_get_exclusive(eth->dev, +- ARRAY_SIZE(eth->xsi_rsts), ++ eth->soc->num_xsi_rsts, + eth->xsi_rsts); + if (err) { + dev_err(eth->dev, "failed to get bulk xsi reset lines\n"); +@@ -3072,8 +3081,23 @@ static void airoha_remove(struct platfor + platform_set_drvdata(pdev, NULL); + } + ++static const char * const en7581_xsi_rsts_names[] = { ++ "xsi-mac", ++ "hsi0-mac", ++ "hsi1-mac", ++ "hsi-mac", ++ "xfp-mac", ++}; ++ ++static const struct airoha_eth_soc_data en7581_soc_data = { ++ .version = 0x7581, ++ .xsi_rsts_names = en7581_xsi_rsts_names, ++ .num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names), ++ .num_ppe = 2, ++}; ++ + static const struct of_device_id of_airoha_match[] = { +- { .compatible = "airoha,en7581-eth" }, ++ { .compatible = "airoha,en7581-eth", .data = &en7581_soc_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, of_airoha_match); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -21,7 +21,6 @@ + #define AIROHA_MAX_NUM_IRQ_BANKS 4 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 +-#define AIROHA_MAX_NUM_XSI_RSTS 5 + #define AIROHA_MAX_MTU 9216 + #define AIROHA_MAX_PACKET_SIZE 2048 + #define AIROHA_NUM_QOS_CHANNELS 4 +@@ -556,9 +555,18 @@ struct airoha_ppe { + struct dentry *debugfs_dir; + }; + ++struct airoha_eth_soc_data { ++ u16 version; ++ const char * const *xsi_rsts_names; ++ int num_xsi_rsts; ++ int num_ppe; ++}; ++ + struct airoha_eth { + struct device *dev; + ++ const struct airoha_eth_soc_data *soc; ++ + unsigned long state; + void __iomem *fe_regs; + +@@ -568,7 +576,7 @@ struct airoha_eth { + struct rhashtable flow_table; + + struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS]; +- struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS]; ++ struct reset_control_bulk_data *xsi_rsts; + + struct net_device *napi_dev; + +@@ -611,6 +619,11 @@ static inline bool airhoa_is_lan_gdm_por + return port->id == 1; + } + ++static inline bool airoha_is_7581(struct airoha_eth *eth) ++{ ++ return eth->soc->version == 0x7581; ++} ++ + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + diff --git a/target/linux/airoha/patches-6.12/099-04-v6.19-net-airoha-Generalize-airoha_ppe2_is_enabled-routine.patch b/target/linux/airoha/patches-6.12/099-04-v6.19-net-airoha-Generalize-airoha_ppe2_is_enabled-routine.patch new file mode 100644 index 00000000000..e6626674e82 --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-04-v6.19-net-airoha-Generalize-airoha_ppe2_is_enabled-routine.patch @@ -0,0 +1,147 @@ +From ef9449f080b61920cdc3d3106f8ffc2d9ba8b861 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:14 +0200 +Subject: [PATCH 04/12] net: airoha: Generalize airoha_ppe2_is_enabled routine + +Rename airoha_ppe2_is_enabled() in airoha_ppe_is_enabled() and +generalize it in order to check if each PPE module is enabled. +Rely on airoha_ppe_is_enabled routine to properly initialize PPE for +AN7583 SoC since AN7583 does not support PPE2. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-5-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 32 ++++++++++++++++-------- + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_ppe.c | 17 +++++++------ + 3 files changed, 32 insertions(+), 18 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -297,8 +297,11 @@ static void airoha_fe_pse_ports_init(str + int q; + + all_rsv = airoha_fe_get_pse_all_rsv(eth); +- /* hw misses PPE2 oq rsv */ +- all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2]; ++ if (airoha_ppe_is_enabled(eth, 1)) { ++ /* hw misses PPE2 oq rsv */ ++ all_rsv += PSE_RSV_PAGES * ++ pse_port_num_queues[FE_PSE_PORT_PPE2]; ++ } + airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv); + + /* CMD1 */ +@@ -335,13 +338,17 @@ static void airoha_fe_pse_ports_init(str + for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++) + airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q, + PSE_QUEUE_RSV_PAGES); +- /* PPE2 */ +- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) { +- if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2) +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, +- PSE_QUEUE_RSV_PAGES); +- else +- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0); ++ if (airoha_ppe_is_enabled(eth, 1)) { ++ /* PPE2 */ ++ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) { ++ if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2) ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, ++ q, ++ PSE_QUEUE_RSV_PAGES); ++ else ++ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, ++ q, 0); ++ } + } + /* GMD4 */ + for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++) +@@ -1788,8 +1795,11 @@ static int airoha_dev_init(struct net_de + airhoha_set_gdm2_loopback(port); + fallthrough; + case 2: +- pse_port = FE_PSE_PORT_PPE2; +- break; ++ if (airoha_ppe_is_enabled(eth, 1)) { ++ pse_port = FE_PSE_PORT_PPE2; ++ break; ++ } ++ fallthrough; + default: + pse_port = FE_PSE_PORT_PPE1; + break; +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -627,6 +627,7 @@ static inline bool airoha_is_7581(struct + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + ++bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); + void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, + u16 hash, bool rx_wlan); + int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -50,9 +50,12 @@ static int airoha_ppe_get_total_num_stat + return num_stats; + } + +-static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) ++bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index) + { +- return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; ++ if (index >= eth->soc->num_ppe) ++ return false; ++ ++ return airoha_fe_rr(eth, REG_PPE_GLO_CFG(index)) & PPE_GLO_CFG_EN_MASK; + } + + static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) +@@ -120,7 +123,7 @@ static void airoha_ppe_hw_init(struct ai + AIROHA_MAX_MTU)); + } + +- if (airoha_ppe2_is_enabled(eth)) { ++ if (airoha_ppe_is_enabled(eth, 1)) { + sram_num_entries = PPE1_SRAM_NUM_ENTRIES; + sram_num_stats_entries = + airoha_ppe_get_num_stats_entries(ppe); +@@ -518,7 +521,7 @@ static int airoha_ppe_foe_get_flow_stats + return ppe_num_stats_entries; + + *index = hash; +- if (airoha_ppe2_is_enabled(ppe->eth) && ++ if (airoha_ppe_is_enabled(ppe->eth, 1) && + hash >= ppe_num_stats_entries) + *index = *index - PPE_STATS_NUM_ENTRIES; + +@@ -613,7 +616,7 @@ airoha_ppe_foe_get_entry_locked(struct a + u32 val; + int i; + +- ppe2 = airoha_ppe2_is_enabled(ppe->eth) && ++ ppe2 = airoha_ppe_is_enabled(ppe->eth, 1) && + hash >= PPE1_SRAM_NUM_ENTRIES; + airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2), + FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) | +@@ -691,7 +694,7 @@ static int airoha_ppe_foe_commit_entry(s + + if (hash < PPE_SRAM_NUM_ENTRIES) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); +- bool ppe2 = airoha_ppe2_is_enabled(eth) && ++ bool ppe2 = airoha_ppe_is_enabled(eth, 1) && + hash >= PPE1_SRAM_NUM_ENTRIES; + + err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), +@@ -1286,7 +1289,7 @@ static int airoha_ppe_flush_sram_entries + int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES; + struct airoha_foe_entry *hwe = ppe->foe; + +- if (airoha_ppe2_is_enabled(ppe->eth)) ++ if (airoha_ppe_is_enabled(ppe->eth, 1)) + sram_num_entries = sram_num_entries / 2; + + for (i = 0; i < sram_num_entries; i++) diff --git a/target/linux/airoha/patches-6.12/099-05-v6.19-net-airoha-ppe-Move-PPE-memory-info-in-airoha_eth_so.patch b/target/linux/airoha/patches-6.12/099-05-v6.19-net-airoha-ppe-Move-PPE-memory-info-in-airoha_eth_so.patch new file mode 100644 index 00000000000..2382a21168c --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-05-v6.19-net-airoha-ppe-Move-PPE-memory-info-in-airoha_eth_so.patch @@ -0,0 +1,359 @@ +From 5bd1d1fd48ea9f8300b211540d946899c7f96480 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:15 +0200 +Subject: [PATCH 05/12] net: airoha: ppe: Move PPE memory info in + airoha_eth_soc_data struct + +AN7583 SoC runs a single PPE device while EN7581 runs two of them. +Moreover PPE SRAM in AN7583 SoC is reduced to 8K (while SRAM is 16K on +EN7581). Take into account PPE memory layout during PPE configuration. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-6-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 10 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 133 +++++++++--------- + .../net/ethernet/airoha/airoha_ppe_debugfs.c | 3 +- + 3 files changed, 70 insertions(+), 76 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -47,14 +47,9 @@ + #define QDMA_METER_IDX(_n) ((_n) & 0xff) + #define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3) + +-#define PPE_NUM 2 +-#define PPE1_SRAM_NUM_ENTRIES (8 * 1024) +-#define PPE_SRAM_NUM_ENTRIES (PPE_NUM * PPE1_SRAM_NUM_ENTRIES) +-#define PPE1_STATS_NUM_ENTRIES (4 * 1024) +-#define PPE_STATS_NUM_ENTRIES (PPE_NUM * PPE1_STATS_NUM_ENTRIES) ++#define PPE_SRAM_NUM_ENTRIES (8 * 1024) ++#define PPE_STATS_NUM_ENTRIES (4 * 1024) + #define PPE_DRAM_NUM_ENTRIES (16 * 1024) +-#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) +-#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1) + #define PPE_ENTRY_SIZE 80 + #define PPE_RAM_NUM_ENTRIES_SHIFT(_n) (__ffs((_n) >> 10)) + +@@ -634,6 +629,7 @@ int airoha_ppe_setup_tc_block_cb(struct + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); + void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); ++u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); + void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -37,19 +37,36 @@ static int airoha_ppe_get_num_stats_entr + if (!IS_ENABLED(CONFIG_NET_AIROHA_FLOW_STATS)) + return -EOPNOTSUPP; + +- return PPE1_STATS_NUM_ENTRIES; ++ return PPE_STATS_NUM_ENTRIES; + } + + static int airoha_ppe_get_total_num_stats_entries(struct airoha_ppe *ppe) + { + int num_stats = airoha_ppe_get_num_stats_entries(ppe); + +- if (num_stats > 0) +- num_stats = num_stats * PPE_NUM; ++ if (num_stats > 0) { ++ struct airoha_eth *eth = ppe->eth; ++ ++ num_stats = num_stats * eth->soc->num_ppe; ++ } + + return num_stats; + } + ++static u32 airoha_ppe_get_total_sram_num_entries(struct airoha_ppe *ppe) ++{ ++ struct airoha_eth *eth = ppe->eth; ++ ++ return PPE_SRAM_NUM_ENTRIES * eth->soc->num_ppe; ++} ++ ++u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe) ++{ ++ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); ++ ++ return sram_num_entries + PPE_DRAM_NUM_ENTRIES; ++} ++ + bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index) + { + if (index >= eth->soc->num_ppe) +@@ -67,14 +84,22 @@ static u32 airoha_ppe_get_timestamp(stru + + static void airoha_ppe_hw_init(struct airoha_ppe *ppe) + { +- u32 sram_tb_size, sram_num_entries, dram_num_entries; ++ u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries; ++ u32 sram_tb_size, dram_num_entries; + struct airoha_eth *eth = ppe->eth; + int i, sram_num_stats_entries; + +- sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry); ++ sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); ++ sram_tb_size = sram_num_entries * sizeof(struct airoha_foe_entry); + dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES); + +- for (i = 0; i < PPE_NUM; i++) { ++ sram_num_stats_entries = airoha_ppe_get_num_stats_entries(ppe); ++ if (sram_num_stats_entries > 0) ++ sram_ppe_num_data_entries -= sram_num_stats_entries; ++ sram_ppe_num_data_entries = ++ PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries); ++ ++ for (i = 0; i < eth->soc->num_ppe; i++) { + int p; + + airoha_fe_wr(eth, REG_PPE_TB_BASE(i), +@@ -106,10 +131,16 @@ static void airoha_ppe_hw_init(struct ai + + airoha_fe_rmw(eth, REG_PPE_TB_CFG(i), + PPE_TB_CFG_SEARCH_MISS_MASK | ++ PPE_SRAM_TB_NUM_ENTRY_MASK | ++ PPE_DRAM_TB_NUM_ENTRY_MASK | + PPE_TB_CFG_KEEPALIVE_MASK | + PPE_TB_ENTRY_SIZE_MASK, + FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) | +- FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0)); ++ FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0) | ++ FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, ++ sram_ppe_num_data_entries) | ++ FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, ++ dram_num_entries)); + + airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); + +@@ -122,45 +153,6 @@ static void airoha_ppe_hw_init(struct ai + FIELD_PREP(FP1_EGRESS_MTU_MASK, + AIROHA_MAX_MTU)); + } +- +- if (airoha_ppe_is_enabled(eth, 1)) { +- sram_num_entries = PPE1_SRAM_NUM_ENTRIES; +- sram_num_stats_entries = +- airoha_ppe_get_num_stats_entries(ppe); +- if (sram_num_stats_entries > 0) +- sram_num_entries -= sram_num_stats_entries; +- sram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(sram_num_entries); +- +- airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), +- PPE_SRAM_TB_NUM_ENTRY_MASK | +- PPE_DRAM_TB_NUM_ENTRY_MASK, +- FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, +- sram_num_entries) | +- FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, +- dram_num_entries)); +- airoha_fe_rmw(eth, REG_PPE_TB_CFG(1), +- PPE_SRAM_TB_NUM_ENTRY_MASK | +- PPE_DRAM_TB_NUM_ENTRY_MASK, +- FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, +- sram_num_entries) | +- FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, +- dram_num_entries)); +- } else { +- sram_num_entries = PPE_SRAM_NUM_ENTRIES; +- sram_num_stats_entries = +- airoha_ppe_get_total_num_stats_entries(ppe); +- if (sram_num_stats_entries > 0) +- sram_num_entries -= sram_num_stats_entries; +- sram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(sram_num_entries); +- +- airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), +- PPE_SRAM_TB_NUM_ENTRY_MASK | +- PPE_DRAM_TB_NUM_ENTRY_MASK, +- FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK, +- sram_num_entries) | +- FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, +- dram_num_entries)); +- } + } + + static void airoha_ppe_flow_mangle_eth(const struct flow_action_entry *act, void *eth) +@@ -459,9 +451,11 @@ static int airoha_ppe_foe_entry_set_ipv6 + return 0; + } + +-static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe) ++static u32 airoha_ppe_foe_get_entry_hash(struct airoha_ppe *ppe, ++ struct airoha_foe_entry *hwe) + { + int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ u32 ppe_hash_mask = airoha_ppe_get_total_num_entries(ppe) - 1; + u32 hash, hv1, hv2, hv3; + + switch (type) { +@@ -499,14 +493,14 @@ static u32 airoha_ppe_foe_get_entry_hash + case PPE_PKT_TYPE_IPV6_6RD: + default: + WARN_ON_ONCE(1); +- return PPE_HASH_MASK; ++ return ppe_hash_mask; + } + + hash = (hv1 & hv2) | ((~hv1) & hv3); + hash = (hash >> 24) | ((hash & 0xffffff) << 8); + hash ^= hv1 ^ hv2 ^ hv3; + hash ^= hash >> 16; +- hash &= PPE_NUM_ENTRIES - 1; ++ hash &= ppe_hash_mask; + + return hash; + } +@@ -607,9 +601,11 @@ static void airoha_ppe_foe_flow_stats_up + static struct airoha_foe_entry * + airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash) + { ++ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); ++ + lockdep_assert_held(&ppe_lock); + +- if (hash < PPE_SRAM_NUM_ENTRIES) { ++ if (hash < sram_num_entries) { + u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); + struct airoha_eth *eth = ppe->eth; + bool ppe2; +@@ -617,7 +613,7 @@ airoha_ppe_foe_get_entry_locked(struct a + int i; + + ppe2 = airoha_ppe_is_enabled(ppe->eth, 1) && +- hash >= PPE1_SRAM_NUM_ENTRIES; ++ hash >= PPE_SRAM_NUM_ENTRIES; + airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2), + FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) | + PPE_SRAM_CTRL_REQ_MASK); +@@ -668,6 +664,7 @@ static int airoha_ppe_foe_commit_entry(s + struct airoha_foe_entry *e, + u32 hash, bool rx_wlan) + { ++ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); + struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); + u32 ts = airoha_ppe_get_timestamp(ppe); + struct airoha_eth *eth = ppe->eth; +@@ -692,10 +689,10 @@ static int airoha_ppe_foe_commit_entry(s + if (!rx_wlan) + airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); + +- if (hash < PPE_SRAM_NUM_ENTRIES) { ++ if (hash < sram_num_entries) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); + bool ppe2 = airoha_ppe_is_enabled(eth, 1) && +- hash >= PPE1_SRAM_NUM_ENTRIES; ++ hash >= PPE_SRAM_NUM_ENTRIES; + + err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), + hash, ppe2); +@@ -822,7 +819,7 @@ static void airoha_ppe_foe_insert_entry( + if (state == AIROHA_FOE_STATE_BIND) + goto unlock; + +- index = airoha_ppe_foe_get_entry_hash(hwe); ++ index = airoha_ppe_foe_get_entry_hash(ppe, hwe); + hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) { + if (e->type == FLOW_TYPE_L2_SUBFLOW) { + state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); +@@ -882,7 +879,7 @@ static int airoha_ppe_foe_flow_commit_en + if (type == PPE_PKT_TYPE_BRIDGE) + return airoha_ppe_foe_l2_flow_commit_entry(ppe, e); + +- hash = airoha_ppe_foe_get_entry_hash(&e->data); ++ hash = airoha_ppe_foe_get_entry_hash(ppe, &e->data); + e->type = FLOW_TYPE_L4; + e->hash = 0xffff; + +@@ -1286,17 +1283,15 @@ static int airoha_ppe_flow_offload_cmd(s + static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe, + struct airoha_npu *npu) + { +- int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES; ++ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); + struct airoha_foe_entry *hwe = ppe->foe; ++ int i; + +- if (airoha_ppe_is_enabled(ppe->eth, 1)) +- sram_num_entries = sram_num_entries / 2; +- +- for (i = 0; i < sram_num_entries; i++) ++ for (i = 0; i < PPE_SRAM_NUM_ENTRIES; i++) + memset(&hwe[i], 0, sizeof(*hwe)); + + return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma, +- PPE_SRAM_NUM_ENTRIES); ++ sram_num_entries); + } + + static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) +@@ -1372,9 +1367,10 @@ void airoha_ppe_check_skb(struct airoha_ + u16 hash, bool rx_wlan) + { + struct airoha_ppe *ppe = dev->priv; ++ u32 ppe_hash_mask = airoha_ppe_get_total_num_entries(ppe) - 1; + u16 now, diff; + +- if (hash > PPE_HASH_MASK) ++ if (hash > ppe_hash_mask) + return; + + now = (u16)jiffies; +@@ -1465,6 +1461,7 @@ EXPORT_SYMBOL_GPL(airoha_ppe_put_dev); + int airoha_ppe_init(struct airoha_eth *eth) + { + int foe_size, err, ppe_num_stats_entries; ++ u32 ppe_num_entries; + struct airoha_ppe *ppe; + + ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL); +@@ -1474,18 +1471,18 @@ int airoha_ppe_init(struct airoha_eth *e + ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb; + ppe->dev.ops.check_skb = airoha_ppe_check_skb; + ppe->dev.priv = ppe; ++ ppe->eth = eth; ++ eth->ppe = ppe; + +- foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); ++ ppe_num_entries = airoha_ppe_get_total_num_entries(ppe); ++ foe_size = ppe_num_entries * sizeof(struct airoha_foe_entry); + ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma, + GFP_KERNEL); + if (!ppe->foe) + return -ENOMEM; + +- ppe->eth = eth; +- eth->ppe = ppe; +- + ppe->foe_flow = devm_kzalloc(eth->dev, +- PPE_NUM_ENTRIES * sizeof(*ppe->foe_flow), ++ ppe_num_entries * sizeof(*ppe->foe_flow), + GFP_KERNEL); + if (!ppe->foe_flow) + return -ENOMEM; +@@ -1500,7 +1497,7 @@ int airoha_ppe_init(struct airoha_eth *e + return -ENOMEM; + } + +- ppe->foe_check_time = devm_kzalloc(eth->dev, PPE_NUM_ENTRIES, ++ ppe->foe_check_time = devm_kzalloc(eth->dev, ppe_num_entries, + GFP_KERNEL); + if (!ppe->foe_check_time) + return -ENOMEM; +--- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +@@ -53,9 +53,10 @@ static int airoha_ppe_debugfs_foe_show(s + [AIROHA_FOE_STATE_FIN] = "FIN", + }; + struct airoha_ppe *ppe = m->private; ++ u32 ppe_num_entries = airoha_ppe_get_total_num_entries(ppe); + int i; + +- for (i = 0; i < PPE_NUM_ENTRIES; i++) { ++ for (i = 0; i < ppe_num_entries; i++) { + const char *state_str, *type_str = "UNKNOWN"; + void *src_addr = NULL, *dest_addr = NULL; + u16 *src_port = NULL, *dest_port = NULL; diff --git a/target/linux/airoha/patches-6.12/099-06-v6.19-net-airoha-ppe-Remove-airoha_ppe_is_enabled-where-no.patch b/target/linux/airoha/patches-6.12/099-06-v6.19-net-airoha-ppe-Remove-airoha_ppe_is_enabled-where-no.patch new file mode 100644 index 00000000000..18b92fb50df --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-06-v6.19-net-airoha-ppe-Remove-airoha_ppe_is_enabled-where-no.patch @@ -0,0 +1,58 @@ +From 41139125f5c70e0f66f0cc4ac1b3a62f5801ab42 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:16 +0200 +Subject: [PATCH 06/12] net: airoha: ppe: Remove airoha_ppe_is_enabled() where + not necessary + +Now each PPE has always PPE_STATS_NUM_ENTRIES entries so we do not need +to run airoha_ppe_is_enabled routine to check if the hash refers to +PPE1 or PPE2. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-7-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -514,10 +514,8 @@ static int airoha_ppe_foe_get_flow_stats + if (ppe_num_stats_entries < 0) + return ppe_num_stats_entries; + +- *index = hash; +- if (airoha_ppe_is_enabled(ppe->eth, 1) && +- hash >= ppe_num_stats_entries) +- *index = *index - PPE_STATS_NUM_ENTRIES; ++ *index = hash >= ppe_num_stats_entries ? hash - PPE_STATS_NUM_ENTRIES ++ : hash; + + return 0; + } +@@ -607,13 +605,11 @@ airoha_ppe_foe_get_entry_locked(struct a + + if (hash < sram_num_entries) { + u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); ++ bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES; + struct airoha_eth *eth = ppe->eth; +- bool ppe2; + u32 val; + int i; + +- ppe2 = airoha_ppe_is_enabled(ppe->eth, 1) && +- hash >= PPE_SRAM_NUM_ENTRIES; + airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2), + FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) | + PPE_SRAM_CTRL_REQ_MASK); +@@ -691,8 +687,7 @@ static int airoha_ppe_foe_commit_entry(s + + if (hash < sram_num_entries) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); +- bool ppe2 = airoha_ppe_is_enabled(eth, 1) && +- hash >= PPE_SRAM_NUM_ENTRIES; ++ bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES; + + err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), + hash, ppe2); diff --git a/target/linux/airoha/patches-6.12/099-07-v6.19-net-airoha-ppe-Configure-SRAM-PPE-entries-via-the-cp.patch b/target/linux/airoha/patches-6.12/099-07-v6.19-net-airoha-ppe-Configure-SRAM-PPE-entries-via-the-cp.patch new file mode 100644 index 00000000000..60b4ee1e448 --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-07-v6.19-net-airoha-ppe-Configure-SRAM-PPE-entries-via-the-cp.patch @@ -0,0 +1,65 @@ +From 306b78f5035af4bd011753c5a6b12812515caa6c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:17 +0200 +Subject: [PATCH 07/12] net: airoha: ppe: Configure SRAM PPE entries via the + cpu + +Introduce airoha_ppe_foe_commit_sram_entry routine in order to configure +the SRAM PPE entries directly via the CPU instead of using the NPU APIs. +This is a preliminary patch to enable netfilter flowtable hw offload for +AN7583 SoC. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-8-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 30 ++++++++++++++++++------ + 1 file changed, 23 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -656,6 +656,27 @@ static bool airoha_ppe_foe_compare_entry + return !memcmp(&e->data.d, &hwe->d, len - sizeof(hwe->ib1)); + } + ++static int airoha_ppe_foe_commit_sram_entry(struct airoha_ppe *ppe, u32 hash) ++{ ++ struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); ++ bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES; ++ u32 *ptr = (u32 *)hwe, val; ++ int i; ++ ++ for (i = 0; i < sizeof(*hwe) / sizeof(*ptr); i++) ++ airoha_fe_wr(ppe->eth, REG_PPE_RAM_ENTRY(ppe2, i), ptr[i]); ++ ++ wmb(); ++ airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2), ++ FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) | ++ PPE_SRAM_CTRL_WR_MASK | PPE_SRAM_CTRL_REQ_MASK); ++ ++ return read_poll_timeout_atomic(airoha_fe_rr, val, ++ val & PPE_SRAM_CTRL_ACK_MASK, ++ 10, 100, false, ppe->eth, ++ REG_PPE_RAM_CTRL(ppe2)); ++} ++ + static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe, + struct airoha_foe_entry *e, + u32 hash, bool rx_wlan) +@@ -685,13 +706,8 @@ static int airoha_ppe_foe_commit_entry(s + if (!rx_wlan) + airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); + +- if (hash < sram_num_entries) { +- dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); +- bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES; +- +- err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), +- hash, ppe2); +- } ++ if (hash < sram_num_entries) ++ err = airoha_ppe_foe_commit_sram_entry(ppe, hash); + unlock: + rcu_read_unlock(); + diff --git a/target/linux/airoha/patches-6.12/099-08-v6.19-net-airoha-ppe-Flush-PPE-SRAM-table-during-PPE-setup.patch b/target/linux/airoha/patches-6.12/099-08-v6.19-net-airoha-ppe-Flush-PPE-SRAM-table-during-PPE-setup.patch new file mode 100644 index 00000000000..24ecccabc8d --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-08-v6.19-net-airoha-ppe-Flush-PPE-SRAM-table-during-PPE-setup.patch @@ -0,0 +1,71 @@ +From 620d7b91aadbd4eb930895b07e34f0a155a9d3c1 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:18 +0200 +Subject: [PATCH 08/12] net: airoha: ppe: Flush PPE SRAM table during PPE setup + +Rely on airoha_ppe_foe_commit_sram_entry routine to flush SRAM PPE table +entries. This patch allow moving PPE SRAM flush during PPE setup and +avoid dumping uninitialized values via the debugfs if no entries are +offloaded yet. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-9-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1291,18 +1291,22 @@ static int airoha_ppe_flow_offload_cmd(s + return -EOPNOTSUPP; + } + +-static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe, +- struct airoha_npu *npu) ++static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe) + { + u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); + struct airoha_foe_entry *hwe = ppe->foe; +- int i; ++ int i, err = 0; ++ ++ for (i = 0; i < sram_num_entries; i++) { ++ int err; + +- for (i = 0; i < PPE_SRAM_NUM_ENTRIES; i++) + memset(&hwe[i], 0, sizeof(*hwe)); ++ err = airoha_ppe_foe_commit_sram_entry(ppe, i); ++ if (err) ++ break; ++ } + +- return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma, +- sram_num_entries); ++ return err; + } + + static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) +@@ -1339,10 +1343,6 @@ static int airoha_ppe_offload_setup(stru + } + + airoha_ppe_hw_init(ppe); +- err = airoha_ppe_flush_sram_entries(ppe, npu); +- if (err) +- goto error_npu_put; +- + airoha_ppe_foe_flow_stats_reset(ppe, npu); + + rcu_assign_pointer(eth->npu, npu); +@@ -1513,6 +1513,10 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe->foe_check_time) + return -ENOMEM; + ++ err = airoha_ppe_flush_sram_entries(ppe); ++ if (err) ++ return err; ++ + err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); + if (err) + return err; diff --git a/target/linux/airoha/patches-6.12/099-09-v6.19-net-airoha-Select-default-ppe-cpu-port-in-airoha_dev.patch b/target/linux/airoha/patches-6.12/099-09-v6.19-net-airoha-Select-default-ppe-cpu-port-in-airoha_dev.patch new file mode 100644 index 00000000000..d6fc01a4471 --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-09-v6.19-net-airoha-Select-default-ppe-cpu-port-in-airoha_dev.patch @@ -0,0 +1,91 @@ +From c71a7a861ef02aa2bebb18c2f3385aa3f19094e0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:19 +0200 +Subject: [PATCH 09/12] net: airoha: Select default ppe cpu port in + airoha_dev_init() + +Select the PPE default cpu port in airoha_dev_init routine. +This patch allows to distribute the load between the two available cpu +ports (FE_PSE_PORT_CDM1 and FE_PSE_PORT_CDM2) if the device is running a +single PPE module (e.g. 7583) selecting the cpu port based on the use +QDMA device. For multi-PPE device (e.g. 7581) assign FE_PSE_PORT_CDM1 to +PPE1 and FE_PSE_PORT_CDM2 to PPE2. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-10-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 38 ++++++++++-------------- + 1 file changed, 16 insertions(+), 22 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -531,25 +531,6 @@ static int airoha_fe_init(struct airoha_ + /* disable IFC by default */ + airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); + +- airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), +- FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) | +- FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1)); +- airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1), +- FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) | +- FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2)); +- + /* enable 1:N vlan action, init vlan table */ + airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); + +@@ -1782,8 +1763,10 @@ static void airhoha_set_gdm2_loopback(st + static int airoha_dev_init(struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->qdma->eth; +- u32 pse_port; ++ struct airoha_qdma *qdma = port->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ u32 pse_port, fe_cpu_port; ++ u8 ppe_id; + + airoha_set_macaddr(port, dev->dev_addr); + +@@ -1796,16 +1779,27 @@ static int airoha_dev_init(struct net_de + fallthrough; + case 2: + if (airoha_ppe_is_enabled(eth, 1)) { ++ /* For PPE2 always use secondary cpu port. */ ++ fe_cpu_port = FE_PSE_PORT_CDM2; + pse_port = FE_PSE_PORT_PPE2; + break; + } + fallthrough; +- default: ++ default: { ++ u8 qdma_id = qdma - ð->qdma[0]; ++ ++ /* For PPE1 select cpu port according to the running QDMA. */ ++ fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1; + pse_port = FE_PSE_PORT_PPE1; + break; + } ++ } + + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port); ++ ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0; ++ airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id), ++ DFT_CPORT_MASK(port->id), ++ fe_cpu_port << __ffs(DFT_CPORT_MASK(port->id))); + + return 0; + } diff --git a/target/linux/airoha/patches-6.12/099-10-v6.19-net-airoha-Refactor-src-port-configuration-in-airhoh.patch b/target/linux/airoha/patches-6.12/099-10-v6.19-net-airoha-Refactor-src-port-configuration-in-airhoh.patch new file mode 100644 index 00000000000..597946f932d --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-10-v6.19-net-airoha-Refactor-src-port-configuration-in-airhoh.patch @@ -0,0 +1,202 @@ +From 9d5b5219f672c80bed4d4e15f0068e648cdca43b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:20 +0200 +Subject: [PATCH 10/12] net: airoha: Refactor src port configuration in + airhoha_set_gdm2_loopback + +AN7583 chipset relies on different definitions for source-port +identifier used for hw offloading. In order to support hw offloading +in AN7583 controller, refactor src port configuration in +airhoha_set_gdm2_loopback routine and introduce get_src_port_id +callback. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-11-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 82 ++++++++++++++++------- + drivers/net/ethernet/airoha/airoha_eth.h | 18 +++-- + drivers/net/ethernet/airoha/airoha_regs.h | 6 +- + 3 files changed, 73 insertions(+), 33 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1708,13 +1708,17 @@ static int airoha_dev_set_macaddr(struct + return 0; + } + +-static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) ++static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + { +- u32 pse_port = port->id == 3 ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4; ++ u32 val, pse_port, chan = port->id == AIROHA_GDM3_IDX ? 4 : 0; + struct airoha_eth *eth = port->qdma->eth; +- u32 chan = port->id == 3 ? 4 : 0; ++ /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ ++ u32 nbq = port->id == AIROHA_GDM3_IDX ? 4 : 0; ++ int src_port; + + /* Forward the traffic to the proper GDM port */ ++ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 ++ : FE_PSE_PORT_GDM4; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port); + airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC); + +@@ -1735,29 +1739,25 @@ static void airhoha_set_gdm2_loopback(st + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2)); + airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2)); + +- if (port->id == 3) { +- /* FIXME: handle XSI_PCE1_PORT */ +- airoha_fe_rmw(eth, REG_FE_WAN_PORT, +- WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, +- FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT)); +- airoha_fe_rmw(eth, +- REG_SP_DFT_CPORT(HSGMII_LAN_PCIE0_SRCPORT >> 3), +- SP_CPORT_PCIE0_MASK, +- FIELD_PREP(SP_CPORT_PCIE0_MASK, +- FE_PSE_PORT_CDM2)); +- } else { +- /* FIXME: handle XSI_USB_PORT */ ++ src_port = eth->soc->ops.get_src_port_id(port, nbq); ++ if (src_port < 0) ++ return src_port; ++ ++ airoha_fe_rmw(eth, REG_FE_WAN_PORT, ++ WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, ++ FIELD_PREP(WAN0_MASK, src_port)); ++ val = src_port & SP_CPORT_DFT_MASK; ++ airoha_fe_rmw(eth, ++ REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)), ++ SP_CPORT_MASK(val), ++ FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val))); ++ ++ if (port->id != AIROHA_GDM3_IDX) + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, + FC_ID_OF_SRC_PORT24_MASK, + FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2)); +- airoha_fe_rmw(eth, REG_FE_WAN_PORT, +- WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, +- FIELD_PREP(WAN0_MASK, HSGMII_LAN_ETH_SRCPORT)); +- airoha_fe_rmw(eth, +- REG_SP_DFT_CPORT(HSGMII_LAN_ETH_SRCPORT >> 3), +- SP_CPORT_ETH_MASK, +- FIELD_PREP(SP_CPORT_ETH_MASK, FE_PSE_PORT_CDM2)); +- } ++ ++ return 0; + } + + static int airoha_dev_init(struct net_device *dev) +@@ -1774,8 +1774,13 @@ static int airoha_dev_init(struct net_de + case 3: + case 4: + /* If GDM2 is active we can't enable loopback */ +- if (!eth->ports[1]) +- airhoha_set_gdm2_loopback(port); ++ if (!eth->ports[1]) { ++ int err; ++ ++ err = airhoha_set_gdm2_loopback(port); ++ if (err) ++ return err; ++ } + fallthrough; + case 2: + if (airoha_ppe_is_enabled(eth, 1)) { +@@ -3093,11 +3098,38 @@ static const char * const en7581_xsi_rst + "xfp-mac", + }; + ++static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq) ++{ ++ switch (port->id) { ++ case 3: ++ /* 7581 SoC supports PCIe serdes on GDM3 port */ ++ if (nbq == 4) ++ return HSGMII_LAN_7581_PCIE0_SRCPORT; ++ if (nbq == 5) ++ return HSGMII_LAN_7581_PCIE1_SRCPORT; ++ break; ++ case 4: ++ /* 7581 SoC supports eth and usb serdes on GDM4 port */ ++ if (!nbq) ++ return HSGMII_LAN_7581_ETH_SRCPORT; ++ if (nbq == 1) ++ return HSGMII_LAN_7581_USB_SRCPORT; ++ break; ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static const struct airoha_eth_soc_data en7581_soc_data = { + .version = 0x7581, + .xsi_rsts_names = en7581_xsi_rsts_names, + .num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names), + .num_ppe = 2, ++ .ops = { ++ .get_src_port_id = airoha_en7581_get_src_port_id, ++ }, + }; + + static const struct of_device_id of_airoha_match[] = { +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -67,10 +67,10 @@ enum { + }; + + enum { +- HSGMII_LAN_PCIE0_SRCPORT = 0x16, +- HSGMII_LAN_PCIE1_SRCPORT, +- HSGMII_LAN_ETH_SRCPORT, +- HSGMII_LAN_USB_SRCPORT, ++ HSGMII_LAN_7581_PCIE0_SRCPORT = 0x16, ++ HSGMII_LAN_7581_PCIE1_SRCPORT, ++ HSGMII_LAN_7581_ETH_SRCPORT, ++ HSGMII_LAN_7581_USB_SRCPORT, + }; + + enum { +@@ -99,6 +99,13 @@ enum { + CRSN_25 = 0x19, + }; + ++enum airoha_gdm_index { ++ AIROHA_GDM1_IDX = 1, ++ AIROHA_GDM2_IDX = 2, ++ AIROHA_GDM3_IDX = 3, ++ AIROHA_GDM4_IDX = 4, ++}; ++ + enum { + FE_PSE_PORT_CDM1, + FE_PSE_PORT_GDM1, +@@ -555,6 +562,9 @@ struct airoha_eth_soc_data { + const char * const *xsi_rsts_names; + int num_xsi_rsts; + int num_ppe; ++ struct { ++ int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq); ++ } ops; + }; + + struct airoha_eth { +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -383,10 +383,8 @@ + #define REG_MC_VLAN_DATA 0x2108 + + #define REG_SP_DFT_CPORT(_n) (0x20e0 + ((_n) << 2)) +-#define SP_CPORT_PCIE1_MASK GENMASK(31, 28) +-#define SP_CPORT_PCIE0_MASK GENMASK(27, 24) +-#define SP_CPORT_USB_MASK GENMASK(7, 4) +-#define SP_CPORT_ETH_MASK GENMASK(7, 4) ++#define SP_CPORT_DFT_MASK GENMASK(2, 0) ++#define SP_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2)) + + #define REG_SRC_PORT_FC_MAP6 0x2298 + #define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24) diff --git a/target/linux/airoha/patches-6.12/099-11-v6.19-net-airoha-ppe-Do-not-use-magic-numbers-in-airoha_pp.patch b/target/linux/airoha/patches-6.12/099-11-v6.19-net-airoha-ppe-Do-not-use-magic-numbers-in-airoha_pp.patch new file mode 100644 index 00000000000..93e7f5e592c --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-11-v6.19-net-airoha-ppe-Do-not-use-magic-numbers-in-airoha_pp.patch @@ -0,0 +1,29 @@ +From 63f283d36b1fb06b55ae609a1f679544f5f66057 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:21 +0200 +Subject: [PATCH 11/12] net: airoha: ppe: Do not use magic numbers in + airoha_ppe_foe_get_entry_locked() + +Explicit the size of entries pointed by hwe pointer in +airoha_ppe_foe_get_entry_locked routine + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-12-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -619,7 +619,8 @@ airoha_ppe_foe_get_entry_locked(struct a + REG_PPE_RAM_CTRL(ppe2))) + return NULL; + +- for (i = 0; i < sizeof(struct airoha_foe_entry) / 4; i++) ++ for (i = 0; i < sizeof(struct airoha_foe_entry) / sizeof(*hwe); ++ i++) + hwe[i] = airoha_fe_rr(eth, + REG_PPE_RAM_ENTRY(ppe2, i)); + } diff --git a/target/linux/airoha/patches-6.12/099-12-v6.19-net-airoha-Add-AN7583-SoC-support.patch b/target/linux/airoha/patches-6.12/099-12-v6.19-net-airoha-Add-AN7583-SoC-support.patch new file mode 100644 index 00000000000..b9812ef455b --- /dev/null +++ b/target/linux/airoha/patches-6.12/099-12-v6.19-net-airoha-Add-AN7583-SoC-support.patch @@ -0,0 +1,185 @@ +From e4e5ce823bdd4601bd75ae7c206ae35e7c2fa60b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 11:06:22 +0200 +Subject: [PATCH 12/12] net: airoha: Add AN7583 SoC support + +Introduce support for AN7583 ethernet controller to airoha-eth dirver. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-13-f28319666667@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 68 ++++++++++++++++++++++-- + drivers/net/ethernet/airoha/airoha_eth.h | 11 ++++ + drivers/net/ethernet/airoha/airoha_ppe.c | 3 ++ + 3 files changed, 77 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1710,10 +1710,8 @@ static int airoha_dev_set_macaddr(struct + + static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + { +- u32 val, pse_port, chan = port->id == AIROHA_GDM3_IDX ? 4 : 0; + struct airoha_eth *eth = port->qdma->eth; +- /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ +- u32 nbq = port->id == AIROHA_GDM3_IDX ? 4 : 0; ++ u32 val, pse_port, chan, nbq; + int src_port; + + /* Forward the traffic to the proper GDM port */ +@@ -1725,6 +1723,8 @@ static int airhoha_set_gdm2_loopback(str + /* Enable GDM2 loopback */ + airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff); + airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff); ++ ++ chan = port->id == AIROHA_GDM3_IDX ? airoha_is_7581(eth) ? 4 : 3 : 0; + airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2), + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK, + FIELD_PREP(LPBK_CHAN_MASK, chan) | +@@ -1739,6 +1739,8 @@ static int airhoha_set_gdm2_loopback(str + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2)); + airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2)); + ++ /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ ++ nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + src_port = eth->soc->ops.get_src_port_id(port, nbq); + if (src_port < 0) + return src_port; +@@ -1752,7 +1754,7 @@ static int airhoha_set_gdm2_loopback(str + SP_CPORT_MASK(val), + FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val))); + +- if (port->id != AIROHA_GDM3_IDX) ++ if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth)) + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, + FC_ID_OF_SRC_PORT24_MASK, + FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2)); +@@ -1921,6 +1923,22 @@ static bool airoha_dev_tx_queue_busy(str + return index >= tail; + } + ++static int airoha_get_fe_port(struct airoha_gdm_port *port) ++{ ++ struct airoha_qdma *qdma = port->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ ++ switch (eth->soc->version) { ++ case 0x7583: ++ return port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 ++ : port->id; ++ case 0x7581: ++ default: ++ return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4 ++ : port->id; ++ } ++} ++ + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, + struct net_device *dev) + { +@@ -1961,7 +1979,7 @@ static netdev_tx_t airoha_dev_xmit(struc + } + } + +- fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; ++ fport = airoha_get_fe_port(port); + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + +@@ -3122,6 +3140,35 @@ static int airoha_en7581_get_src_port_id + return -EINVAL; + } + ++static const char * const an7583_xsi_rsts_names[] = { ++ "xsi-mac", ++ "hsi0-mac", ++ "hsi1-mac", ++ "xfp-mac", ++}; ++ ++static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq) ++{ ++ switch (port->id) { ++ case 3: ++ /* 7583 SoC supports eth serdes on GDM3 port */ ++ if (!nbq) ++ return HSGMII_LAN_7583_ETH_SRCPORT; ++ break; ++ case 4: ++ /* 7583 SoC supports PCIe and USB serdes on GDM4 port */ ++ if (!nbq) ++ return HSGMII_LAN_7583_PCIE_SRCPORT; ++ if (nbq == 1) ++ return HSGMII_LAN_7583_USB_SRCPORT; ++ break; ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static const struct airoha_eth_soc_data en7581_soc_data = { + .version = 0x7581, + .xsi_rsts_names = en7581_xsi_rsts_names, +@@ -3132,8 +3179,19 @@ static const struct airoha_eth_soc_data + }, + }; + ++static const struct airoha_eth_soc_data an7583_soc_data = { ++ .version = 0x7583, ++ .xsi_rsts_names = an7583_xsi_rsts_names, ++ .num_xsi_rsts = ARRAY_SIZE(an7583_xsi_rsts_names), ++ .num_ppe = 1, ++ .ops = { ++ .get_src_port_id = airoha_an7583_get_src_port_id, ++ }, ++}; ++ + static const struct of_device_id of_airoha_match[] = { + { .compatible = "airoha,en7581-eth", .data = &en7581_soc_data }, ++ { .compatible = "airoha,an7583-eth", .data = &an7583_soc_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, of_airoha_match); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -74,6 +74,12 @@ enum { + }; + + enum { ++ HSGMII_LAN_7583_ETH_SRCPORT = 0x16, ++ HSGMII_LAN_7583_PCIE_SRCPORT = 0x18, ++ HSGMII_LAN_7583_USB_SRCPORT, ++}; ++ ++enum { + XSI_PCIE0_VIP_PORT_MASK = BIT(22), + XSI_PCIE1_VIP_PORT_MASK = BIT(23), + XSI_USB_VIP_PORT_MASK = BIT(25), +@@ -629,6 +635,11 @@ static inline bool airoha_is_7581(struct + return eth->soc->version == 0x7581; + } + ++static inline bool airoha_is_7583(struct airoha_eth *eth) ++{ ++ return eth->soc->version == 0x7583; ++} ++ + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -37,6 +37,9 @@ static int airoha_ppe_get_num_stats_entr + if (!IS_ENABLED(CONFIG_NET_AIROHA_FLOW_STATS)) + return -EOPNOTSUPP; + ++ if (airoha_is_7583(ppe->eth)) ++ return -EOPNOTSUPP; ++ + return PPE_STATS_NUM_ENTRIES; + } + diff --git a/target/linux/airoha/patches-6.12/100-v6.17-net-airoha-npu-Add-missing-MODULE_FIRMWARE-macros.patch b/target/linux/airoha/patches-6.12/100-v6.17-net-airoha-npu-Add-missing-MODULE_FIRMWARE-macros.patch new file mode 100644 index 00000000000..7e86417f9de --- /dev/null +++ b/target/linux/airoha/patches-6.12/100-v6.17-net-airoha-npu-Add-missing-MODULE_FIRMWARE-macros.patch @@ -0,0 +1,27 @@ +From 4e7e471e2e3f9085fe1dbe821c4dd904a917c66a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 1 Aug 2025 09:12:25 +0200 +Subject: [PATCH] net: airoha: npu: Add missing MODULE_FIRMWARE macros + +Introduce missing MODULE_FIRMWARE definitions for firmware autoload. + +Fixes: 23290c7bc190d ("net: airoha: Introduce Airoha NPU support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250801-airoha-npu-missing-module-firmware-v2-1-e860c824d515@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -741,6 +741,8 @@ static struct platform_driver airoha_npu + }; + module_platform_driver(airoha_npu_driver); + ++MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_DATA); ++MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_RV32); + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Lorenzo Bianconi "); + MODULE_DESCRIPTION("Airoha Network Processor Unit driver"); diff --git a/target/linux/airoha/patches-6.12/101-v6.17-net-airoha-Fix-a-NULL-vs-IS_ERR-bug-in-airoha_npu_ru.patch b/target/linux/airoha/patches-6.12/101-v6.17-net-airoha-Fix-a-NULL-vs-IS_ERR-bug-in-airoha_npu_ru.patch new file mode 100644 index 00000000000..71453fd57fd --- /dev/null +++ b/target/linux/airoha/patches-6.12/101-v6.17-net-airoha-Fix-a-NULL-vs-IS_ERR-bug-in-airoha_npu_ru.patch @@ -0,0 +1,31 @@ +From 1e5e40f2558c07f6bc60a8983000309cc0a9d600 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Tue, 15 Jul 2025 18:01:10 -0500 +Subject: [PATCH] net: airoha: Fix a NULL vs IS_ERR() bug in + airoha_npu_run_firmware() + +The devm_ioremap_resource() function returns error pointers. It never +returns NULL. Update the check to match. + +Fixes: e27dba1951ce ("net: Use of_reserved_mem_region_to_resource{_byname}() for "memory-region"") +Signed-off-by: Dan Carpenter +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/fc6d194e-6bf5-49ca-bc77-3fdfda62c434@sabinyo.mountain +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -201,8 +201,8 @@ static int airoha_npu_run_firmware(struc + } + + addr = devm_ioremap(dev, rmem->base, rmem->size); +- if (!addr) { +- ret = -ENOMEM; ++ if (IS_ERR(addr)) { ++ ret = PTR_ERR(addr); + goto out; + } + diff --git a/target/linux/airoha/patches-6.12/102-02-v6.19-net-airoha-npu-Add-airoha_npu_soc_data-struct.patch b/target/linux/airoha/patches-6.12/102-02-v6.19-net-airoha-npu-Add-airoha_npu_soc_data-struct.patch new file mode 100644 index 00000000000..09972a9b629 --- /dev/null +++ b/target/linux/airoha/patches-6.12/102-02-v6.19-net-airoha-npu-Add-airoha_npu_soc_data-struct.patch @@ -0,0 +1,136 @@ +From 0850ae496d534847ec2c26744521c1bce04ec59d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 13 Oct 2025 15:58:50 +0200 +Subject: [PATCH 2/3] net: airoha: npu: Add airoha_npu_soc_data struct + +Introduce airoha_npu_soc_data structure in order to generalize per-SoC +NPU firmware info. Introduce airoha_npu_load_firmware utility routine. +This is a preliminary patch in order to introduce AN7583 NPU support. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251013-airoha-npu-7583-v3-2-00f748b5a0c7@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 77 ++++++++++++++++-------- + 1 file changed, 51 insertions(+), 26 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -103,6 +103,16 @@ enum { + QDMA_WAN_PON_XDSL, + }; + ++struct airoha_npu_fw { ++ const char *name; ++ int max_size; ++}; ++ ++struct airoha_npu_soc_data { ++ struct airoha_npu_fw fw_rv32; ++ struct airoha_npu_fw fw_data; ++}; ++ + #define MBOX_MSG_FUNC_ID GENMASK(14, 11) + #define MBOX_MSG_STATIC_BUF BIT(5) + #define MBOX_MSG_STATUS GENMASK(4, 2) +@@ -182,49 +192,53 @@ static int airoha_npu_send_msg(struct ai + return ret; + } + +-static int airoha_npu_run_firmware(struct device *dev, void __iomem *base, +- struct reserved_mem *rmem) ++static int airoha_npu_load_firmware(struct device *dev, void __iomem *addr, ++ const struct airoha_npu_fw *fw_info) + { + const struct firmware *fw; +- void __iomem *addr; + int ret; + +- ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_RV32, dev); ++ ret = request_firmware(&fw, fw_info->name, dev); + if (ret) + return ret == -ENOENT ? -EPROBE_DEFER : ret; + +- if (fw->size > NPU_EN7581_FIRMWARE_RV32_MAX_SIZE) { ++ if (fw->size > fw_info->max_size) { + dev_err(dev, "%s: fw size too overlimit (%zu)\n", +- NPU_EN7581_FIRMWARE_RV32, fw->size); ++ fw_info->name, fw->size); + ret = -E2BIG; + goto out; + } + +- addr = devm_ioremap(dev, rmem->base, rmem->size); +- if (IS_ERR(addr)) { +- ret = PTR_ERR(addr); +- goto out; +- } +- + memcpy_toio(addr, fw->data, fw->size); ++out: + release_firmware(fw); + +- ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_DATA, dev); +- if (ret) +- return ret == -ENOENT ? -EPROBE_DEFER : ret; ++ return ret; ++} + +- if (fw->size > NPU_EN7581_FIRMWARE_DATA_MAX_SIZE) { +- dev_err(dev, "%s: fw size too overlimit (%zu)\n", +- NPU_EN7581_FIRMWARE_DATA, fw->size); +- ret = -E2BIG; +- goto out; +- } ++static int airoha_npu_run_firmware(struct device *dev, void __iomem *base, ++ struct reserved_mem *rmem) ++{ ++ const struct airoha_npu_soc_data *soc; ++ void __iomem *addr; ++ int ret; + +- memcpy_toio(base + REG_NPU_LOCAL_SRAM, fw->data, fw->size); +-out: +- release_firmware(fw); ++ soc = of_device_get_match_data(dev); ++ if (!soc) ++ return -EINVAL; + +- return ret; ++ addr = devm_ioremap(dev, rmem->base, rmem->size); ++ if (IS_ERR(addr)) ++ return PTR_ERR(addr); ++ ++ /* Load rv32 npu firmware */ ++ ret = airoha_npu_load_firmware(dev, addr, &soc->fw_rv32); ++ if (ret) ++ return ret; ++ ++ /* Load data npu firmware */ ++ return airoha_npu_load_firmware(dev, base + REG_NPU_LOCAL_SRAM, ++ &soc->fw_data); + } + + static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance) +@@ -596,8 +610,19 @@ void airoha_npu_put(struct airoha_npu *n + } + EXPORT_SYMBOL_GPL(airoha_npu_put); + ++static const struct airoha_npu_soc_data en7581_npu_soc_data = { ++ .fw_rv32 = { ++ .name = NPU_EN7581_FIRMWARE_RV32, ++ .max_size = NPU_EN7581_FIRMWARE_RV32_MAX_SIZE, ++ }, ++ .fw_data = { ++ .name = NPU_EN7581_FIRMWARE_DATA, ++ .max_size = NPU_EN7581_FIRMWARE_DATA_MAX_SIZE, ++ }, ++}; ++ + static const struct of_device_id of_airoha_npu_match[] = { +- { .compatible = "airoha,en7581-npu" }, ++ { .compatible = "airoha,en7581-npu", .data = &en7581_npu_soc_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, of_airoha_npu_match); diff --git a/target/linux/airoha/patches-6.12/102-03-v6.19-net-airoha-npu-Add-7583-SoC-support.patch b/target/linux/airoha/patches-6.12/102-03-v6.19-net-airoha-npu-Add-7583-SoC-support.patch new file mode 100644 index 00000000000..a628d09b779 --- /dev/null +++ b/target/linux/airoha/patches-6.12/102-03-v6.19-net-airoha-npu-Add-7583-SoC-support.patch @@ -0,0 +1,56 @@ +From 4478596f71d92060c9093bdf1d2d940881f41bcc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 13 Oct 2025 15:58:51 +0200 +Subject: [PATCH 3/3] net: airoha: npu: Add 7583 SoC support + +Introduce support for Airoha 7583 SoC NPU selecting proper firmware images. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251013-airoha-npu-7583-v3-3-00f748b5a0c7@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -16,6 +16,8 @@ + + #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" + #define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin" ++#define NPU_AN7583_FIRMWARE_DATA "airoha/an7583_npu_data.bin" ++#define NPU_AN7583_FIRMWARE_RV32 "airoha/an7583_npu_rv32.bin" + #define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE 0x200000 + #define NPU_EN7581_FIRMWARE_DATA_MAX_SIZE 0x10000 + #define NPU_DUMP_SIZE 512 +@@ -621,8 +623,20 @@ static const struct airoha_npu_soc_data + }, + }; + ++static const struct airoha_npu_soc_data an7583_npu_soc_data = { ++ .fw_rv32 = { ++ .name = NPU_AN7583_FIRMWARE_RV32, ++ .max_size = NPU_EN7581_FIRMWARE_RV32_MAX_SIZE, ++ }, ++ .fw_data = { ++ .name = NPU_AN7583_FIRMWARE_DATA, ++ .max_size = NPU_EN7581_FIRMWARE_DATA_MAX_SIZE, ++ }, ++}; ++ + static const struct of_device_id of_airoha_npu_match[] = { + { .compatible = "airoha,en7581-npu", .data = &en7581_npu_soc_data }, ++ { .compatible = "airoha,an7583-npu", .data = &an7583_npu_soc_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, of_airoha_npu_match); +@@ -768,6 +782,8 @@ module_platform_driver(airoha_npu_driver + + MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_DATA); + MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_RV32); ++MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_DATA); ++MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_RV32); + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Lorenzo Bianconi "); + MODULE_DESCRIPTION("Airoha Network Processor Unit driver"); diff --git a/target/linux/airoha/patches-6.12/103-v6.19-net-airoha-Remove-code-duplication-in-airoha_regs.h.patch b/target/linux/airoha/patches-6.12/103-v6.19-net-airoha-Remove-code-duplication-in-airoha_regs.h.patch new file mode 100644 index 00000000000..036144280f5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/103-v6.19-net-airoha-Remove-code-duplication-in-airoha_regs.h.patch @@ -0,0 +1,342 @@ +From 99ad2b6815f41acbec15ab051ccc79b11b05710a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 22 Oct 2025 09:11:12 +0200 +Subject: [PATCH] net: airoha: Remove code duplication in airoha_regs.h + +This patch does not introduce any logical change, it just removes +duplicated code in airoha_regs.h. +Fix naming conventions in airoha_regs.h. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251022-airoha-regs-cosmetics-v2-1-e0425b3f2c2c@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 102 ++++++++++---------- + drivers/net/ethernet/airoha/airoha_regs.h | 109 ++++++++++------------ + 2 files changed, 100 insertions(+), 111 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -137,11 +137,11 @@ static void airoha_fe_maccr_init(struct + + for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) + airoha_fe_set(eth, REG_GDM_FWD_CFG(p), +- GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | +- GDM_DROP_CRC_ERR); ++ GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK | ++ GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK); + +- airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, +- FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); ++ airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK, ++ FIELD_PREP(CDM_VLAN_MASK, 0x8100)); + + airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD); + } +@@ -403,46 +403,46 @@ static int airoha_fe_mc_vlan_clear(struc + static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth) + { + /* CDM1_CRSN_QSEL */ +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_22 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_22), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_22), + CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_08 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_08), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_08), + CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_21 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_21), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_21), + CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_24 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_24), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_24), + CDM_CRSN_QSEL_Q6)); +- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2), +- CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), +- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_25 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_25), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_25), + CDM_CRSN_QSEL_Q1)); + /* CDM2_CRSN_QSEL */ +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_08 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_08), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_08), + CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_21 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_21), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_21), + CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_22 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_22), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_22), + CDM_CRSN_QSEL_Q1)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_24 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_24), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_24), + CDM_CRSN_QSEL_Q6)); +- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2), +- CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), +- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25), ++ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_25 >> 2), ++ CDM_CRSN_QSEL_REASON_MASK(CRSN_25), ++ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_25), + CDM_CRSN_QSEL_Q1)); + } + +@@ -462,18 +462,18 @@ static int airoha_fe_init(struct airoha_ + airoha_fe_wr(eth, REG_FE_PCE_CFG, + PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK); + /* set vip queue selection to ring 1 */ +- airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK, +- FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4)); +- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK, +- FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4)); ++ airoha_fe_rmw(eth, REG_CDM_FWD_CFG(1), CDM_VIP_QSEL_MASK, ++ FIELD_PREP(CDM_VIP_QSEL_MASK, 0x4)); ++ airoha_fe_rmw(eth, REG_CDM_FWD_CFG(2), CDM_VIP_QSEL_MASK, ++ FIELD_PREP(CDM_VIP_QSEL_MASK, 0x4)); + /* set GDM4 source interface offset to 8 */ +- airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET, +- GDM4_SPORT_OFF2_MASK | +- GDM4_SPORT_OFF1_MASK | +- GDM4_SPORT_OFF0_MASK, +- FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) | +- FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) | +- FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8)); ++ airoha_fe_rmw(eth, REG_GDM_SRC_PORT_SET(4), ++ GDM_SPORT_OFF2_MASK | ++ GDM_SPORT_OFF1_MASK | ++ GDM_SPORT_OFF0_MASK, ++ FIELD_PREP(GDM_SPORT_OFF2_MASK, 8) | ++ FIELD_PREP(GDM_SPORT_OFF1_MASK, 8) | ++ FIELD_PREP(GDM_SPORT_OFF0_MASK, 8)); + + /* set PSE Page as 128B */ + airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG, +@@ -499,8 +499,8 @@ static int airoha_fe_init(struct airoha_ + airoha_fe_set(eth, REG_GDM_MISC_CFG, + GDM2_RDM_ACK_WAIT_PREF_MASK | + GDM2_CHN_VLD_MODE_MASK); +- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK, +- FIELD_PREP(CDM2_OAM_QSEL_MASK, 15)); ++ airoha_fe_rmw(eth, REG_CDM_FWD_CFG(2), CDM_OAM_QSEL_MASK, ++ FIELD_PREP(CDM_OAM_QSEL_MASK, 15)); + + /* init fragment and assemble Force Port */ + /* NPU Core-3, NPU Bridge Channel-3 */ +@@ -514,8 +514,8 @@ static int airoha_fe_init(struct airoha_ + FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | + FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); + +- airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK); +- airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(3), GDM_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(4), GDM_PAD_EN_MASK); + + airoha_fe_crsn_qsel_init(eth); + +@@ -523,7 +523,7 @@ static int airoha_fe_init(struct airoha_ + airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK); + + /* default aging mode for mbi unlock issue */ +- airoha_fe_rmw(eth, REG_GDM2_CHN_RLS, ++ airoha_fe_rmw(eth, REG_GDM_CHN_RLS(2), + MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK, + FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) | + FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3)); +@@ -1718,7 +1718,7 @@ static int airhoha_set_gdm2_loopback(str + pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 + : FE_PSE_PORT_GDM4; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port); +- airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC); ++ airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC_MASK); + + /* Enable GDM2 loopback */ + airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff); +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -23,6 +23,8 @@ + #define GDM3_BASE 0x1100 + #define GDM4_BASE 0x2500 + ++#define CDM_BASE(_n) \ ++ ((_n) == 2 ? CDM2_BASE : CDM1_BASE) + #define GDM_BASE(_n) \ + ((_n) == 4 ? GDM4_BASE : \ + (_n) == 3 ? GDM3_BASE : \ +@@ -109,30 +111,24 @@ + #define PATN_DP_MASK GENMASK(31, 16) + #define PATN_SP_MASK GENMASK(15, 0) + +-#define REG_CDM1_VLAN_CTRL CDM1_BASE +-#define CDM1_VLAN_MASK GENMASK(31, 16) ++#define REG_CDM_VLAN_CTRL(_n) CDM_BASE(_n) ++#define CDM_VLAN_MASK GENMASK(31, 16) + +-#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08) +-#define CDM1_VIP_QSEL_MASK GENMASK(24, 20) ++#define REG_CDM_FWD_CFG(_n) (CDM_BASE(_n) + 0x08) ++#define CDM_OAM_QSEL_MASK GENMASK(31, 27) ++#define CDM_VIP_QSEL_MASK GENMASK(24, 20) + +-#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2)) +-#define CDM1_CRSN_QSEL_REASON_MASK(_n) \ +- GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) +- +-#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08) +-#define CDM2_OAM_QSEL_MASK GENMASK(31, 27) +-#define CDM2_VIP_QSEL_MASK GENMASK(24, 20) +- +-#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2)) +-#define CDM2_CRSN_QSEL_REASON_MASK(_n) \ ++#define REG_CDM_CRSN_QSEL(_n, _m) (CDM_BASE(_n) + 0x10 + ((_m) << 2)) ++#define CDM_CRSN_QSEL_REASON_MASK(_n) \ + GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) + + #define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) +-#define GDM_DROP_CRC_ERR BIT(23) +-#define GDM_IP4_CKSUM BIT(22) +-#define GDM_TCP_CKSUM BIT(21) +-#define GDM_UDP_CKSUM BIT(20) +-#define GDM_STRIP_CRC BIT(16) ++#define GDM_PAD_EN_MASK BIT(28) ++#define GDM_DROP_CRC_ERR_MASK BIT(23) ++#define GDM_IP4_CKSUM_MASK BIT(22) ++#define GDM_TCP_CKSUM_MASK BIT(21) ++#define GDM_UDP_CKSUM_MASK BIT(20) ++#define GDM_STRIP_CRC_MASK BIT(16) + #define GDM_UCFQ_MASK GENMASK(15, 12) + #define GDM_BCFQ_MASK GENMASK(11, 8) + #define GDM_MCFQ_MASK GENMASK(7, 4) +@@ -156,6 +152,10 @@ + #define LBK_CHAN_MODE_MASK BIT(1) + #define LPBK_EN_MASK BIT(0) + ++#define REG_GDM_CHN_RLS(_n) (GDM_BASE(_n) + 0x20) ++#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) ++#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) ++ + #define REG_GDM_TXCHN_EN(_n) (GDM_BASE(_n) + 0x24) + #define REG_GDM_RXCHN_EN(_n) (GDM_BASE(_n) + 0x28) + +@@ -168,10 +168,10 @@ + #define FE_GDM_MIB_RX_CLEAR_MASK BIT(1) + #define FE_GDM_MIB_TX_CLEAR_MASK BIT(0) + +-#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4) ++#define REG_FE_GDM_MIB_CFG(_n) (GDM_BASE(_n) + 0xf4) + #define FE_STRICT_RFC2819_MODE_MASK BIT(31) +-#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17) +-#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16) ++#define FE_GDM_TX_MIB_SPLIT_EN_MASK BIT(17) ++#define FE_GDM_RX_MIB_SPLIT_EN_MASK BIT(16) + #define FE_TX_MIB_ID_MASK GENMASK(15, 8) + #define FE_RX_MIB_ID_MASK GENMASK(7, 0) + +@@ -214,6 +214,33 @@ + #define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198) + #define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c) + ++#define REG_GDM_SRC_PORT_SET(_n) (GDM_BASE(_n) + 0x23c) ++#define GDM_SPORT_OFF2_MASK GENMASK(19, 16) ++#define GDM_SPORT_OFF1_MASK GENMASK(15, 12) ++#define GDM_SPORT_OFF0_MASK GENMASK(11, 8) ++ ++#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) ++#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) ++#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) ++#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) ++ ++#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) ++#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) ++#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) ++#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) ++#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) ++#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) ++#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) ++#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) ++#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) ++#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) ++#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) ++#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) ++#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) ++#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) ++#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) ++#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) ++ + #define REG_PPE_GLO_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x200) + #define PPE_GLO_CFG_BUSY_MASK BIT(31) + #define PPE_GLO_CFG_FLOW_DROP_UPDATE_MASK BIT(9) +@@ -326,44 +353,6 @@ + + #define REG_UPDMEM_DATA(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x374) + +-#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) +-#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) +-#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) +-#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c) +- +-#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290) +-#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294) +-#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298) +-#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c) +-#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8) +-#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc) +-#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0) +-#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4) +-#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8) +-#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc) +-#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8) +-#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec) +-#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0) +-#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4) +-#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8) +-#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc) +- +-#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20) +-#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25) +-#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17) +- +-#define REG_GDM3_FWD_CFG GDM3_BASE +-#define GDM3_PAD_EN_MASK BIT(28) +- +-#define REG_GDM4_FWD_CFG GDM4_BASE +-#define GDM4_PAD_EN_MASK BIT(28) +-#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8) +- +-#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x23c) +-#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16) +-#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12) +-#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8) +- + #define REG_IP_FRAG_FP 0x2010 + #define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21) + #define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16) diff --git a/target/linux/airoha/patches-6.12/104-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_prob.patch b/target/linux/airoha/patches-6.12/104-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_prob.patch new file mode 100644 index 00000000000..f1172323010 --- /dev/null +++ b/target/linux/airoha/patches-6.12/104-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_prob.patch @@ -0,0 +1,29 @@ +From 3ef07434c7dbfba302df477bb6c70e082965f232 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sat, 5 Jul 2025 10:34:32 +0200 +Subject: [PATCH] net: airoha: Fix an error handling path in airoha_probe() + +If an error occurs after a successful airoha_hw_init() call, +airoha_ppe_deinit() needs to be called as already done in the remove +function. + +Fixes: 00a7678310fe ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Christophe JAILLET +Reviewed-by: Simon Horman +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/1c940851b4fa3c3ed2a142910c821493a136f121.1746715755.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -3065,6 +3065,7 @@ static int airoha_probe(struct platform_ + error_napi_stop: + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_qdma_stop_napi(ð->qdma[i]); ++ airoha_ppe_deinit(eth); + error_hw_cleanup: + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_hw_cleanup(ð->qdma[i]); diff --git a/target/linux/airoha/patches-6.12/104-v6.19-net-airoha-Fix-a-copy-and-paste-bug-in-probe.patch b/target/linux/airoha/patches-6.12/104-v6.19-net-airoha-Fix-a-copy-and-paste-bug-in-probe.patch new file mode 100644 index 00000000000..f27cd14cdaf --- /dev/null +++ b/target/linux/airoha/patches-6.12/104-v6.19-net-airoha-Fix-a-copy-and-paste-bug-in-probe.patch @@ -0,0 +1,37 @@ +From 05e090620bacf317020f9591cfff8926093380bd Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Fri, 24 Oct 2025 14:23:35 +0300 +Subject: [PATCH] net: airoha: Fix a copy and paste bug in probe() + +This code has a copy and paste bug where it accidentally checks "if (err)" +instead of checking if "xsi_rsts" is NULL. Also, as a free bonus, I +changed the allocation from kzalloc() to kcalloc() which is a kernel +hardening measure to protect against integer overflows. + +Fixes: 5863b4e065e2 ("net: airoha: Add airoha_eth_soc_data struct") +Signed-off-by: Dan Carpenter +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/aPtht6y5DRokn9zv@stanley.mountain +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -3011,11 +3011,11 @@ static int airoha_probe(struct platform_ + return err; + } + +- xsi_rsts = devm_kzalloc(eth->dev, +- eth->soc->num_xsi_rsts * sizeof(*xsi_rsts), ++ xsi_rsts = devm_kcalloc(eth->dev, ++ eth->soc->num_xsi_rsts, sizeof(*xsi_rsts), + GFP_KERNEL); +- if (err) +- return err; ++ if (!xsi_rsts) ++ return -ENOMEM; + + eth->xsi_rsts = xsi_rsts; + for (i = 0; i < eth->soc->num_xsi_rsts; i++) diff --git a/target/linux/airoha/patches-6.12/105-v6.17-net-airoha-Get-rid-of-dma_sync_single_for_device-in-.patch b/target/linux/airoha/patches-6.12/105-v6.17-net-airoha-Get-rid-of-dma_sync_single_for_device-in-.patch new file mode 100644 index 00000000000..0c690b0d655 --- /dev/null +++ b/target/linux/airoha/patches-6.12/105-v6.17-net-airoha-Get-rid-of-dma_sync_single_for_device-in-.patch @@ -0,0 +1,40 @@ +From 4cd9d227ab838b3590c4b27e3707b8c3ef14d7e9 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 25 Jun 2025 16:43:15 +0200 +Subject: [PATCH] net: airoha: Get rid of dma_sync_single_for_device() in + airoha_qdma_fill_rx_queue() + +Since the page_pool for airoha_eth driver is created with +PP_FLAG_DMA_SYNC_DEV flag, we do not need to sync_for_device each page +received from the pool since it is already done by the page_pool codebase. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Alexander Lobakin +Link: https://patch.msgid.link/20250625-airoha-sync-for-device-v1-1-923741deaabf@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 5 ----- + 1 file changed, 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -539,9 +539,7 @@ static int airoha_fe_init(struct airoha_ + + static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) + { +- enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); + struct airoha_qdma *qdma = q->qdma; +- struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0]; + int nframes = 0; + +@@ -565,9 +563,6 @@ static int airoha_qdma_fill_rx_queue(str + e->dma_addr = page_pool_get_dma_addr(page) + offset; + e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); + +- dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len, +- dir); +- + val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); + WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); + WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr)); diff --git a/target/linux/airoha/patches-6.12/106-v6.16-net-airoha-fix-potential-use-after-free-in-airoha_np.patch b/target/linux/airoha/patches-6.12/106-v6.16-net-airoha-fix-potential-use-after-free-in-airoha_np.patch new file mode 100644 index 00000000000..251395c751e --- /dev/null +++ b/target/linux/airoha/patches-6.12/106-v6.16-net-airoha-fix-potential-use-after-free-in-airoha_np.patch @@ -0,0 +1,41 @@ +From 3cd582e7d0787506990ef0180405eb6224fa90a6 Mon Sep 17 00:00:00 2001 +From: Alok Tiwari +Date: Tue, 15 Jul 2025 07:30:58 -0700 +Subject: [PATCH] net: airoha: fix potential use-after-free in airoha_npu_get() + +np->name was being used after calling of_node_put(np), which +releases the node and can lead to a use-after-free bug. +Previously, of_node_put(np) was called unconditionally after +of_find_device_by_node(np), which could result in a use-after-free if +pdev is NULL. + +This patch moves of_node_put(np) after the error check to ensure +the node is only released after both the error and success cases +are handled appropriately, preventing potential resource issues. + +Fixes: 23290c7bc190 ("net: airoha: Introduce Airoha NPU support") +Signed-off-by: Alok Tiwari +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250715143102.3458286-1-alok.a.tiwari@oracle.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -567,12 +567,13 @@ struct airoha_npu *airoha_npu_get(struct + return ERR_PTR(-ENODEV); + + pdev = of_find_device_by_node(np); +- of_node_put(np); + + if (!pdev) { + dev_err(dev, "cannot find device node %s\n", np->name); ++ of_node_put(np); + return ERR_PTR(-ENODEV); + } ++ of_node_put(np); + + if (!try_module_get(THIS_MODULE)) { + dev_err(dev, "failed to get the device driver module\n"); diff --git a/target/linux/airoha/patches-6.12/107-v6.19-pwm-airoha-Add-support-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.12/107-v6.19-pwm-airoha-Add-support-for-EN7581-SoC.patch new file mode 100644 index 00000000000..0848cb410e6 --- /dev/null +++ b/target/linux/airoha/patches-6.12/107-v6.19-pwm-airoha-Add-support-for-EN7581-SoC.patch @@ -0,0 +1,689 @@ +From 61d7c2f94d391594de08d8a52a7c2630d2f3d263 Mon Sep 17 00:00:00 2001 +From: Benjamin Larsson +Date: Mon, 13 Oct 2025 12:34:03 +0200 +Subject: [PATCH] pwm: airoha: Add support for EN7581 SoC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce driver for PWM module available on EN7581 SoC. + +Limitations: +- Only 8 concurrent waveform generators are available for 8 combinations of + duty_cycle and period. Waveform generators are shared between 16 GPIO + pins and 17 SIPO GPIO pins. +- Supports only normal polarity. +- On configuration the currently running period is completed. +- Minimum supported period is 4 ms +- Maximum supported period is 1s + +Signed-off-by: Benjamin Larsson +Reviewed-by: AngeloGioacchino Del Regno +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Andy Shevchenko +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20251013103408.14724-1-ansuelsmth@gmail.com +Signed-off-by: Uwe Kleine-König +--- + drivers/pwm/Kconfig | 10 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-airoha.c | 622 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 633 insertions(+) + create mode 100644 drivers/pwm/pwm-airoha.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -54,6 +54,16 @@ config PWM_ADP5585 + This option enables support for the PWM function found in the Analog + Devices ADP5585. + ++config PWM_AIROHA ++ tristate "Airoha PWM support" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ select REGMAP_MMIO ++ help ++ Generic PWM framework driver for Airoha SoC. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-airoha. ++ + config PWM_APPLE + tristate "Apple SoC PWM support" + depends on ARCH_APPLE || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -2,6 +2,7 @@ + obj-$(CONFIG_PWM) += core.o + obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o + obj-$(CONFIG_PWM_ADP5585) += pwm-adp5585.o ++obj-$(CONFIG_PWM_AIROHA) += pwm-airoha.o + obj-$(CONFIG_PWM_APPLE) += pwm-apple.o + obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o + obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o +--- /dev/null ++++ b/drivers/pwm/pwm-airoha.c +@@ -0,0 +1,622 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2022 Markus Gothe ++ * Copyright 2025 Christian Marangi ++ * ++ * Limitations: ++ * - Only 8 concurrent waveform generators are available for 8 combinations of ++ * duty_cycle and period. Waveform generators are shared between 16 GPIO ++ * pins and 17 SIPO GPIO pins. ++ * - Supports only normal polarity. ++ * - On configuration the currently running period is completed. ++ * - Minimum supported period is 4 ms ++ * - Maximum supported period is 1s ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AIROHA_PWM_REG_SGPIO_LED_DATA 0x0024 ++#define AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG BIT(31) ++#define AIROHA_PWM_SGPIO_LED_DATA_DATA GENMASK(16, 0) ++ ++#define AIROHA_PWM_REG_SGPIO_CLK_DIVR 0x0028 ++#define AIROHA_PWM_SGPIO_CLK_DIVR GENMASK(1, 0) ++#define AIROHA_PWM_SGPIO_CLK_DIVR_32 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 3) ++#define AIROHA_PWM_SGPIO_CLK_DIVR_16 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 2) ++#define AIROHA_PWM_SGPIO_CLK_DIVR_8 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 1) ++#define AIROHA_PWM_SGPIO_CLK_DIVR_4 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 0) ++ ++#define AIROHA_PWM_REG_SGPIO_CLK_DLY 0x002c ++ ++#define AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG 0x0030 ++#define AIROHA_PWM_SERIAL_GPIO_FLASH_MODE BIT(1) ++#define AIROHA_PWM_SERIAL_GPIO_MODE_74HC164 BIT(0) ++ ++#define AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(_n) (0x003c + (4 * (_n))) ++#define AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(_n) (16 * (_n)) ++#define AIROHA_PWM_GPIO_FLASH_PRD_LOW GENMASK(15, 8) ++#define AIROHA_PWM_GPIO_FLASH_PRD_HIGH GENMASK(7, 0) ++ ++#define AIROHA_PWM_REG_GPIO_FLASH_MAP(_n) (0x004c + (4 * (_n))) ++#define AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(_n) (4 * (_n)) ++#define AIROHA_PWM_GPIO_FLASH_EN BIT(3) ++#define AIROHA_PWM_GPIO_FLASH_SET_ID GENMASK(2, 0) ++ ++/* Register map is equal to GPIO flash map */ ++#define AIROHA_PWM_REG_SIPO_FLASH_MAP(_n) (0x0054 + (4 * (_n))) ++ ++#define AIROHA_PWM_REG_CYCLE_CFG_VALUE(_n) (0x0098 + (4 * (_n))) ++#define AIROHA_PWM_REG_CYCLE_CFG_SHIFT(_n) (8 * (_n)) ++#define AIROHA_PWM_WAVE_GEN_CYCLE GENMASK(7, 0) ++ ++/* GPIO/SIPO flash map handles 8 pins in one register */ ++#define AIROHA_PWM_PINS_PER_FLASH_MAP 8 ++/* Cycle(Period) registers handles 4 generators in one 32-bit register */ ++#define AIROHA_PWM_BUCKET_PER_CYCLE_CFG 4 ++/* Flash(Duty) producer handles 2 generators in one 32-bit register */ ++#define AIROHA_PWM_BUCKET_PER_FLASH_PROD 2 ++ ++#define AIROHA_PWM_NUM_BUCKETS 8 ++/* ++ * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15. ++ * The SIPO GPIO pins are 17 pins which are mapped into 17 PWM channels, 16-32. ++ * However, we've only got 8 concurrent waveform generators and can therefore ++ * only use up to 8 different combinations of duty cycle and period at a time. ++ */ ++#define AIROHA_PWM_NUM_GPIO 16 ++#define AIROHA_PWM_NUM_SIPO 17 ++#define AIROHA_PWM_MAX_CHANNELS (AIROHA_PWM_NUM_GPIO + AIROHA_PWM_NUM_SIPO) ++ ++struct airoha_pwm_bucket { ++ /* Concurrent access protected by PWM core */ ++ int used; ++ u32 period_ticks; ++ u32 duty_ticks; ++}; ++ ++struct airoha_pwm { ++ struct regmap *regmap; ++ ++ DECLARE_BITMAP(initialized, AIROHA_PWM_MAX_CHANNELS); ++ ++ struct airoha_pwm_bucket buckets[AIROHA_PWM_NUM_BUCKETS]; ++ ++ /* Cache bucket used by each pwm channel */ ++ u8 channel_bucket[AIROHA_PWM_MAX_CHANNELS]; ++}; ++ ++/* The PWM hardware supports periods between 4 ms and 1 s */ ++#define AIROHA_PWM_PERIOD_TICK_NS (4 * NSEC_PER_MSEC) ++#define AIROHA_PWM_PERIOD_MAX_NS (1 * NSEC_PER_SEC) ++/* It is represented internally as 1/250 s between 1 and 250. Unit is ticks. */ ++#define AIROHA_PWM_PERIOD_MIN 1 ++#define AIROHA_PWM_PERIOD_MAX 250 ++/* Duty cycle is relative with 255 corresponding to 100% */ ++#define AIROHA_PWM_DUTY_FULL 255 ++ ++static void airoha_pwm_get_flash_map_addr_and_shift(unsigned int hwpwm, ++ u32 *addr, u32 *shift) ++{ ++ unsigned int offset, hwpwm_bit; ++ ++ if (hwpwm >= AIROHA_PWM_NUM_GPIO) { ++ unsigned int sipohwpwm = hwpwm - AIROHA_PWM_NUM_GPIO; ++ ++ offset = sipohwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP; ++ hwpwm_bit = sipohwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP; ++ ++ /* One FLASH_MAP register handles 8 pins */ ++ *shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit); ++ *addr = AIROHA_PWM_REG_SIPO_FLASH_MAP(offset); ++ } else { ++ offset = hwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP; ++ hwpwm_bit = hwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP; ++ ++ /* One FLASH_MAP register handles 8 pins */ ++ *shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit); ++ *addr = AIROHA_PWM_REG_GPIO_FLASH_MAP(offset); ++ } ++} ++ ++static u32 airoha_pwm_get_period_ticks_from_ns(u32 period_ns) ++{ ++ return period_ns / AIROHA_PWM_PERIOD_TICK_NS; ++} ++ ++static u32 airoha_pwm_get_duty_ticks_from_ns(u32 period_ns, u32 duty_ns) ++{ ++ return mul_u64_u32_div(duty_ns, AIROHA_PWM_DUTY_FULL, period_ns); ++} ++ ++static u32 airoha_pwm_get_period_ns_from_ticks(u32 period_tick) ++{ ++ return period_tick * AIROHA_PWM_PERIOD_TICK_NS; ++} ++ ++static u32 airoha_pwm_get_duty_ns_from_ticks(u32 period_tick, u32 duty_tick) ++{ ++ u32 period_ns = period_tick * AIROHA_PWM_PERIOD_TICK_NS; ++ ++ /* ++ * Overflow can't occur in multiplication as duty_tick is just 8 bit ++ * and period_ns is clamped to AIROHA_PWM_PERIOD_MAX_NS and fit in a ++ * u64. ++ */ ++ return DIV_U64_ROUND_UP(duty_tick * period_ns, AIROHA_PWM_DUTY_FULL); ++} ++ ++static int airoha_pwm_get_bucket(struct airoha_pwm *pc, int bucket, ++ u64 *period_ns, u64 *duty_ns) ++{ ++ struct regmap *map = pc->regmap; ++ u32 period_tick, duty_tick; ++ unsigned int offset; ++ u32 shift, val; ++ int ret; ++ ++ offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG; ++ shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG; ++ shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift); ++ ++ ret = regmap_read(map, AIROHA_PWM_REG_CYCLE_CFG_VALUE(offset), &val); ++ if (ret) ++ return ret; ++ ++ period_tick = FIELD_GET(AIROHA_PWM_WAVE_GEN_CYCLE, val >> shift); ++ *period_ns = airoha_pwm_get_period_ns_from_ticks(period_tick); ++ ++ offset = bucket / AIROHA_PWM_BUCKET_PER_FLASH_PROD; ++ shift = bucket % AIROHA_PWM_BUCKET_PER_FLASH_PROD; ++ shift = AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(shift); ++ ++ ret = regmap_read(map, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset), ++ &val); ++ if (ret) ++ return ret; ++ ++ duty_tick = FIELD_GET(AIROHA_PWM_GPIO_FLASH_PRD_HIGH, val >> shift); ++ *duty_ns = airoha_pwm_get_duty_ns_from_ticks(period_tick, duty_tick); ++ ++ return 0; ++} ++ ++static int airoha_pwm_get_generator(struct airoha_pwm *pc, u32 duty_ticks, ++ u32 period_ticks) ++{ ++ int best = -ENOENT, unused = -ENOENT; ++ u32 duty_ns, best_duty_ns = 0; ++ u32 best_period_ticks = 0; ++ unsigned int i; ++ ++ duty_ns = airoha_pwm_get_duty_ns_from_ticks(period_ticks, duty_ticks); ++ ++ for (i = 0; i < ARRAY_SIZE(pc->buckets); i++) { ++ struct airoha_pwm_bucket *bucket = &pc->buckets[i]; ++ u32 bucket_period_ticks = bucket->period_ticks; ++ u32 bucket_duty_ticks = bucket->duty_ticks; ++ ++ /* If found, save an unused bucket to return it later */ ++ if (!bucket->used) { ++ unused = i; ++ continue; ++ } ++ ++ /* We found a matching bucket, exit early */ ++ if (duty_ticks == bucket_duty_ticks && ++ period_ticks == bucket_period_ticks) ++ return i; ++ ++ /* ++ * Unlike duty cycle zero, which can be handled by ++ * disabling PWM, a generator is needed for full duty ++ * cycle but it can be reused regardless of period ++ */ ++ if (duty_ticks == AIROHA_PWM_DUTY_FULL && ++ bucket_duty_ticks == AIROHA_PWM_DUTY_FULL) ++ return i; ++ ++ /* ++ * With an unused bucket available, skip searching for ++ * a bucket to recycle (closer to the requested period/duty) ++ */ ++ if (unused >= 0) ++ continue; ++ ++ /* Ignore bucket with invalid period */ ++ if (bucket_period_ticks > period_ticks) ++ continue; ++ ++ /* ++ * Search for a bucket closer to the requested period ++ * that has the maximal possible period that isn't bigger ++ * than the requested period. For that period pick the maximal ++ * duty cycle that isn't bigger than the requested duty_cycle. ++ */ ++ if (bucket_period_ticks >= best_period_ticks) { ++ u32 bucket_duty_ns = airoha_pwm_get_duty_ns_from_ticks(bucket_period_ticks, ++ bucket_duty_ticks); ++ ++ /* Skip bucket that goes over the requested duty */ ++ if (bucket_duty_ns > duty_ns) ++ continue; ++ ++ if (bucket_duty_ns > best_duty_ns) { ++ best_period_ticks = bucket_period_ticks; ++ best_duty_ns = bucket_duty_ns; ++ best = i; ++ } ++ } ++ } ++ ++ /* Return an unused bucket or the best one found (if ever) */ ++ return unused >= 0 ? unused : best; ++} ++ ++static void airoha_pwm_release_bucket_config(struct airoha_pwm *pc, ++ unsigned int hwpwm) ++{ ++ int bucket; ++ ++ /* Nothing to clear, PWM channel never used */ ++ if (!test_bit(hwpwm, pc->initialized)) ++ return; ++ ++ bucket = pc->channel_bucket[hwpwm]; ++ pc->buckets[bucket].used--; ++} ++ ++static int airoha_pwm_apply_bucket_config(struct airoha_pwm *pc, unsigned int bucket, ++ u32 duty_ticks, u32 period_ticks) ++{ ++ u32 mask, shift, val; ++ u32 offset; ++ int ret; ++ ++ offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG; ++ shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG; ++ shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift); ++ ++ /* Configure frequency divisor */ ++ mask = AIROHA_PWM_WAVE_GEN_CYCLE << shift; ++ val = FIELD_PREP(AIROHA_PWM_WAVE_GEN_CYCLE, period_ticks) << shift; ++ ret = regmap_update_bits(pc->regmap, AIROHA_PWM_REG_CYCLE_CFG_VALUE(offset), ++ mask, val); ++ if (ret) ++ return ret; ++ ++ offset = bucket / AIROHA_PWM_BUCKET_PER_FLASH_PROD; ++ shift = bucket % AIROHA_PWM_BUCKET_PER_FLASH_PROD; ++ shift = AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(shift); ++ ++ /* Configure duty cycle */ ++ mask = AIROHA_PWM_GPIO_FLASH_PRD_HIGH << shift; ++ val = FIELD_PREP(AIROHA_PWM_GPIO_FLASH_PRD_HIGH, duty_ticks) << shift; ++ ret = regmap_update_bits(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset), ++ mask, val); ++ if (ret) ++ return ret; ++ ++ mask = AIROHA_PWM_GPIO_FLASH_PRD_LOW << shift; ++ val = FIELD_PREP(AIROHA_PWM_GPIO_FLASH_PRD_LOW, ++ AIROHA_PWM_DUTY_FULL - duty_ticks) << shift; ++ return regmap_update_bits(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset), ++ mask, val); ++} ++ ++static int airoha_pwm_consume_generator(struct airoha_pwm *pc, ++ u32 duty_ticks, u32 period_ticks, ++ unsigned int hwpwm) ++{ ++ bool config_bucket = false; ++ int bucket, ret; ++ ++ /* ++ * Search for a bucket that already satisfies duty and period ++ * or an unused one. ++ * If not found, -ENOENT is returned. ++ */ ++ bucket = airoha_pwm_get_generator(pc, duty_ticks, period_ticks); ++ if (bucket < 0) ++ return bucket; ++ ++ /* Release previous used bucket (if any) */ ++ airoha_pwm_release_bucket_config(pc, hwpwm); ++ ++ if (!pc->buckets[bucket].used) ++ config_bucket = true; ++ pc->buckets[bucket].used++; ++ ++ if (config_bucket) { ++ pc->buckets[bucket].period_ticks = period_ticks; ++ pc->buckets[bucket].duty_ticks = duty_ticks; ++ ret = airoha_pwm_apply_bucket_config(pc, bucket, ++ duty_ticks, ++ period_ticks); ++ if (ret) { ++ pc->buckets[bucket].used--; ++ return ret; ++ } ++ } ++ ++ return bucket; ++} ++ ++static int airoha_pwm_sipo_init(struct airoha_pwm *pc) ++{ ++ u32 val; ++ int ret; ++ ++ ret = regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG, ++ AIROHA_PWM_SERIAL_GPIO_MODE_74HC164); ++ if (ret) ++ return ret; ++ ++ /* Configure shift register chip clock timings, use 32x divisor */ ++ ret = regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DIVR, ++ AIROHA_PWM_SGPIO_CLK_DIVR_32); ++ if (ret) ++ return ret; ++ ++ /* ++ * Configure the shift register chip clock delay. This needs ++ * to be configured based on the chip characteristics when the SoC ++ * apply the shift register configuration. ++ * This doesn't affect actual PWM operation and is only specific to ++ * the shift register chip. ++ * ++ * For 74HC164 we set it to 0. ++ * ++ * For reference, the actual delay applied is the internal clock ++ * feed to the SGPIO chip + 1. ++ * ++ * From documentation is specified that clock delay should not be ++ * greater than (AIROHA_PWM_REG_SGPIO_CLK_DIVR / 2) - 1. ++ */ ++ ret = regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DLY, 0); ++ if (ret) ++ return ret; ++ ++ /* ++ * It is necessary to explicitly shift out all zeros after muxing ++ * to initialize the shift register before enabling PWM ++ * mode because in PWM mode SIPO will not start shifting until ++ * it needs to output a non-zero value (bit 31 of led_data ++ * indicates shifting in progress and it must return to zero ++ * before led_data can be written or PWM mode can be set). ++ */ ++ ret = regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val, ++ !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG), ++ 10, 200 * USEC_PER_MSEC); ++ if (ret) ++ return ret; ++ ++ ret = regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, ++ AIROHA_PWM_SGPIO_LED_DATA_DATA); ++ if (ret) ++ return ret; ++ ret = regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val, ++ !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG), ++ 10, 200 * USEC_PER_MSEC); ++ if (ret) ++ return ret; ++ ++ /* Set SIPO in PWM mode */ ++ return regmap_set_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG, ++ AIROHA_PWM_SERIAL_GPIO_FLASH_MODE); ++} ++ ++static int airoha_pwm_config_flash_map(struct airoha_pwm *pc, ++ unsigned int hwpwm, int index) ++{ ++ unsigned int addr; ++ u32 shift; ++ int ret; ++ ++ airoha_pwm_get_flash_map_addr_and_shift(hwpwm, &addr, &shift); ++ ++ /* negative index means disable PWM channel */ ++ if (index < 0) { ++ /* ++ * If we need to disable the PWM, we just put low the ++ * GPIO. No need to setup buckets. ++ */ ++ return regmap_clear_bits(pc->regmap, addr, ++ AIROHA_PWM_GPIO_FLASH_EN << shift); ++ } ++ ++ ret = regmap_update_bits(pc->regmap, addr, ++ AIROHA_PWM_GPIO_FLASH_SET_ID << shift, ++ FIELD_PREP(AIROHA_PWM_GPIO_FLASH_SET_ID, index) << shift); ++ if (ret) ++ return ret; ++ ++ return regmap_set_bits(pc->regmap, addr, AIROHA_PWM_GPIO_FLASH_EN << shift); ++} ++ ++static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm, ++ u32 period_ticks, u32 duty_ticks) ++{ ++ unsigned int hwpwm = pwm->hwpwm; ++ int bucket, ret; ++ ++ bucket = airoha_pwm_consume_generator(pc, duty_ticks, period_ticks, ++ hwpwm); ++ if (bucket < 0) ++ return bucket; ++ ++ ret = airoha_pwm_config_flash_map(pc, hwpwm, bucket); ++ if (ret) { ++ pc->buckets[bucket].used--; ++ return ret; ++ } ++ ++ __set_bit(hwpwm, pc->initialized); ++ pc->channel_bucket[hwpwm] = bucket; ++ ++ /* ++ * SIPO are special GPIO attached to a shift register chip. The handling ++ * of this chip is internal to the SoC that takes care of applying the ++ * values based on the flash map. To apply a new flash map, it's needed ++ * to trigger a refresh on the shift register chip. ++ * If a SIPO is getting configuring , always reinit the shift register ++ * chip to make sure the correct flash map is applied. ++ * Skip reconfiguring the shift register if the related hwpwm ++ * is disabled (as it doesn't need to be mapped). ++ */ ++ if (hwpwm >= AIROHA_PWM_NUM_GPIO) { ++ ret = airoha_pwm_sipo_init(pc); ++ if (ret) { ++ airoha_pwm_release_bucket_config(pc, hwpwm); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void airoha_pwm_disable(struct airoha_pwm *pc, struct pwm_device *pwm) ++{ ++ /* Disable PWM and release the bucket */ ++ airoha_pwm_config_flash_map(pc, pwm->hwpwm, -1); ++ airoha_pwm_release_bucket_config(pc, pwm->hwpwm); ++ ++ __clear_bit(pwm->hwpwm, pc->initialized); ++ ++ /* If no SIPO is used, disable the shift register chip */ ++ if (!bitmap_read(pc->initialized, ++ AIROHA_PWM_NUM_GPIO, AIROHA_PWM_NUM_SIPO)) ++ regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG, ++ AIROHA_PWM_SERIAL_GPIO_FLASH_MODE); ++} ++ ++static int airoha_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct airoha_pwm *pc = pwmchip_get_drvdata(chip); ++ u32 period_ticks, duty_ticks; ++ u32 period_ns, duty_ns; ++ ++ if (!state->enabled) { ++ airoha_pwm_disable(pc, pwm); ++ return 0; ++ } ++ ++ /* Only normal polarity is supported */ ++ if (state->polarity == PWM_POLARITY_INVERSED) ++ return -EINVAL; ++ ++ /* Exit early if period is less than minimum supported */ ++ if (state->period < AIROHA_PWM_PERIOD_TICK_NS) ++ return -EINVAL; ++ ++ /* Clamp period to MAX supported value */ ++ if (state->period > AIROHA_PWM_PERIOD_MAX_NS) ++ period_ns = AIROHA_PWM_PERIOD_MAX_NS; ++ else ++ period_ns = state->period; ++ ++ /* Validate duty to configured period */ ++ if (state->duty_cycle > period_ns) ++ duty_ns = period_ns; ++ else ++ duty_ns = state->duty_cycle; ++ ++ /* Convert period ns to ticks */ ++ period_ticks = airoha_pwm_get_period_ticks_from_ns(period_ns); ++ /* Convert period ticks to ns again for cosistent duty tick calculation */ ++ period_ns = airoha_pwm_get_period_ns_from_ticks(period_ticks); ++ duty_ticks = airoha_pwm_get_duty_ticks_from_ns(period_ns, duty_ns); ++ ++ return airoha_pwm_config(pc, pwm, period_ticks, duty_ticks); ++} ++ ++static int airoha_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct airoha_pwm *pc = pwmchip_get_drvdata(chip); ++ int ret, hwpwm = pwm->hwpwm; ++ u32 addr, shift, val; ++ u8 bucket; ++ ++ airoha_pwm_get_flash_map_addr_and_shift(hwpwm, &addr, &shift); ++ ++ ret = regmap_read(pc->regmap, addr, &val); ++ if (ret) ++ return ret; ++ ++ state->enabled = FIELD_GET(AIROHA_PWM_GPIO_FLASH_EN, val >> shift); ++ if (!state->enabled) ++ return 0; ++ ++ state->polarity = PWM_POLARITY_NORMAL; ++ ++ bucket = FIELD_GET(AIROHA_PWM_GPIO_FLASH_SET_ID, val >> shift); ++ return airoha_pwm_get_bucket(pc, bucket, &state->period, ++ &state->duty_cycle); ++} ++ ++static const struct pwm_ops airoha_pwm_ops = { ++ .apply = airoha_pwm_apply, ++ .get_state = airoha_pwm_get_state, ++}; ++ ++static int airoha_pwm_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct airoha_pwm *pc; ++ struct pwm_chip *chip; ++ int ret; ++ ++ chip = devm_pwmchip_alloc(dev, AIROHA_PWM_MAX_CHANNELS, sizeof(*pc)); ++ if (IS_ERR(chip)) ++ return PTR_ERR(chip); ++ ++ chip->ops = &airoha_pwm_ops; ++ pc = pwmchip_get_drvdata(chip); ++ ++ pc->regmap = device_node_to_regmap(dev_of_node(dev->parent)); ++ if (IS_ERR(pc->regmap)) ++ return dev_err_probe(dev, PTR_ERR(pc->regmap), "Failed to get PWM regmap\n"); ++ ++ ret = devm_pwmchip_add(dev, chip); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_pwm_of_match[] = { ++ { .compatible = "airoha,en7581-pwm" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_pwm_of_match); ++ ++static struct platform_driver airoha_pwm_driver = { ++ .driver = { ++ .name = "pwm-airoha", ++ .probe_type = PROBE_PREFER_ASYNCHRONOUS, ++ .of_match_table = airoha_pwm_of_match, ++ }, ++ .probe = airoha_pwm_probe, ++}; ++module_platform_driver(airoha_pwm_driver); ++ ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_AUTHOR("Markus Gothe "); ++MODULE_AUTHOR("Benjamin Larsson "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Airoha EN7581 PWM driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/108-v6.19-net-airoha-Add-the-capability-to-consume-out-of-orde.patch b/target/linux/airoha/patches-6.12/108-v6.19-net-airoha-Add-the-capability-to-consume-out-of-orde.patch new file mode 100644 index 00000000000..8f0787c92e1 --- /dev/null +++ b/target/linux/airoha/patches-6.12/108-v6.19-net-airoha-Add-the-capability-to-consume-out-of-orde.patch @@ -0,0 +1,245 @@ +From 3f47e67dff1f7266e112c50313d63824f6f17102 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 6 Nov 2025 13:53:23 +0100 +Subject: [PATCH] net: airoha: Add the capability to consume out-of-order DMA + tx descriptors + +EN7581 and AN7583 SoCs are capable of DMA mapping non-linear tx skbs on +non-consecutive DMA descriptors. This feature is useful when multiple +flows are queued on the same hw tx queue since it allows to fully utilize +the available tx DMA descriptors and to avoid the starvation of +high-priority flow we have in the current codebase due to head-of-line +blocking introduced by low-priority flows. + +Tested-by: Xuegang Lu +Reviewed-by: Jacob Keller +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251106-airoha-tx-linked-list-v2-1-0706d4a322bd@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 85 +++++++++++------------- + drivers/net/ethernet/airoha/airoha_eth.h | 7 +- + 2 files changed, 45 insertions(+), 47 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -913,19 +913,13 @@ static int airoha_qdma_tx_napi_poll(stru + + dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); +- memset(e, 0, sizeof(*e)); ++ e->dma_addr = 0; ++ list_add_tail(&e->list, &q->tx_list); ++ + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + q->queued--; + +- /* completion ring can report out-of-order indexes if hw QoS +- * is enabled and packets with different priority are queued +- * to same DMA ring. Take into account possible out-of-order +- * reports incrementing DMA ring tail pointer +- */ +- while (q->tail != q->head && !q->entry[q->tail].dma_addr) +- q->tail = (q->tail + 1) % q->ndesc; +- + if (skb) { + u16 queue = skb_get_queue_mapping(skb); + struct netdev_queue *txq; +@@ -970,6 +964,7 @@ static int airoha_qdma_init_tx_queue(str + q->ndesc = size; + q->qdma = qdma; + q->free_thr = 1 + MAX_SKB_FRAGS; ++ INIT_LIST_HEAD(&q->tx_list); + + q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), + GFP_KERNEL); +@@ -982,9 +977,9 @@ static int airoha_qdma_init_tx_queue(str + return -ENOMEM; + + for (i = 0; i < q->ndesc; i++) { +- u32 val; ++ u32 val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); + +- val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); ++ list_add_tail(&q->entry[i].list, &q->tx_list); + WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); + } + +@@ -994,9 +989,9 @@ static int airoha_qdma_init_tx_queue(str + + airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, +- FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, 0)); + airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, +- FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); ++ FIELD_PREP(TX_RING_DMA_IDX_MASK, 0)); + + return 0; + } +@@ -1052,17 +1047,21 @@ static int airoha_qdma_init_tx(struct ai + static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) + { + struct airoha_eth *eth = q->qdma->eth; ++ int i; + + spin_lock_bh(&q->lock); +- while (q->queued) { +- struct airoha_queue_entry *e = &q->entry[q->tail]; ++ for (i = 0; i < q->ndesc; i++) { ++ struct airoha_queue_entry *e = &q->entry[i]; ++ ++ if (!e->dma_addr) ++ continue; + + dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); + dev_kfree_skb_any(e->skb); ++ e->dma_addr = 0; + e->skb = NULL; +- +- q->tail = (q->tail + 1) % q->ndesc; ++ list_add_tail(&e->list, &q->tx_list); + q->queued--; + } + spin_unlock_bh(&q->lock); +@@ -1904,20 +1903,6 @@ static u32 airoha_get_dsa_tag(struct sk_ + #endif + } + +-static bool airoha_dev_tx_queue_busy(struct airoha_queue *q, u32 nr_frags) +-{ +- u32 tail = q->tail <= q->head ? q->tail + q->ndesc : q->tail; +- u32 index = q->head + nr_frags; +- +- /* completion napi can free out-of-order tx descriptors if hw QoS is +- * enabled and packets with different priorities are queued to the same +- * DMA ring. Take into account possible out-of-order reports checking +- * if the tx queue is full using circular buffer head/tail pointers +- * instead of the number of queued packets. +- */ +- return index >= tail; +-} +- + static int airoha_get_fe_port(struct airoha_gdm_port *port) + { + struct airoha_qdma *qdma = port->qdma; +@@ -1940,8 +1925,10 @@ static netdev_tx_t airoha_dev_xmit(struc + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; + u32 nr_frags, tag, msg0, msg1, len; ++ struct airoha_queue_entry *e; + struct netdev_queue *txq; + struct airoha_queue *q; ++ LIST_HEAD(tx_list); + void *data; + int i, qid; + u16 index; +@@ -1987,7 +1974,7 @@ static netdev_tx_t airoha_dev_xmit(struc + txq = netdev_get_tx_queue(dev, qid); + nr_frags = 1 + skb_shinfo(skb)->nr_frags; + +- if (airoha_dev_tx_queue_busy(q, nr_frags)) { ++ if (q->queued + nr_frags >= q->ndesc) { + /* not enough space in the queue */ + netif_tx_stop_queue(txq); + spin_unlock_bh(&q->lock); +@@ -1996,11 +1983,13 @@ static netdev_tx_t airoha_dev_xmit(struc + + len = skb_headlen(skb); + data = skb->data; +- index = q->head; ++ ++ e = list_first_entry(&q->tx_list, struct airoha_queue_entry, ++ list); ++ index = e - q->entry; + + for (i = 0; i < nr_frags; i++) { + struct airoha_qdma_desc *desc = &q->desc[index]; +- struct airoha_queue_entry *e = &q->entry[index]; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t addr; + u32 val; +@@ -2010,7 +1999,14 @@ static netdev_tx_t airoha_dev_xmit(struc + if (unlikely(dma_mapping_error(dev->dev.parent, addr))) + goto error_unmap; + +- index = (index + 1) % q->ndesc; ++ list_move_tail(&e->list, &tx_list); ++ e->skb = i ? NULL : skb; ++ e->dma_addr = addr; ++ e->dma_len = len; ++ ++ e = list_first_entry(&q->tx_list, struct airoha_queue_entry, ++ list); ++ index = e - q->entry; + + val = FIELD_PREP(QDMA_DESC_LEN_MASK, len); + if (i < nr_frags - 1) +@@ -2023,15 +2019,9 @@ static netdev_tx_t airoha_dev_xmit(struc + WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); + WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); + +- e->skb = i ? NULL : skb; +- e->dma_addr = addr; +- e->dma_len = len; +- + data = skb_frag_address(frag); + len = skb_frag_size(frag); + } +- +- q->head = index; + q->queued += i; + + skb_tx_timestamp(skb); +@@ -2040,7 +2030,7 @@ static netdev_tx_t airoha_dev_xmit(struc + if (netif_xmit_stopped(txq) || !netdev_xmit_more()) + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), + TX_RING_CPU_IDX_MASK, +- FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + + if (q->ndesc - q->queued < q->free_thr) + netif_tx_stop_queue(txq); +@@ -2050,10 +2040,13 @@ static netdev_tx_t airoha_dev_xmit(struc + return NETDEV_TX_OK; + + error_unmap: +- for (i--; i >= 0; i--) { +- index = (q->head + i) % q->ndesc; +- dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr, +- q->entry[index].dma_len, DMA_TO_DEVICE); ++ while (!list_empty(&tx_list)) { ++ e = list_first_entry(&tx_list, struct airoha_queue_entry, ++ list); ++ dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len, ++ DMA_TO_DEVICE); ++ e->dma_addr = 0; ++ list_move_tail(&e->list, &q->tx_list); + } + + spin_unlock_bh(&q->lock); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -169,7 +169,10 @@ enum trtcm_param { + struct airoha_queue_entry { + union { + void *buf; +- struct sk_buff *skb; ++ struct { ++ struct list_head list; ++ struct sk_buff *skb; ++ }; + }; + dma_addr_t dma_addr; + u16 dma_len; +@@ -193,6 +196,8 @@ struct airoha_queue { + struct napi_struct napi; + struct page_pool *page_pool; + struct sk_buff *skb; ++ ++ struct list_head tx_list; + }; + + struct airoha_tx_irq_queue { diff --git a/target/linux/airoha/patches-6.12/109-01-v6.19-pinctrl-airoha-generalize-pins-group-function-confs-.patch b/target/linux/airoha/patches-6.12/109-01-v6.19-pinctrl-airoha-generalize-pins-group-function-confs-.patch new file mode 100644 index 00000000000..9546fa3e83c --- /dev/null +++ b/target/linux/airoha/patches-6.12/109-01-v6.19-pinctrl-airoha-generalize-pins-group-function-confs-.patch @@ -0,0 +1,775 @@ +From 4043b0c45f8555a079bdac69a19ed08695a47a7b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Nov 2025 00:57:04 +0100 +Subject: [PATCH 1/5] pinctrl: airoha: generalize pins/group/function/confs + handling + +In preparation for support of Airoha AN7583, generalize +pins/group/function/confs handling and move them in match_data. +Inner function will base the values on the pinctrl priv struct instead of +relying on hardcoded struct. + +This permits to use different PIN data while keeping the same logic. + +Signed-off-by: Christian Marangi +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 567 ++++++++++++---------- + 1 file changed, 318 insertions(+), 249 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -30,15 +30,15 @@ + #include "../pinconf.h" + #include "../pinmux.h" + +-#define PINCTRL_PIN_GROUP(id) \ +- PINCTRL_PINGROUP(#id, id##_pins, ARRAY_SIZE(id##_pins)) ++#define PINCTRL_PIN_GROUP(id, table) \ ++ PINCTRL_PINGROUP(id, table##_pins, ARRAY_SIZE(table##_pins)) + +-#define PINCTRL_FUNC_DESC(id) \ ++#define PINCTRL_FUNC_DESC(id, table) \ + { \ +- .desc = PINCTRL_PINFUNCTION(#id, id##_groups, \ +- ARRAY_SIZE(id##_groups)), \ +- .groups = id##_func_group, \ +- .group_size = ARRAY_SIZE(id##_func_group), \ ++ .desc = PINCTRL_PINFUNCTION(#id, table##_groups, \ ++ ARRAY_SIZE(table##_groups)),\ ++ .groups = table##_func_group, \ ++ .group_size = ARRAY_SIZE(table##_func_group), \ + } + + #define PINCTRL_CONF_DESC(p, offset, mask) \ +@@ -357,16 +357,46 @@ struct airoha_pinctrl_gpiochip { + u32 irq_type[AIROHA_NUM_PINS]; + }; + ++struct airoha_pinctrl_confs_info { ++ const struct airoha_pinctrl_conf *confs; ++ unsigned int num_confs; ++}; ++ ++enum airoha_pinctrl_confs_type { ++ AIROHA_PINCTRL_CONFS_PULLUP, ++ AIROHA_PINCTRL_CONFS_PULLDOWN, ++ AIROHA_PINCTRL_CONFS_DRIVE_E2, ++ AIROHA_PINCTRL_CONFS_DRIVE_E4, ++ AIROHA_PINCTRL_CONFS_PCIE_RST_OD, ++ ++ AIROHA_PINCTRL_CONFS_MAX, ++}; ++ + struct airoha_pinctrl { + struct pinctrl_dev *ctrl; + ++ struct pinctrl_desc desc; ++ const struct pingroup *grps; ++ const struct airoha_pinctrl_func *funcs; ++ const struct airoha_pinctrl_confs_info *confs_info; ++ + struct regmap *chip_scu; + struct regmap *regmap; + + struct airoha_pinctrl_gpiochip gpiochip; + }; + +-static struct pinctrl_pin_desc airoha_pinctrl_pins[] = { ++struct airoha_pinctrl_match_data { ++ const struct pinctrl_pin_desc *pins; ++ const unsigned int num_pins; ++ const struct pingroup *grps; ++ const unsigned int num_grps; ++ const struct airoha_pinctrl_func *funcs; ++ const unsigned int num_funcs; ++ const struct airoha_pinctrl_confs_info confs_info[AIROHA_PINCTRL_CONFS_MAX]; ++}; ++ ++static struct pinctrl_pin_desc en7581_pinctrl_pins[] = { + PINCTRL_PIN(0, "uart1_txd"), + PINCTRL_PIN(1, "uart1_rxd"), + PINCTRL_PIN(2, "i2c_scl"), +@@ -427,172 +457,172 @@ static struct pinctrl_pin_desc airoha_pi + PINCTRL_PIN(63, "pcie_reset2"), + }; + +-static const int pon_pins[] = { 49, 50, 51, 52, 53, 54 }; +-static const int pon_tod_1pps_pins[] = { 46 }; +-static const int gsw_tod_1pps_pins[] = { 46 }; +-static const int sipo_pins[] = { 16, 17 }; +-static const int sipo_rclk_pins[] = { 16, 17, 43 }; +-static const int mdio_pins[] = { 14, 15 }; +-static const int uart2_pins[] = { 48, 55 }; +-static const int uart2_cts_rts_pins[] = { 46, 47 }; +-static const int hsuart_pins[] = { 28, 29 }; +-static const int hsuart_cts_rts_pins[] = { 26, 27 }; +-static const int uart4_pins[] = { 38, 39 }; +-static const int uart5_pins[] = { 18, 19 }; +-static const int i2c0_pins[] = { 2, 3 }; +-static const int i2c1_pins[] = { 14, 15 }; +-static const int jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; +-static const int jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; +-static const int i2s_pins[] = { 26, 27, 28, 29 }; +-static const int pcm1_pins[] = { 22, 23, 24, 25 }; +-static const int pcm2_pins[] = { 18, 19, 20, 21 }; +-static const int spi_quad_pins[] = { 32, 33 }; +-static const int spi_pins[] = { 4, 5, 6, 7 }; +-static const int spi_cs1_pins[] = { 34 }; +-static const int pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; +-static const int pcm_spi_int_pins[] = { 14 }; +-static const int pcm_spi_rst_pins[] = { 15 }; +-static const int pcm_spi_cs1_pins[] = { 43 }; +-static const int pcm_spi_cs2_pins[] = { 40 }; +-static const int pcm_spi_cs2_p128_pins[] = { 40 }; +-static const int pcm_spi_cs2_p156_pins[] = { 40 }; +-static const int pcm_spi_cs3_pins[] = { 41 }; +-static const int pcm_spi_cs4_pins[] = { 42 }; +-static const int emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; +-static const int pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; +-static const int gpio0_pins[] = { 13 }; +-static const int gpio1_pins[] = { 14 }; +-static const int gpio2_pins[] = { 15 }; +-static const int gpio3_pins[] = { 16 }; +-static const int gpio4_pins[] = { 17 }; +-static const int gpio5_pins[] = { 18 }; +-static const int gpio6_pins[] = { 19 }; +-static const int gpio7_pins[] = { 20 }; +-static const int gpio8_pins[] = { 21 }; +-static const int gpio9_pins[] = { 22 }; +-static const int gpio10_pins[] = { 23 }; +-static const int gpio11_pins[] = { 24 }; +-static const int gpio12_pins[] = { 25 }; +-static const int gpio13_pins[] = { 26 }; +-static const int gpio14_pins[] = { 27 }; +-static const int gpio15_pins[] = { 28 }; +-static const int gpio16_pins[] = { 29 }; +-static const int gpio17_pins[] = { 30 }; +-static const int gpio18_pins[] = { 31 }; +-static const int gpio19_pins[] = { 32 }; +-static const int gpio20_pins[] = { 33 }; +-static const int gpio21_pins[] = { 34 }; +-static const int gpio22_pins[] = { 35 }; +-static const int gpio23_pins[] = { 36 }; +-static const int gpio24_pins[] = { 37 }; +-static const int gpio25_pins[] = { 38 }; +-static const int gpio26_pins[] = { 39 }; +-static const int gpio27_pins[] = { 40 }; +-static const int gpio28_pins[] = { 41 }; +-static const int gpio29_pins[] = { 42 }; +-static const int gpio30_pins[] = { 43 }; +-static const int gpio31_pins[] = { 44 }; +-static const int gpio33_pins[] = { 46 }; +-static const int gpio34_pins[] = { 47 }; +-static const int gpio35_pins[] = { 48 }; +-static const int gpio36_pins[] = { 49 }; +-static const int gpio37_pins[] = { 50 }; +-static const int gpio38_pins[] = { 51 }; +-static const int gpio39_pins[] = { 52 }; +-static const int gpio40_pins[] = { 53 }; +-static const int gpio41_pins[] = { 54 }; +-static const int gpio42_pins[] = { 55 }; +-static const int gpio43_pins[] = { 56 }; +-static const int gpio44_pins[] = { 57 }; +-static const int gpio45_pins[] = { 58 }; +-static const int gpio46_pins[] = { 59 }; +-static const int pcie_reset0_pins[] = { 61 }; +-static const int pcie_reset1_pins[] = { 62 }; +-static const int pcie_reset2_pins[] = { 63 }; +- +-static const struct pingroup airoha_pinctrl_groups[] = { +- PINCTRL_PIN_GROUP(pon), +- PINCTRL_PIN_GROUP(pon_tod_1pps), +- PINCTRL_PIN_GROUP(gsw_tod_1pps), +- PINCTRL_PIN_GROUP(sipo), +- PINCTRL_PIN_GROUP(sipo_rclk), +- PINCTRL_PIN_GROUP(mdio), +- PINCTRL_PIN_GROUP(uart2), +- PINCTRL_PIN_GROUP(uart2_cts_rts), +- PINCTRL_PIN_GROUP(hsuart), +- PINCTRL_PIN_GROUP(hsuart_cts_rts), +- PINCTRL_PIN_GROUP(uart4), +- PINCTRL_PIN_GROUP(uart5), +- PINCTRL_PIN_GROUP(i2c0), +- PINCTRL_PIN_GROUP(i2c1), +- PINCTRL_PIN_GROUP(jtag_udi), +- PINCTRL_PIN_GROUP(jtag_dfd), +- PINCTRL_PIN_GROUP(i2s), +- PINCTRL_PIN_GROUP(pcm1), +- PINCTRL_PIN_GROUP(pcm2), +- PINCTRL_PIN_GROUP(spi), +- PINCTRL_PIN_GROUP(spi_quad), +- PINCTRL_PIN_GROUP(spi_cs1), +- PINCTRL_PIN_GROUP(pcm_spi), +- PINCTRL_PIN_GROUP(pcm_spi_int), +- PINCTRL_PIN_GROUP(pcm_spi_rst), +- PINCTRL_PIN_GROUP(pcm_spi_cs1), +- PINCTRL_PIN_GROUP(pcm_spi_cs2_p128), +- PINCTRL_PIN_GROUP(pcm_spi_cs2_p156), +- PINCTRL_PIN_GROUP(pcm_spi_cs2), +- PINCTRL_PIN_GROUP(pcm_spi_cs3), +- PINCTRL_PIN_GROUP(pcm_spi_cs4), +- PINCTRL_PIN_GROUP(emmc), +- PINCTRL_PIN_GROUP(pnand), +- PINCTRL_PIN_GROUP(gpio0), +- PINCTRL_PIN_GROUP(gpio1), +- PINCTRL_PIN_GROUP(gpio2), +- PINCTRL_PIN_GROUP(gpio3), +- PINCTRL_PIN_GROUP(gpio4), +- PINCTRL_PIN_GROUP(gpio5), +- PINCTRL_PIN_GROUP(gpio6), +- PINCTRL_PIN_GROUP(gpio7), +- PINCTRL_PIN_GROUP(gpio8), +- PINCTRL_PIN_GROUP(gpio9), +- PINCTRL_PIN_GROUP(gpio10), +- PINCTRL_PIN_GROUP(gpio11), +- PINCTRL_PIN_GROUP(gpio12), +- PINCTRL_PIN_GROUP(gpio13), +- PINCTRL_PIN_GROUP(gpio14), +- PINCTRL_PIN_GROUP(gpio15), +- PINCTRL_PIN_GROUP(gpio16), +- PINCTRL_PIN_GROUP(gpio17), +- PINCTRL_PIN_GROUP(gpio18), +- PINCTRL_PIN_GROUP(gpio19), +- PINCTRL_PIN_GROUP(gpio20), +- PINCTRL_PIN_GROUP(gpio21), +- PINCTRL_PIN_GROUP(gpio22), +- PINCTRL_PIN_GROUP(gpio23), +- PINCTRL_PIN_GROUP(gpio24), +- PINCTRL_PIN_GROUP(gpio25), +- PINCTRL_PIN_GROUP(gpio26), +- PINCTRL_PIN_GROUP(gpio27), +- PINCTRL_PIN_GROUP(gpio28), +- PINCTRL_PIN_GROUP(gpio29), +- PINCTRL_PIN_GROUP(gpio30), +- PINCTRL_PIN_GROUP(gpio31), +- PINCTRL_PIN_GROUP(gpio33), +- PINCTRL_PIN_GROUP(gpio34), +- PINCTRL_PIN_GROUP(gpio35), +- PINCTRL_PIN_GROUP(gpio36), +- PINCTRL_PIN_GROUP(gpio37), +- PINCTRL_PIN_GROUP(gpio38), +- PINCTRL_PIN_GROUP(gpio39), +- PINCTRL_PIN_GROUP(gpio40), +- PINCTRL_PIN_GROUP(gpio41), +- PINCTRL_PIN_GROUP(gpio42), +- PINCTRL_PIN_GROUP(gpio43), +- PINCTRL_PIN_GROUP(gpio44), +- PINCTRL_PIN_GROUP(gpio45), +- PINCTRL_PIN_GROUP(gpio46), +- PINCTRL_PIN_GROUP(pcie_reset0), +- PINCTRL_PIN_GROUP(pcie_reset1), +- PINCTRL_PIN_GROUP(pcie_reset2), ++static const int en7581_pon_pins[] = { 49, 50, 51, 52, 53, 54 }; ++static const int en7581_pon_tod_1pps_pins[] = { 46 }; ++static const int en7581_gsw_tod_1pps_pins[] = { 46 }; ++static const int en7581_sipo_pins[] = { 16, 17 }; ++static const int en7581_sipo_rclk_pins[] = { 16, 17, 43 }; ++static const int en7581_mdio_pins[] = { 14, 15 }; ++static const int en7581_uart2_pins[] = { 48, 55 }; ++static const int en7581_uart2_cts_rts_pins[] = { 46, 47 }; ++static const int en7581_hsuart_pins[] = { 28, 29 }; ++static const int en7581_hsuart_cts_rts_pins[] = { 26, 27 }; ++static const int en7581_uart4_pins[] = { 38, 39 }; ++static const int en7581_uart5_pins[] = { 18, 19 }; ++static const int en7581_i2c0_pins[] = { 2, 3 }; ++static const int en7581_i2c1_pins[] = { 14, 15 }; ++static const int en7581_jtag_udi_pins[] = { 16, 17, 18, 19, 20 }; ++static const int en7581_jtag_dfd_pins[] = { 16, 17, 18, 19, 20 }; ++static const int en7581_i2s_pins[] = { 26, 27, 28, 29 }; ++static const int en7581_pcm1_pins[] = { 22, 23, 24, 25 }; ++static const int en7581_pcm2_pins[] = { 18, 19, 20, 21 }; ++static const int en7581_spi_quad_pins[] = { 32, 33 }; ++static const int en7581_spi_pins[] = { 4, 5, 6, 7 }; ++static const int en7581_spi_cs1_pins[] = { 34 }; ++static const int en7581_pcm_spi_pins[] = { 18, 19, 20, 21, 22, 23, 24, 25 }; ++static const int en7581_pcm_spi_int_pins[] = { 14 }; ++static const int en7581_pcm_spi_rst_pins[] = { 15 }; ++static const int en7581_pcm_spi_cs1_pins[] = { 43 }; ++static const int en7581_pcm_spi_cs2_pins[] = { 40 }; ++static const int en7581_pcm_spi_cs2_p128_pins[] = { 40 }; ++static const int en7581_pcm_spi_cs2_p156_pins[] = { 40 }; ++static const int en7581_pcm_spi_cs3_pins[] = { 41 }; ++static const int en7581_pcm_spi_cs4_pins[] = { 42 }; ++static const int en7581_emmc_pins[] = { 4, 5, 6, 30, 31, 32, 33, 34, 35, 36, 37 }; ++static const int en7581_pnand_pins[] = { 4, 5, 6, 7, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; ++static const int en7581_gpio0_pins[] = { 13 }; ++static const int en7581_gpio1_pins[] = { 14 }; ++static const int en7581_gpio2_pins[] = { 15 }; ++static const int en7581_gpio3_pins[] = { 16 }; ++static const int en7581_gpio4_pins[] = { 17 }; ++static const int en7581_gpio5_pins[] = { 18 }; ++static const int en7581_gpio6_pins[] = { 19 }; ++static const int en7581_gpio7_pins[] = { 20 }; ++static const int en7581_gpio8_pins[] = { 21 }; ++static const int en7581_gpio9_pins[] = { 22 }; ++static const int en7581_gpio10_pins[] = { 23 }; ++static const int en7581_gpio11_pins[] = { 24 }; ++static const int en7581_gpio12_pins[] = { 25 }; ++static const int en7581_gpio13_pins[] = { 26 }; ++static const int en7581_gpio14_pins[] = { 27 }; ++static const int en7581_gpio15_pins[] = { 28 }; ++static const int en7581_gpio16_pins[] = { 29 }; ++static const int en7581_gpio17_pins[] = { 30 }; ++static const int en7581_gpio18_pins[] = { 31 }; ++static const int en7581_gpio19_pins[] = { 32 }; ++static const int en7581_gpio20_pins[] = { 33 }; ++static const int en7581_gpio21_pins[] = { 34 }; ++static const int en7581_gpio22_pins[] = { 35 }; ++static const int en7581_gpio23_pins[] = { 36 }; ++static const int en7581_gpio24_pins[] = { 37 }; ++static const int en7581_gpio25_pins[] = { 38 }; ++static const int en7581_gpio26_pins[] = { 39 }; ++static const int en7581_gpio27_pins[] = { 40 }; ++static const int en7581_gpio28_pins[] = { 41 }; ++static const int en7581_gpio29_pins[] = { 42 }; ++static const int en7581_gpio30_pins[] = { 43 }; ++static const int en7581_gpio31_pins[] = { 44 }; ++static const int en7581_gpio33_pins[] = { 46 }; ++static const int en7581_gpio34_pins[] = { 47 }; ++static const int en7581_gpio35_pins[] = { 48 }; ++static const int en7581_gpio36_pins[] = { 49 }; ++static const int en7581_gpio37_pins[] = { 50 }; ++static const int en7581_gpio38_pins[] = { 51 }; ++static const int en7581_gpio39_pins[] = { 52 }; ++static const int en7581_gpio40_pins[] = { 53 }; ++static const int en7581_gpio41_pins[] = { 54 }; ++static const int en7581_gpio42_pins[] = { 55 }; ++static const int en7581_gpio43_pins[] = { 56 }; ++static const int en7581_gpio44_pins[] = { 57 }; ++static const int en7581_gpio45_pins[] = { 58 }; ++static const int en7581_gpio46_pins[] = { 59 }; ++static const int en7581_pcie_reset0_pins[] = { 61 }; ++static const int en7581_pcie_reset1_pins[] = { 62 }; ++static const int en7581_pcie_reset2_pins[] = { 63 }; ++ ++static const struct pingroup en7581_pinctrl_groups[] = { ++ PINCTRL_PIN_GROUP("pon", en7581_pon), ++ PINCTRL_PIN_GROUP("pon_tod_1pps", en7581_pon_tod_1pps), ++ PINCTRL_PIN_GROUP("gsw_tod_1pps", en7581_gsw_tod_1pps), ++ PINCTRL_PIN_GROUP("sipo", en7581_sipo), ++ PINCTRL_PIN_GROUP("sipo_rclk", en7581_sipo_rclk), ++ PINCTRL_PIN_GROUP("mdio", en7581_mdio), ++ PINCTRL_PIN_GROUP("uart2", en7581_uart2), ++ PINCTRL_PIN_GROUP("uart2_cts_rts", en7581_uart2_cts_rts), ++ PINCTRL_PIN_GROUP("hsuart", en7581_hsuart), ++ PINCTRL_PIN_GROUP("hsuart_cts_rts", en7581_hsuart_cts_rts), ++ PINCTRL_PIN_GROUP("uart4", en7581_uart4), ++ PINCTRL_PIN_GROUP("uart5", en7581_uart5), ++ PINCTRL_PIN_GROUP("i2c0", en7581_i2c0), ++ PINCTRL_PIN_GROUP("i2c1", en7581_i2c1), ++ PINCTRL_PIN_GROUP("jtag_udi", en7581_jtag_udi), ++ PINCTRL_PIN_GROUP("jtag_dfd", en7581_jtag_dfd), ++ PINCTRL_PIN_GROUP("i2s", en7581_i2s), ++ PINCTRL_PIN_GROUP("pcm1", en7581_pcm1), ++ PINCTRL_PIN_GROUP("pcm2", en7581_pcm2), ++ PINCTRL_PIN_GROUP("spi", en7581_spi), ++ PINCTRL_PIN_GROUP("spi_quad", en7581_spi_quad), ++ PINCTRL_PIN_GROUP("spi_cs1", en7581_spi_cs1), ++ PINCTRL_PIN_GROUP("pcm_spi", en7581_pcm_spi), ++ PINCTRL_PIN_GROUP("pcm_spi_int", en7581_pcm_spi_int), ++ PINCTRL_PIN_GROUP("pcm_spi_rst", en7581_pcm_spi_rst), ++ PINCTRL_PIN_GROUP("pcm_spi_cs1", en7581_pcm_spi_cs1), ++ PINCTRL_PIN_GROUP("pcm_spi_cs2_p128", en7581_pcm_spi_cs2_p128), ++ PINCTRL_PIN_GROUP("pcm_spi_cs2_p156", en7581_pcm_spi_cs2_p156), ++ PINCTRL_PIN_GROUP("pcm_spi_cs2", en7581_pcm_spi_cs2), ++ PINCTRL_PIN_GROUP("pcm_spi_cs3", en7581_pcm_spi_cs3), ++ PINCTRL_PIN_GROUP("pcm_spi_cs4", en7581_pcm_spi_cs4), ++ PINCTRL_PIN_GROUP("emmc", en7581_emmc), ++ PINCTRL_PIN_GROUP("pnand", en7581_pnand), ++ PINCTRL_PIN_GROUP("gpio0", en7581_gpio0), ++ PINCTRL_PIN_GROUP("gpio1", en7581_gpio1), ++ PINCTRL_PIN_GROUP("gpio2", en7581_gpio2), ++ PINCTRL_PIN_GROUP("gpio3", en7581_gpio3), ++ PINCTRL_PIN_GROUP("gpio4", en7581_gpio4), ++ PINCTRL_PIN_GROUP("gpio5", en7581_gpio5), ++ PINCTRL_PIN_GROUP("gpio6", en7581_gpio6), ++ PINCTRL_PIN_GROUP("gpio7", en7581_gpio7), ++ PINCTRL_PIN_GROUP("gpio8", en7581_gpio8), ++ PINCTRL_PIN_GROUP("gpio9", en7581_gpio9), ++ PINCTRL_PIN_GROUP("gpio10", en7581_gpio10), ++ PINCTRL_PIN_GROUP("gpio11", en7581_gpio11), ++ PINCTRL_PIN_GROUP("gpio12", en7581_gpio12), ++ PINCTRL_PIN_GROUP("gpio13", en7581_gpio13), ++ PINCTRL_PIN_GROUP("gpio14", en7581_gpio14), ++ PINCTRL_PIN_GROUP("gpio15", en7581_gpio15), ++ PINCTRL_PIN_GROUP("gpio16", en7581_gpio16), ++ PINCTRL_PIN_GROUP("gpio17", en7581_gpio17), ++ PINCTRL_PIN_GROUP("gpio18", en7581_gpio18), ++ PINCTRL_PIN_GROUP("gpio19", en7581_gpio19), ++ PINCTRL_PIN_GROUP("gpio20", en7581_gpio20), ++ PINCTRL_PIN_GROUP("gpio21", en7581_gpio21), ++ PINCTRL_PIN_GROUP("gpio22", en7581_gpio22), ++ PINCTRL_PIN_GROUP("gpio23", en7581_gpio23), ++ PINCTRL_PIN_GROUP("gpio24", en7581_gpio24), ++ PINCTRL_PIN_GROUP("gpio25", en7581_gpio25), ++ PINCTRL_PIN_GROUP("gpio26", en7581_gpio26), ++ PINCTRL_PIN_GROUP("gpio27", en7581_gpio27), ++ PINCTRL_PIN_GROUP("gpio28", en7581_gpio28), ++ PINCTRL_PIN_GROUP("gpio29", en7581_gpio29), ++ PINCTRL_PIN_GROUP("gpio30", en7581_gpio30), ++ PINCTRL_PIN_GROUP("gpio31", en7581_gpio31), ++ PINCTRL_PIN_GROUP("gpio33", en7581_gpio33), ++ PINCTRL_PIN_GROUP("gpio34", en7581_gpio34), ++ PINCTRL_PIN_GROUP("gpio35", en7581_gpio35), ++ PINCTRL_PIN_GROUP("gpio36", en7581_gpio36), ++ PINCTRL_PIN_GROUP("gpio37", en7581_gpio37), ++ PINCTRL_PIN_GROUP("gpio38", en7581_gpio38), ++ PINCTRL_PIN_GROUP("gpio39", en7581_gpio39), ++ PINCTRL_PIN_GROUP("gpio40", en7581_gpio40), ++ PINCTRL_PIN_GROUP("gpio41", en7581_gpio41), ++ PINCTRL_PIN_GROUP("gpio42", en7581_gpio42), ++ PINCTRL_PIN_GROUP("gpio43", en7581_gpio43), ++ PINCTRL_PIN_GROUP("gpio44", en7581_gpio44), ++ PINCTRL_PIN_GROUP("gpio45", en7581_gpio45), ++ PINCTRL_PIN_GROUP("gpio46", en7581_gpio46), ++ PINCTRL_PIN_GROUP("pcie_reset0", en7581_pcie_reset0), ++ PINCTRL_PIN_GROUP("pcie_reset1", en7581_pcie_reset1), ++ PINCTRL_PIN_GROUP("pcie_reset2", en7581_pcie_reset2), + }; + + static const char *const pon_groups[] = { "pon" }; +@@ -1955,33 +1985,33 @@ static const struct airoha_pinctrl_func_ + }, + }; + +-static const struct airoha_pinctrl_func airoha_pinctrl_funcs[] = { +- PINCTRL_FUNC_DESC(pon), +- PINCTRL_FUNC_DESC(tod_1pps), +- PINCTRL_FUNC_DESC(sipo), +- PINCTRL_FUNC_DESC(mdio), +- PINCTRL_FUNC_DESC(uart), +- PINCTRL_FUNC_DESC(i2c), +- PINCTRL_FUNC_DESC(jtag), +- PINCTRL_FUNC_DESC(pcm), +- PINCTRL_FUNC_DESC(spi), +- PINCTRL_FUNC_DESC(pcm_spi), +- PINCTRL_FUNC_DESC(i2s), +- PINCTRL_FUNC_DESC(emmc), +- PINCTRL_FUNC_DESC(pnand), +- PINCTRL_FUNC_DESC(pcie_reset), +- PINCTRL_FUNC_DESC(pwm), +- PINCTRL_FUNC_DESC(phy1_led0), +- PINCTRL_FUNC_DESC(phy2_led0), +- PINCTRL_FUNC_DESC(phy3_led0), +- PINCTRL_FUNC_DESC(phy4_led0), +- PINCTRL_FUNC_DESC(phy1_led1), +- PINCTRL_FUNC_DESC(phy2_led1), +- PINCTRL_FUNC_DESC(phy3_led1), +- PINCTRL_FUNC_DESC(phy4_led1), ++static const struct airoha_pinctrl_func en7581_pinctrl_funcs[] = { ++ PINCTRL_FUNC_DESC("pon", pon), ++ PINCTRL_FUNC_DESC("tod_1pps", tod_1pps), ++ PINCTRL_FUNC_DESC("sipo", sipo), ++ PINCTRL_FUNC_DESC("mdio", mdio), ++ PINCTRL_FUNC_DESC("uart", uart), ++ PINCTRL_FUNC_DESC("i2c", i2c), ++ PINCTRL_FUNC_DESC("jtag", jtag), ++ PINCTRL_FUNC_DESC("pcm", pcm), ++ PINCTRL_FUNC_DESC("spi", spi), ++ PINCTRL_FUNC_DESC("pcm_spi", pcm_spi), ++ PINCTRL_FUNC_DESC("i2s", i2s), ++ PINCTRL_FUNC_DESC("emmc", emmc), ++ PINCTRL_FUNC_DESC("pnand", pnand), ++ PINCTRL_FUNC_DESC("pcie_reset", pcie_reset), ++ PINCTRL_FUNC_DESC("pwm", pwm), ++ PINCTRL_FUNC_DESC("phy1_led0", phy1_led0), ++ PINCTRL_FUNC_DESC("phy2_led0", phy2_led0), ++ PINCTRL_FUNC_DESC("phy3_led0", phy3_led0), ++ PINCTRL_FUNC_DESC("phy4_led0", phy4_led0), ++ PINCTRL_FUNC_DESC("phy1_led1", phy1_led1), ++ PINCTRL_FUNC_DESC("phy2_led1", phy2_led1), ++ PINCTRL_FUNC_DESC("phy3_led1", phy3_led1), ++ PINCTRL_FUNC_DESC("phy4_led1", phy4_led1), + }; + +-static const struct airoha_pinctrl_conf airoha_pinctrl_pullup_conf[] = { ++static const struct airoha_pinctrl_conf en7581_pinctrl_pullup_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_PU, I2C_SDA_PU_MASK), +@@ -2042,7 +2072,7 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK), + }; + +-static const struct airoha_pinctrl_conf airoha_pinctrl_pulldown_conf[] = { ++static const struct airoha_pinctrl_conf en7581_pinctrl_pulldown_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_PD, I2C_SDA_PD_MASK), +@@ -2103,7 +2133,7 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK), + }; + +-static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e2_conf[] = { ++static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e2_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_E2, I2C_SDA_E2_MASK), +@@ -2164,7 +2194,7 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK), + }; + +-static const struct airoha_pinctrl_conf airoha_pinctrl_drive_e4_conf[] = { ++static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e4_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), + PINCTRL_CONF_DESC(2, REG_I2C_SDA_E4, I2C_SDA_E4_MASK), +@@ -2225,7 +2255,7 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK), + }; + +-static const struct airoha_pinctrl_conf airoha_pinctrl_pcie_rst_od_conf[] = { ++static const struct airoha_pinctrl_conf en7581_pinctrl_pcie_rst_od_conf[] = { + PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), + PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), + PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK), +@@ -2547,12 +2577,17 @@ airoha_pinctrl_get_conf_reg(const struct + } + + static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, +- const struct airoha_pinctrl_conf *conf, +- int conf_size, int pin, u32 *val) ++ enum airoha_pinctrl_confs_type conf_type, ++ int pin, u32 *val) + { ++ const struct airoha_pinctrl_confs_info *confs_info; + const struct airoha_pinctrl_reg *reg; + +- reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); ++ confs_info = &pinctrl->confs_info[conf_type]; ++ ++ reg = airoha_pinctrl_get_conf_reg(confs_info->confs, ++ confs_info->num_confs, ++ pin); + if (!reg) + return -EINVAL; + +@@ -2565,12 +2600,17 @@ static int airoha_pinctrl_get_conf(struc + } + + static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, +- const struct airoha_pinctrl_conf *conf, +- int conf_size, int pin, u32 val) ++ enum airoha_pinctrl_confs_type conf_type, ++ int pin, u32 val) + { ++ const struct airoha_pinctrl_confs_info *confs_info; + const struct airoha_pinctrl_reg *reg = NULL; + +- reg = airoha_pinctrl_get_conf_reg(conf, conf_size, pin); ++ confs_info = &pinctrl->confs_info[conf_type]; ++ ++ reg = airoha_pinctrl_get_conf_reg(confs_info->confs, ++ confs_info->num_confs, ++ pin); + if (!reg) + return -EINVAL; + +@@ -2583,44 +2623,34 @@ static int airoha_pinctrl_set_conf(struc + } + + #define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val) \ +- airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pullup_conf, \ +- ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ ++ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLUP, \ + (pin), (val)) + #define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val) \ +- airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ +- ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ ++ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLDOWN, \ + (pin), (val)) + #define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val) \ +- airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ +- ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ ++ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E2, \ + (pin), (val)) + #define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val) \ +- airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ +- ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ ++ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E4, \ + (pin), (val)) + #define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val) \ +- airoha_pinctrl_get_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ +- ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ ++ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PCIE_RST_OD, \ + (pin), (val)) + #define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val) \ +- airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pullup_conf, \ +- ARRAY_SIZE(airoha_pinctrl_pullup_conf), \ ++ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLUP, \ + (pin), (val)) + #define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val) \ +- airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pulldown_conf, \ +- ARRAY_SIZE(airoha_pinctrl_pulldown_conf), \ ++ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLDOWN, \ + (pin), (val)) + #define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val) \ +- airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e2_conf, \ +- ARRAY_SIZE(airoha_pinctrl_drive_e2_conf), \ ++ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E2, \ + (pin), (val)) + #define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val) \ +- airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_drive_e4_conf, \ +- ARRAY_SIZE(airoha_pinctrl_drive_e4_conf), \ ++ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E4, \ + (pin), (val)) + #define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val) \ +- airoha_pinctrl_set_conf((pinctrl), airoha_pinctrl_pcie_rst_od_conf, \ +- ARRAY_SIZE(airoha_pinctrl_pcie_rst_od_conf), \ ++ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PCIE_RST_OD, \ + (pin), (val)) + + static int airoha_pinconf_get_direction(struct pinctrl_dev *pctrl_dev, u32 p) +@@ -2799,12 +2829,13 @@ static int airoha_pinconf_set(struct pin + static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, + unsigned int group, unsigned long *config) + { ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + u32 cur_config = 0; + int i; + +- for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { ++ for (i = 0; i < pinctrl->grps[group].npins; i++) { + if (airoha_pinconf_get(pctrl_dev, +- airoha_pinctrl_groups[group].pins[i], ++ pinctrl->grps[group].pins[i], + config)) + return -ENOTSUPP; + +@@ -2821,13 +2852,14 @@ static int airoha_pinconf_group_set(stru + unsigned int group, unsigned long *configs, + unsigned int num_configs) + { ++ struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + int i; + +- for (i = 0; i < airoha_pinctrl_groups[group].npins; i++) { ++ for (i = 0; i < pinctrl->grps[group].npins; i++) { + int err; + + err = airoha_pinconf_set(pctrl_dev, +- airoha_pinctrl_groups[group].pins[i], ++ pinctrl->grps[group].pins[i], + configs, num_configs); + if (err) + return err; +@@ -2853,23 +2885,16 @@ static const struct pinctrl_ops airoha_p + .dt_free_map = pinconf_generic_dt_free_map, + }; + +-static struct pinctrl_desc airoha_pinctrl_desc = { +- .name = KBUILD_MODNAME, +- .owner = THIS_MODULE, +- .pctlops = &airoha_pctlops, +- .pmxops = &airoha_pmxops, +- .confops = &airoha_confops, +- .pins = airoha_pinctrl_pins, +- .npins = ARRAY_SIZE(airoha_pinctrl_pins), +-}; +- + static int airoha_pinctrl_probe(struct platform_device *pdev) + { ++ const struct airoha_pinctrl_match_data *data; + struct device *dev = &pdev->dev; + struct airoha_pinctrl *pinctrl; + struct regmap *map; + int err, i; + ++ data = device_get_match_data(dev); ++ + pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; +@@ -2884,14 +2909,23 @@ static int airoha_pinctrl_probe(struct p + + pinctrl->chip_scu = map; + +- err = devm_pinctrl_register_and_init(dev, &airoha_pinctrl_desc, ++ /* Init pinctrl desc struct */ ++ pinctrl->desc.name = KBUILD_MODNAME; ++ pinctrl->desc.owner = THIS_MODULE, ++ pinctrl->desc.pctlops = &airoha_pctlops, ++ pinctrl->desc.pmxops = &airoha_pmxops, ++ pinctrl->desc.confops = &airoha_confops, ++ pinctrl->desc.pins = data->pins, ++ pinctrl->desc.npins = data->num_pins, ++ ++ err = devm_pinctrl_register_and_init(dev, &pinctrl->desc, + pinctrl, &pinctrl->ctrl); + if (err) + return err; + + /* build pin groups */ +- for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_groups); i++) { +- const struct pingroup *grp = &airoha_pinctrl_groups[i]; ++ for (i = 0; i < data->num_grps; i++) { ++ const struct pingroup *grp = &data->grps[i]; + + err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name, + grp->pins, grp->npins, +@@ -2904,10 +2938,10 @@ static int airoha_pinctrl_probe(struct p + } + + /* build functions */ +- for (i = 0; i < ARRAY_SIZE(airoha_pinctrl_funcs); i++) { ++ for (i = 0; i < data->num_funcs; i++) { + const struct airoha_pinctrl_func *func; + +- func = &airoha_pinctrl_funcs[i]; ++ func = &data->funcs[i]; + err = pinmux_generic_add_function(pinctrl->ctrl, + func->desc.name, + func->desc.groups, +@@ -2920,6 +2954,10 @@ static int airoha_pinctrl_probe(struct p + } + } + ++ pinctrl->grps = data->grps; ++ pinctrl->funcs = data->funcs; ++ pinctrl->confs_info = data->confs_info; ++ + err = pinctrl_enable(pinctrl->ctrl); + if (err) + return err; +@@ -2928,8 +2966,39 @@ static int airoha_pinctrl_probe(struct p + return airoha_pinctrl_add_gpiochip(pinctrl, pdev); + } + ++static const struct airoha_pinctrl_match_data en7581_pinctrl_match_data = { ++ .pins = en7581_pinctrl_pins, ++ .num_pins = ARRAY_SIZE(en7581_pinctrl_pins), ++ .grps = en7581_pinctrl_groups, ++ .num_grps = ARRAY_SIZE(en7581_pinctrl_groups), ++ .funcs = en7581_pinctrl_funcs, ++ .num_funcs = ARRAY_SIZE(en7581_pinctrl_funcs), ++ .confs_info = { ++ [AIROHA_PINCTRL_CONFS_PULLUP] = { ++ .confs = en7581_pinctrl_pullup_conf, ++ .num_confs = ARRAY_SIZE(en7581_pinctrl_pullup_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_PULLDOWN] = { ++ .confs = en7581_pinctrl_pulldown_conf, ++ .num_confs = ARRAY_SIZE(en7581_pinctrl_pulldown_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_DRIVE_E2] = { ++ .confs = en7581_pinctrl_drive_e2_conf, ++ .num_confs = ARRAY_SIZE(en7581_pinctrl_drive_e2_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_DRIVE_E4] = { ++ .confs = en7581_pinctrl_drive_e4_conf, ++ .num_confs = ARRAY_SIZE(en7581_pinctrl_drive_e4_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_PCIE_RST_OD] = { ++ .confs = en7581_pinctrl_pcie_rst_od_conf, ++ .num_confs = ARRAY_SIZE(en7581_pinctrl_pcie_rst_od_conf), ++ }, ++ }, ++}; ++ + static const struct of_device_id airoha_pinctrl_of_match[] = { +- { .compatible = "airoha,en7581-pinctrl" }, ++ { .compatible = "airoha,en7581-pinctrl", .data = &en7581_pinctrl_match_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, airoha_pinctrl_of_match); diff --git a/target/linux/airoha/patches-6.12/109-02-v6.19-pinctrl-airoha-convert-PHY-LED-GPIO-to-macro.patch b/target/linux/airoha/patches-6.12/109-02-v6.19-pinctrl-airoha-convert-PHY-LED-GPIO-to-macro.patch new file mode 100644 index 00000000000..569fcafaa24 --- /dev/null +++ b/target/linux/airoha/patches-6.12/109-02-v6.19-pinctrl-airoha-convert-PHY-LED-GPIO-to-macro.patch @@ -0,0 +1,635 @@ +From 579839c9548cf2a85e873ad787bc2fa6610bf8ab Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Nov 2025 00:57:05 +0100 +Subject: [PATCH 2/5] pinctrl: airoha: convert PHY LED GPIO to macro + +PHY LED GPIO pinctrl struct definition is very similar across the +different 4 PHY and 2 LED and it can be generelized to a macro. + +To reduce code size, convert them to a common macro. + +Signed-off-by: Christian Marangi +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 588 ++++------------------ + 1 file changed, 100 insertions(+), 488 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -1473,516 +1473,128 @@ static const struct airoha_pinctrl_func_ + }, + }; + ++#define AIROHA_PINCTRL_PHY_LED0(gpio, mux_val, map_mask, map_val) \ ++ { \ ++ .name = (gpio), \ ++ .regmap[0] = { \ ++ AIROHA_FUNC_MUX, \ ++ REG_GPIO_2ND_I2C_MODE, \ ++ (mux_val), \ ++ (mux_val), \ ++ }, \ ++ .regmap[1] = { \ ++ AIROHA_FUNC_MUX, \ ++ REG_LAN_LED0_MAPPING, \ ++ (map_mask), \ ++ (map_val), \ ++ }, \ ++ .regmap_size = 2, \ ++ } ++ ++#define AIROHA_PINCTRL_PHY_LED1(gpio, mux_val, map_mask, map_val) \ ++ { \ ++ .name = (gpio), \ ++ .regmap[0] = { \ ++ AIROHA_FUNC_MUX, \ ++ REG_GPIO_2ND_I2C_MODE, \ ++ (mux_val), \ ++ (mux_val), \ ++ }, \ ++ .regmap[1] = { \ ++ AIROHA_FUNC_MUX, \ ++ REG_LAN_LED1_MAPPING, \ ++ (map_mask), \ ++ (map_val), \ ++ }, \ ++ .regmap_size = 2, \ ++ } ++ + static const struct airoha_pinctrl_func_group phy1_led0_func_group[] = { +- { +- .name = "gpio33", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED0_MODE_MASK, +- GPIO_LAN0_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio34", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED0_MODE_MASK, +- GPIO_LAN1_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio35", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED0_MODE_MASK, +- GPIO_LAN2_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio42", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED0("gpio33", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio34", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio35", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), + }; + + static const struct airoha_pinctrl_func_group phy2_led0_func_group[] = { +- { +- .name = "gpio33", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED0_MODE_MASK, +- GPIO_LAN0_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio34", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED0_MODE_MASK, +- GPIO_LAN1_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio35", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED0_MODE_MASK, +- GPIO_LAN2_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio42", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED0("gpio33", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio34", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio35", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(1)), + }; + + static const struct airoha_pinctrl_func_group phy3_led0_func_group[] = { +- { +- .name = "gpio33", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED0_MODE_MASK, +- GPIO_LAN0_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio34", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED0_MODE_MASK, +- GPIO_LAN1_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio35", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED0_MODE_MASK, +- GPIO_LAN2_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio42", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED0("gpio33", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio34", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio35", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + }; + + static const struct airoha_pinctrl_func_group phy4_led0_func_group[] = { +- { +- .name = "gpio33", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED0_MODE_MASK, +- GPIO_LAN0_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio34", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED0_MODE_MASK, +- GPIO_LAN1_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio35", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED0_MODE_MASK, +- GPIO_LAN2_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio42", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED0_MODE_MASK, +- GPIO_LAN3_LED0_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED0("gpio33", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio34", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio35", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), + }; + + static const struct airoha_pinctrl_func_group phy1_led1_func_group[] = { +- { +- .name = "gpio43", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED1_MODE_MASK, +- GPIO_LAN0_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio44", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED1_MODE_MASK, +- GPIO_LAN1_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio45", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED1_MODE_MASK, +- GPIO_LAN2_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio46", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED1_MODE_MASK, +- GPIO_LAN3_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(0) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED1("gpio43", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED1("gpio44", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED1("gpio45", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED1("gpio46", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), + }; + + static const struct airoha_pinctrl_func_group phy2_led1_func_group[] = { +- { +- .name = "gpio43", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED1_MODE_MASK, +- GPIO_LAN0_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio44", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED1_MODE_MASK, +- GPIO_LAN1_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio45", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED1_MODE_MASK, +- GPIO_LAN2_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio46", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED1_MODE_MASK, +- GPIO_LAN3_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(1) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED1("gpio43", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED1("gpio44", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED1("gpio45", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED1("gpio46", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(1)), + }; + + static const struct airoha_pinctrl_func_group phy3_led1_func_group[] = { +- { +- .name = "gpio43", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED1_MODE_MASK, +- GPIO_LAN0_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio44", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED1_MODE_MASK, +- GPIO_LAN1_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio45", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED1_MODE_MASK, +- GPIO_LAN2_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio46", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED1_MODE_MASK, +- GPIO_LAN3_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(2) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED1("gpio43", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio44", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio45", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio46", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + }; + + static const struct airoha_pinctrl_func_group phy4_led1_func_group[] = { +- { +- .name = "gpio43", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN0_LED1_MODE_MASK, +- GPIO_LAN0_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN0_LED_MAPPING_MASK, +- LAN0_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio44", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN1_LED1_MODE_MASK, +- GPIO_LAN1_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio45", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN2_LED1_MODE_MASK, +- GPIO_LAN2_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, { +- .name = "gpio46", +- .regmap[0] = { +- AIROHA_FUNC_MUX, +- REG_GPIO_2ND_I2C_MODE, +- GPIO_LAN3_LED1_MODE_MASK, +- GPIO_LAN3_LED1_MODE_MASK +- }, +- .regmap[1] = { +- AIROHA_FUNC_MUX, +- REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY_LED_MAP(3) +- }, +- .regmap_size = 2, +- }, ++ AIROHA_PINCTRL_PHY_LED1("gpio43", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio44", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio45", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio46", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + }; + + static const struct airoha_pinctrl_func en7581_pinctrl_funcs[] = { diff --git a/target/linux/airoha/patches-6.12/109-03-v6.19-pinctrl-airoha-convert-PWM-GPIO-to-macro.patch b/target/linux/airoha/patches-6.12/109-03-v6.19-pinctrl-airoha-convert-PWM-GPIO-to-macro.patch new file mode 100644 index 00000000000..cab0517f7ec --- /dev/null +++ b/target/linux/airoha/patches-6.12/109-03-v6.19-pinctrl-airoha-convert-PWM-GPIO-to-macro.patch @@ -0,0 +1,492 @@ +From 1552ad5d649cff9d170e5bc1d13ab1487333b4b7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Nov 2025 00:57:06 +0100 +Subject: [PATCH 3/5] pinctrl: airoha: convert PWM GPIO to macro + +The PWM GPIO struct definition follow the same pattern for every GPIO +pin hence it can be converted to a macro. + +Create 2 macro one for normal mux and one for ext mux and convert all +the entry to these new macro to reduce code size. + +Signed-off-by: Christian Marangi +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 465 ++++------------------ + 1 file changed, 68 insertions(+), 397 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -1073,404 +1073,75 @@ static const struct airoha_pinctrl_func_ + }; + + /* PWM */ ++#define AIROHA_PINCTRL_PWM(gpio, mux_val) \ ++ { \ ++ .name = (gpio), \ ++ .regmap[0] = { \ ++ AIROHA_FUNC_PWM_MUX, \ ++ REG_GPIO_FLASH_MODE_CFG, \ ++ (mux_val), \ ++ (mux_val) \ ++ }, \ ++ .regmap_size = 1, \ ++ } \ ++ ++#define AIROHA_PINCTRL_PWM_EXT(gpio, mux_val) \ ++ { \ ++ .name = (gpio), \ ++ .regmap[0] = { \ ++ AIROHA_FUNC_PWM_EXT_MUX, \ ++ REG_GPIO_FLASH_MODE_CFG_EXT, \ ++ (mux_val), \ ++ (mux_val) \ ++ }, \ ++ .regmap_size = 1, \ ++ } \ ++ + static const struct airoha_pinctrl_func_group pwm_func_group[] = { +- { +- .name = "gpio0", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO0_FLASH_MODE_CFG, +- GPIO0_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio1", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO1_FLASH_MODE_CFG, +- GPIO1_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio2", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO2_FLASH_MODE_CFG, +- GPIO2_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio3", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO3_FLASH_MODE_CFG, +- GPIO3_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio4", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO4_FLASH_MODE_CFG, +- GPIO4_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio5", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO5_FLASH_MODE_CFG, +- GPIO5_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio6", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO6_FLASH_MODE_CFG, +- GPIO6_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio7", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO7_FLASH_MODE_CFG, +- GPIO7_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio8", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO8_FLASH_MODE_CFG, +- GPIO8_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio9", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO9_FLASH_MODE_CFG, +- GPIO9_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio10", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO10_FLASH_MODE_CFG, +- GPIO10_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio11", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO11_FLASH_MODE_CFG, +- GPIO11_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio12", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO12_FLASH_MODE_CFG, +- GPIO12_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio13", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO13_FLASH_MODE_CFG, +- GPIO13_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio14", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO14_FLASH_MODE_CFG, +- GPIO14_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio15", +- .regmap[0] = { +- AIROHA_FUNC_PWM_MUX, +- REG_GPIO_FLASH_MODE_CFG, +- GPIO15_FLASH_MODE_CFG, +- GPIO15_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio16", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO16_FLASH_MODE_CFG, +- GPIO16_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio17", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO17_FLASH_MODE_CFG, +- GPIO17_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio18", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO18_FLASH_MODE_CFG, +- GPIO18_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio19", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO19_FLASH_MODE_CFG, +- GPIO19_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio20", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO20_FLASH_MODE_CFG, +- GPIO20_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio21", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO21_FLASH_MODE_CFG, +- GPIO21_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio22", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO22_FLASH_MODE_CFG, +- GPIO22_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio23", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO23_FLASH_MODE_CFG, +- GPIO23_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio24", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO24_FLASH_MODE_CFG, +- GPIO24_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio25", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO25_FLASH_MODE_CFG, +- GPIO25_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio26", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO26_FLASH_MODE_CFG, +- GPIO26_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio27", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO27_FLASH_MODE_CFG, +- GPIO27_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio28", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO28_FLASH_MODE_CFG, +- GPIO28_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio29", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO29_FLASH_MODE_CFG, +- GPIO29_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio30", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO30_FLASH_MODE_CFG, +- GPIO30_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio31", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO31_FLASH_MODE_CFG, +- GPIO31_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio36", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO36_FLASH_MODE_CFG, +- GPIO36_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio37", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO37_FLASH_MODE_CFG, +- GPIO37_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio38", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO38_FLASH_MODE_CFG, +- GPIO38_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio39", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO39_FLASH_MODE_CFG, +- GPIO39_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio40", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO40_FLASH_MODE_CFG, +- GPIO40_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio41", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO41_FLASH_MODE_CFG, +- GPIO41_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio42", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO42_FLASH_MODE_CFG, +- GPIO42_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio43", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO43_FLASH_MODE_CFG, +- GPIO43_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio44", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO44_FLASH_MODE_CFG, +- GPIO44_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio45", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO45_FLASH_MODE_CFG, +- GPIO45_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio46", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO46_FLASH_MODE_CFG, +- GPIO46_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, { +- .name = "gpio47", +- .regmap[0] = { +- AIROHA_FUNC_PWM_EXT_MUX, +- REG_GPIO_FLASH_MODE_CFG_EXT, +- GPIO47_FLASH_MODE_CFG, +- GPIO47_FLASH_MODE_CFG +- }, +- .regmap_size = 1, +- }, ++ AIROHA_PINCTRL_PWM("gpio0", GPIO0_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio1", GPIO1_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio2", GPIO2_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio3", GPIO3_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio4", GPIO4_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio5", GPIO5_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio6", GPIO6_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio7", GPIO7_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio8", GPIO8_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio9", GPIO9_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio10", GPIO10_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio11", GPIO11_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio12", GPIO12_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio13", GPIO13_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio14", GPIO14_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM("gpio15", GPIO15_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio16", GPIO16_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio17", GPIO17_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio18", GPIO18_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio19", GPIO19_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio20", GPIO20_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio21", GPIO21_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio22", GPIO22_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio23", GPIO23_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio24", GPIO24_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio25", GPIO25_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio26", GPIO26_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio27", GPIO27_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio28", GPIO28_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio29", GPIO29_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio30", GPIO30_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio31", GPIO31_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio36", GPIO36_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio37", GPIO37_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio38", GPIO38_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio39", GPIO39_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio40", GPIO40_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio41", GPIO41_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio42", GPIO42_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio43", GPIO43_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio44", GPIO44_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio45", GPIO45_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio46", GPIO46_FLASH_MODE_CFG), ++ AIROHA_PINCTRL_PWM_EXT("gpio47", GPIO47_FLASH_MODE_CFG), + }; + + #define AIROHA_PINCTRL_PHY_LED0(gpio, mux_val, map_mask, map_val) \ diff --git a/target/linux/airoha/patches-6.12/109-05-v6.19-pinctrl-airoha-add-support-for-Airoha-AN7583-PINs.patch b/target/linux/airoha/patches-6.12/109-05-v6.19-pinctrl-airoha-add-support-for-Airoha-AN7583-PINs.patch new file mode 100644 index 00000000000..b33af2bb78b --- /dev/null +++ b/target/linux/airoha/patches-6.12/109-05-v6.19-pinctrl-airoha-add-support-for-Airoha-AN7583-PINs.patch @@ -0,0 +1,969 @@ +From 3ffeb17a9a27a668efb6fbd074835e187910a9bb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Nov 2025 00:57:08 +0100 +Subject: [PATCH 5/5] pinctrl: airoha: add support for Airoha AN7583 PINs + +Add all the required entry to add suppot for Airoha AN7583 PINs. + +Where possible the same function group are used from Airoha EN7581 to +reduce code duplication. + +Signed-off-by: Christian Marangi +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 747 +++++++++++++++++++++- + 1 file changed, 740 insertions(+), 7 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -70,6 +70,7 @@ + #define GPIO_PCM_SPI_CS3_MODE_MASK BIT(20) + #define GPIO_PCM_SPI_CS2_MODE_P156_MASK BIT(19) + #define GPIO_PCM_SPI_CS2_MODE_P128_MASK BIT(18) ++#define AN7583_GPIO_PCM_SPI_CS2_MODE_MASK BIT(18) + #define GPIO_PCM_SPI_CS1_MODE_MASK BIT(17) + #define GPIO_PCM_SPI_MODE_MASK BIT(16) + #define GPIO_PCM2_MODE_MASK BIT(13) +@@ -127,6 +128,8 @@ + + /* CONF */ + #define REG_I2C_SDA_E2 0x001c ++#define AN7583_I2C1_SCL_E2_MASK BIT(16) ++#define AN7583_I2C1_SDA_E2_MASK BIT(15) + #define SPI_MISO_E2_MASK BIT(14) + #define SPI_MOSI_E2_MASK BIT(13) + #define SPI_CLK_E2_MASK BIT(12) +@@ -134,12 +137,16 @@ + #define PCIE2_RESET_E2_MASK BIT(10) + #define PCIE1_RESET_E2_MASK BIT(9) + #define PCIE0_RESET_E2_MASK BIT(8) ++#define AN7583_MDIO_0_E2_MASK BIT(5) ++#define AN7583_MDC_0_E2_MASK BIT(4) + #define UART1_RXD_E2_MASK BIT(3) + #define UART1_TXD_E2_MASK BIT(2) + #define I2C_SCL_E2_MASK BIT(1) + #define I2C_SDA_E2_MASK BIT(0) + + #define REG_I2C_SDA_E4 0x0020 ++#define AN7583_I2C1_SCL_E4_MASK BIT(16) ++#define AN7583_I2C1_SDA_E4_MASK BIT(15) + #define SPI_MISO_E4_MASK BIT(14) + #define SPI_MOSI_E4_MASK BIT(13) + #define SPI_CLK_E4_MASK BIT(12) +@@ -147,6 +154,8 @@ + #define PCIE2_RESET_E4_MASK BIT(10) + #define PCIE1_RESET_E4_MASK BIT(9) + #define PCIE0_RESET_E4_MASK BIT(8) ++#define AN7583_MDIO_0_E4_MASK BIT(5) ++#define AN7583_MDC_0_E4_MASK BIT(4) + #define UART1_RXD_E4_MASK BIT(3) + #define UART1_TXD_E4_MASK BIT(2) + #define I2C_SCL_E4_MASK BIT(1) +@@ -158,6 +167,8 @@ + #define REG_GPIO_H_E4 0x0030 + + #define REG_I2C_SDA_PU 0x0044 ++#define AN7583_I2C1_SCL_PU_MASK BIT(16) ++#define AN7583_I2C1_SDA_PU_MASK BIT(15) + #define SPI_MISO_PU_MASK BIT(14) + #define SPI_MOSI_PU_MASK BIT(13) + #define SPI_CLK_PU_MASK BIT(12) +@@ -165,12 +176,16 @@ + #define PCIE2_RESET_PU_MASK BIT(10) + #define PCIE1_RESET_PU_MASK BIT(9) + #define PCIE0_RESET_PU_MASK BIT(8) ++#define AN7583_MDIO_0_PU_MASK BIT(5) ++#define AN7583_MDC_0_PU_MASK BIT(4) + #define UART1_RXD_PU_MASK BIT(3) + #define UART1_TXD_PU_MASK BIT(2) + #define I2C_SCL_PU_MASK BIT(1) + #define I2C_SDA_PU_MASK BIT(0) + + #define REG_I2C_SDA_PD 0x0048 ++#define AN7583_I2C1_SDA_PD_MASK BIT(16) ++#define AN7583_I2C1_SCL_PD_MASK BIT(15) + #define SPI_MISO_PD_MASK BIT(14) + #define SPI_MOSI_PD_MASK BIT(13) + #define SPI_CLK_PD_MASK BIT(12) +@@ -178,6 +193,8 @@ + #define PCIE2_RESET_PD_MASK BIT(10) + #define PCIE1_RESET_PD_MASK BIT(9) + #define PCIE0_RESET_PD_MASK BIT(8) ++#define AN7583_MDIO_0_PD_MASK BIT(5) ++#define AN7583_MDC_0_PD_MASK BIT(4) + #define UART1_RXD_PD_MASK BIT(3) + #define UART1_TXD_PD_MASK BIT(2) + #define I2C_SCL_PD_MASK BIT(1) +@@ -625,10 +642,223 @@ static const struct pingroup en7581_pinc + PINCTRL_PIN_GROUP("pcie_reset2", en7581_pcie_reset2), + }; + ++static struct pinctrl_pin_desc an7583_pinctrl_pins[] = { ++ PINCTRL_PIN(2, "gpio0"), ++ PINCTRL_PIN(3, "gpio1"), ++ PINCTRL_PIN(4, "gpio2"), ++ PINCTRL_PIN(5, "gpio3"), ++ PINCTRL_PIN(6, "gpio4"), ++ PINCTRL_PIN(7, "gpio5"), ++ PINCTRL_PIN(8, "gpio6"), ++ PINCTRL_PIN(9, "gpio7"), ++ PINCTRL_PIN(10, "gpio8"), ++ PINCTRL_PIN(11, "gpio9"), ++ PINCTRL_PIN(12, "gpio10"), ++ PINCTRL_PIN(13, "gpio11"), ++ PINCTRL_PIN(14, "gpio12"), ++ PINCTRL_PIN(15, "gpio13"), ++ PINCTRL_PIN(16, "gpio14"), ++ PINCTRL_PIN(17, "gpio15"), ++ PINCTRL_PIN(18, "gpio16"), ++ PINCTRL_PIN(19, "gpio17"), ++ PINCTRL_PIN(20, "gpio18"), ++ PINCTRL_PIN(21, "gpio19"), ++ PINCTRL_PIN(22, "gpio20"), ++ PINCTRL_PIN(23, "gpio21"), ++ PINCTRL_PIN(24, "gpio22"), ++ PINCTRL_PIN(25, "gpio23"), ++ PINCTRL_PIN(26, "gpio24"), ++ PINCTRL_PIN(27, "gpio25"), ++ PINCTRL_PIN(28, "gpio26"), ++ PINCTRL_PIN(29, "gpio27"), ++ PINCTRL_PIN(30, "gpio28"), ++ PINCTRL_PIN(31, "gpio29"), ++ PINCTRL_PIN(32, "gpio30"), ++ PINCTRL_PIN(33, "gpio31"), ++ PINCTRL_PIN(34, "gpio32"), ++ PINCTRL_PIN(35, "gpio33"), ++ PINCTRL_PIN(36, "gpio34"), ++ PINCTRL_PIN(37, "gpio35"), ++ PINCTRL_PIN(38, "gpio36"), ++ PINCTRL_PIN(39, "gpio37"), ++ PINCTRL_PIN(40, "gpio38"), ++ PINCTRL_PIN(41, "i2c0_scl"), ++ PINCTRL_PIN(42, "i2c0_sda"), ++ PINCTRL_PIN(43, "i2c1_scl"), ++ PINCTRL_PIN(44, "i2c1_sda"), ++ PINCTRL_PIN(45, "spi_clk"), ++ PINCTRL_PIN(46, "spi_cs"), ++ PINCTRL_PIN(47, "spi_mosi"), ++ PINCTRL_PIN(48, "spi_miso"), ++ PINCTRL_PIN(49, "uart_txd"), ++ PINCTRL_PIN(50, "uart_rxd"), ++ PINCTRL_PIN(51, "pcie_reset0"), ++ PINCTRL_PIN(52, "pcie_reset1"), ++ PINCTRL_PIN(53, "mdc_0"), ++ PINCTRL_PIN(54, "mdio_0"), ++}; ++ ++static const int an7583_pon_pins[] = { 15, 16, 17, 18, 19, 20 }; ++static const int an7583_pon_tod_1pps_pins[] = { 32 }; ++static const int an7583_gsw_tod_1pps_pins[] = { 32 }; ++static const int an7583_sipo_pins[] = { 34, 35 }; ++static const int an7583_sipo_rclk_pins[] = { 34, 35, 33 }; ++static const int an7583_mdio_pins[] = { 43, 44 }; ++static const int an7583_uart2_pins[] = { 34, 35 }; ++static const int an7583_uart2_cts_rts_pins[] = { 32, 33 }; ++static const int an7583_hsuart_pins[] = { 30, 31 }; ++static const int an7583_hsuart_cts_rts_pins[] = { 28, 29 }; ++static const int an7583_npu_uart_pins[] = { 7, 8 }; ++static const int an7583_uart4_pins[] = { 7, 8 }; ++static const int an7583_uart5_pins[] = { 23, 24 }; ++static const int an7583_i2c0_pins[] = { 41, 42 }; ++static const int an7583_i2c1_pins[] = { 43, 44 }; ++static const int an7583_jtag_udi_pins[] = { 23, 24, 22, 25, 26 }; ++static const int an7583_jtag_dfd_pins[] = { 23, 24, 22, 25, 26 }; ++static const int an7583_pcm1_pins[] = { 10, 11, 12, 13, 14 }; ++static const int an7583_pcm2_pins[] = { 28, 29, 30, 31, 24 }; ++static const int an7583_spi_pins[] = { 28, 29, 30, 31 }; ++static const int an7583_spi_quad_pins[] = { 25, 26 }; ++static const int an7583_spi_cs1_pins[] = { 27 }; ++static const int an7583_pcm_spi_pins[] = { 28, 29, 30, 31, 10, 11, 12, 13 }; ++static const int an7583_pcm_spi_rst_pins[] = { 14 }; ++static const int an7583_pcm_spi_cs1_pins[] = { 24 }; ++static const int an7583_emmc_pins[] = { 7, 8, 9, 22, 23, 24, 25, 26, 45, 46, 47 }; ++static const int an7583_pnand_pins[] = { 7, 8, 9, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 45, 46, 47, 48 }; ++static const int an7583_gpio0_pins[] = { 2 }; ++static const int an7583_gpio1_pins[] = { 3 }; ++static const int an7583_gpio2_pins[] = { 4 }; ++static const int an7583_gpio3_pins[] = { 5 }; ++static const int an7583_gpio4_pins[] = { 6 }; ++static const int an7583_gpio5_pins[] = { 7 }; ++static const int an7583_gpio6_pins[] = { 8 }; ++static const int an7583_gpio7_pins[] = { 9 }; ++static const int an7583_gpio8_pins[] = { 10 }; ++static const int an7583_gpio9_pins[] = { 11 }; ++static const int an7583_gpio10_pins[] = { 12 }; ++static const int an7583_gpio11_pins[] = { 13 }; ++static const int an7583_gpio12_pins[] = { 14 }; ++static const int an7583_gpio13_pins[] = { 15 }; ++static const int an7583_gpio14_pins[] = { 16 }; ++static const int an7583_gpio15_pins[] = { 17 }; ++static const int an7583_gpio16_pins[] = { 18 }; ++static const int an7583_gpio17_pins[] = { 19 }; ++static const int an7583_gpio18_pins[] = { 20 }; ++static const int an7583_gpio19_pins[] = { 21 }; ++static const int an7583_gpio20_pins[] = { 22 }; ++static const int an7583_gpio21_pins[] = { 24 }; ++static const int an7583_gpio23_pins[] = { 25 }; ++static const int an7583_gpio24_pins[] = { 26 }; ++static const int an7583_gpio25_pins[] = { 27 }; ++static const int an7583_gpio26_pins[] = { 28 }; ++static const int an7583_gpio27_pins[] = { 29 }; ++static const int an7583_gpio28_pins[] = { 30 }; ++static const int an7583_gpio29_pins[] = { 31 }; ++static const int an7583_gpio30_pins[] = { 32 }; ++static const int an7583_gpio31_pins[] = { 33 }; ++static const int an7583_gpio33_pins[] = { 35 }; ++static const int an7583_gpio34_pins[] = { 36 }; ++static const int an7583_gpio35_pins[] = { 37 }; ++static const int an7583_gpio36_pins[] = { 38 }; ++static const int an7583_gpio37_pins[] = { 39 }; ++static const int an7583_gpio38_pins[] = { 40 }; ++static const int an7583_gpio39_pins[] = { 41 }; ++static const int an7583_gpio40_pins[] = { 42 }; ++static const int an7583_gpio41_pins[] = { 43 }; ++static const int an7583_gpio42_pins[] = { 44 }; ++static const int an7583_gpio43_pins[] = { 45 }; ++static const int an7583_gpio44_pins[] = { 46 }; ++static const int an7583_gpio45_pins[] = { 47 }; ++static const int an7583_gpio46_pins[] = { 48 }; ++static const int an7583_gpio47_pins[] = { 49 }; ++static const int an7583_gpio48_pins[] = { 50 }; ++static const int an7583_pcie_reset0_pins[] = { 51 }; ++static const int an7583_pcie_reset1_pins[] = { 52 }; ++ ++static const struct pingroup an7583_pinctrl_groups[] = { ++ PINCTRL_PIN_GROUP("pon", an7583_pon), ++ PINCTRL_PIN_GROUP("pon_tod_1pps", an7583_pon_tod_1pps), ++ PINCTRL_PIN_GROUP("gsw_tod_1pps", an7583_gsw_tod_1pps), ++ PINCTRL_PIN_GROUP("sipo", an7583_sipo), ++ PINCTRL_PIN_GROUP("sipo_rclk", an7583_sipo_rclk), ++ PINCTRL_PIN_GROUP("mdio", an7583_mdio), ++ PINCTRL_PIN_GROUP("uart2", an7583_uart2), ++ PINCTRL_PIN_GROUP("uart2_cts_rts", an7583_uart2_cts_rts), ++ PINCTRL_PIN_GROUP("hsuart", an7583_hsuart), ++ PINCTRL_PIN_GROUP("hsuart_cts_rts", an7583_hsuart_cts_rts), ++ PINCTRL_PIN_GROUP("npu_uart", an7583_npu_uart), ++ PINCTRL_PIN_GROUP("uart4", an7583_uart4), ++ PINCTRL_PIN_GROUP("uart5", an7583_uart5), ++ PINCTRL_PIN_GROUP("i2c0", an7583_i2c0), ++ PINCTRL_PIN_GROUP("i2c1", an7583_i2c1), ++ PINCTRL_PIN_GROUP("jtag_udi", an7583_jtag_udi), ++ PINCTRL_PIN_GROUP("jtag_dfd", an7583_jtag_dfd), ++ PINCTRL_PIN_GROUP("pcm1", an7583_pcm1), ++ PINCTRL_PIN_GROUP("pcm2", an7583_pcm2), ++ PINCTRL_PIN_GROUP("spi", an7583_spi), ++ PINCTRL_PIN_GROUP("spi_quad", an7583_spi_quad), ++ PINCTRL_PIN_GROUP("spi_cs1", an7583_spi_cs1), ++ PINCTRL_PIN_GROUP("pcm_spi", an7583_pcm_spi), ++ PINCTRL_PIN_GROUP("pcm_spi_rst", an7583_pcm_spi_rst), ++ PINCTRL_PIN_GROUP("pcm_spi_cs1", an7583_pcm_spi_cs1), ++ PINCTRL_PIN_GROUP("emmc", an7583_emmc), ++ PINCTRL_PIN_GROUP("pnand", an7583_pnand), ++ PINCTRL_PIN_GROUP("gpio0", an7583_gpio0), ++ PINCTRL_PIN_GROUP("gpio1", an7583_gpio1), ++ PINCTRL_PIN_GROUP("gpio2", an7583_gpio2), ++ PINCTRL_PIN_GROUP("gpio3", an7583_gpio3), ++ PINCTRL_PIN_GROUP("gpio4", an7583_gpio4), ++ PINCTRL_PIN_GROUP("gpio5", an7583_gpio5), ++ PINCTRL_PIN_GROUP("gpio6", an7583_gpio6), ++ PINCTRL_PIN_GROUP("gpio7", an7583_gpio7), ++ PINCTRL_PIN_GROUP("gpio8", an7583_gpio8), ++ PINCTRL_PIN_GROUP("gpio9", an7583_gpio9), ++ PINCTRL_PIN_GROUP("gpio10", an7583_gpio10), ++ PINCTRL_PIN_GROUP("gpio11", an7583_gpio11), ++ PINCTRL_PIN_GROUP("gpio12", an7583_gpio12), ++ PINCTRL_PIN_GROUP("gpio13", an7583_gpio13), ++ PINCTRL_PIN_GROUP("gpio14", an7583_gpio14), ++ PINCTRL_PIN_GROUP("gpio15", an7583_gpio15), ++ PINCTRL_PIN_GROUP("gpio16", an7583_gpio16), ++ PINCTRL_PIN_GROUP("gpio17", an7583_gpio17), ++ PINCTRL_PIN_GROUP("gpio18", an7583_gpio18), ++ PINCTRL_PIN_GROUP("gpio19", an7583_gpio19), ++ PINCTRL_PIN_GROUP("gpio20", an7583_gpio20), ++ PINCTRL_PIN_GROUP("gpio21", an7583_gpio21), ++ PINCTRL_PIN_GROUP("gpio23", an7583_gpio23), ++ PINCTRL_PIN_GROUP("gpio24", an7583_gpio24), ++ PINCTRL_PIN_GROUP("gpio25", an7583_gpio25), ++ PINCTRL_PIN_GROUP("gpio26", an7583_gpio26), ++ PINCTRL_PIN_GROUP("gpio27", an7583_gpio27), ++ PINCTRL_PIN_GROUP("gpio28", an7583_gpio28), ++ PINCTRL_PIN_GROUP("gpio29", an7583_gpio29), ++ PINCTRL_PIN_GROUP("gpio30", an7583_gpio30), ++ PINCTRL_PIN_GROUP("gpio31", an7583_gpio31), ++ PINCTRL_PIN_GROUP("gpio33", an7583_gpio33), ++ PINCTRL_PIN_GROUP("gpio34", an7583_gpio34), ++ PINCTRL_PIN_GROUP("gpio35", an7583_gpio35), ++ PINCTRL_PIN_GROUP("gpio36", an7583_gpio36), ++ PINCTRL_PIN_GROUP("gpio37", an7583_gpio37), ++ PINCTRL_PIN_GROUP("gpio38", an7583_gpio38), ++ PINCTRL_PIN_GROUP("gpio39", an7583_gpio39), ++ PINCTRL_PIN_GROUP("gpio40", an7583_gpio40), ++ PINCTRL_PIN_GROUP("gpio41", an7583_gpio41), ++ PINCTRL_PIN_GROUP("gpio42", an7583_gpio42), ++ PINCTRL_PIN_GROUP("gpio43", an7583_gpio43), ++ PINCTRL_PIN_GROUP("gpio44", an7583_gpio44), ++ PINCTRL_PIN_GROUP("gpio45", an7583_gpio45), ++ PINCTRL_PIN_GROUP("gpio46", an7583_gpio46), ++ PINCTRL_PIN_GROUP("gpio47", an7583_gpio47), ++ PINCTRL_PIN_GROUP("gpio48", an7583_gpio48), ++ PINCTRL_PIN_GROUP("pcie_reset0", an7583_pcie_reset0), ++ PINCTRL_PIN_GROUP("pcie_reset1", an7583_pcie_reset1), ++}; ++ + static const char *const pon_groups[] = { "pon" }; + static const char *const tod_1pps_groups[] = { "pon_tod_1pps", "gsw_tod_1pps" }; + static const char *const sipo_groups[] = { "sipo", "sipo_rclk" }; + static const char *const mdio_groups[] = { "mdio" }; ++static const char *const an7583_mdio_groups[] = { "mdio" }; + static const char *const uart_groups[] = { "uart2", "uart2_cts_rts", "hsuart", + "hsuart_cts_rts", "uart4", + "uart5" }; +@@ -641,11 +871,16 @@ static const char *const pcm_spi_groups[ + "pcm_spi_cs2_p156", + "pcm_spi_cs2_p128", + "pcm_spi_cs3", "pcm_spi_cs4" }; ++static const char *const an7583_pcm_spi_groups[] = { "pcm_spi", "pcm_spi_int", ++ "pcm_spi_rst", "pcm_spi_cs1", ++ "pcm_spi_cs2", "pcm_spi_cs3", ++ "pcm_spi_cs4" }; + static const char *const i2s_groups[] = { "i2s" }; + static const char *const emmc_groups[] = { "emmc" }; + static const char *const pnand_groups[] = { "pnand" }; + static const char *const pcie_reset_groups[] = { "pcie_reset0", "pcie_reset1", + "pcie_reset2" }; ++static const char *const an7583_pcie_reset_groups[] = { "pcie_reset0", "pcie_reset1" }; + static const char *const pwm_groups[] = { "gpio0", "gpio1", + "gpio2", "gpio3", + "gpio4", "gpio5", +@@ -684,6 +919,22 @@ static const char *const phy3_led1_group + "gpio45", "gpio46" }; + static const char *const phy4_led1_groups[] = { "gpio43", "gpio44", + "gpio45", "gpio46" }; ++static const char *const an7583_phy1_led0_groups[] = { "gpio1", "gpio2", ++ "gpio3", "gpio4" }; ++static const char *const an7583_phy2_led0_groups[] = { "gpio1", "gpio2", ++ "gpio3", "gpio4" }; ++static const char *const an7583_phy3_led0_groups[] = { "gpio1", "gpio2", ++ "gpio3", "gpio4" }; ++static const char *const an7583_phy4_led0_groups[] = { "gpio1", "gpio2", ++ "gpio3", "gpio4" }; ++static const char *const an7583_phy1_led1_groups[] = { "gpio8", "gpio9", ++ "gpio10", "gpio11" }; ++static const char *const an7583_phy2_led1_groups[] = { "gpio8", "gpio9", ++ "gpio10", "gpio11" }; ++static const char *const an7583_phy3_led1_groups[] = { "gpio8", "gpio9", ++ "gpio10", "gpio11" }; ++static const char *const an7583_phy4_led1_groups[] = { "gpio8", "gpio9", ++ "gpio10", "gpio11" }; + + static const struct airoha_pinctrl_func_group pon_func_group[] = { + { +@@ -761,6 +1012,25 @@ static const struct airoha_pinctrl_func_ + }, + }; + ++static const struct airoha_pinctrl_func_group an7583_mdio_func_group[] = { ++ { ++ .name = "mdio", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_SGMII_MDIO_MODE_MASK, ++ GPIO_SGMII_MDIO_MODE_MASK ++ }, ++ .regmap[1] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_MDC_IO_MASTER_MODE_MODE, ++ GPIO_MDC_IO_MASTER_MODE_MODE ++ }, ++ .regmap_size = 2, ++ }, ++}; ++ + static const struct airoha_pinctrl_func_group uart_func_group[] = { + { + .name = "uart2", +@@ -1002,6 +1272,73 @@ static const struct airoha_pinctrl_func_ + }, + }; + ++static const struct airoha_pinctrl_func_group an7583_pcm_spi_func_group[] = { ++ { ++ .name = "pcm_spi", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_MODE_MASK, ++ GPIO_PCM_SPI_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_int", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_INT_MODE_MASK, ++ GPIO_PCM_INT_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_rst", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_RESET_MODE_MASK, ++ GPIO_PCM_RESET_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS1_MODE_MASK, ++ GPIO_PCM_SPI_CS1_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs2", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ AN7583_GPIO_PCM_SPI_CS2_MODE_MASK, ++ AN7583_GPIO_PCM_SPI_CS2_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs3", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS3_MODE_MASK, ++ GPIO_PCM_SPI_CS3_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcm_spi_cs4", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_SPI_CS1_MODE, ++ GPIO_PCM_SPI_CS4_MODE_MASK, ++ GPIO_PCM_SPI_CS4_MODE_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ + static const struct airoha_pinctrl_func_group i2s_func_group[] = { + { + .name = "i2s", +@@ -1072,6 +1409,28 @@ static const struct airoha_pinctrl_func_ + }, + }; + ++static const struct airoha_pinctrl_func_group an7583_pcie_reset_func_group[] = { ++ { ++ .name = "pcie_reset0", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET0_MASK, ++ GPIO_PCIE_RESET0_MASK ++ }, ++ .regmap_size = 1, ++ }, { ++ .name = "pcie_reset1", ++ .regmap[0] = { ++ AIROHA_FUNC_MUX, ++ REG_GPIO_PON_MODE, ++ GPIO_PCIE_RESET1_MASK, ++ GPIO_PCIE_RESET1_MASK ++ }, ++ .regmap_size = 1, ++ }, ++}; ++ + /* PWM */ + #define AIROHA_PINCTRL_PWM(gpio, mux_val) \ + { \ +@@ -1268,6 +1627,94 @@ static const struct airoha_pinctrl_func_ + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + }; + ++static const struct airoha_pinctrl_func_group an7583_phy1_led0_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED0("gpio1", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio2", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio3", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio4", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy2_led0_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED0("gpio1", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio2", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio3", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio4", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(1)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy3_led0_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED0("gpio1", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio2", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio3", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio4", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy4_led0_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED0("gpio1", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio2", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio3", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio4", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy1_led1_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED1("gpio8", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED1("gpio9", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED1("gpio1", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy2_led1_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED1("gpio8", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED1("gpio9", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(1)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy3_led1_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED1("gpio8", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio9", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), ++}; ++ ++static const struct airoha_pinctrl_func_group an7583_phy4_led1_func_group[] = { ++ AIROHA_PINCTRL_PHY_LED1("gpio8", GPIO_LAN0_LED1_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio9", GPIO_LAN1_LED1_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio10", GPIO_LAN2_LED1_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED1("gpio11", GPIO_LAN3_LED1_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), ++}; ++ + static const struct airoha_pinctrl_func en7581_pinctrl_funcs[] = { + PINCTRL_FUNC_DESC("pon", pon), + PINCTRL_FUNC_DESC("tod_1pps", tod_1pps), +@@ -1294,6 +1741,31 @@ static const struct airoha_pinctrl_func + PINCTRL_FUNC_DESC("phy4_led1", phy4_led1), + }; + ++static const struct airoha_pinctrl_func an7583_pinctrl_funcs[] = { ++ PINCTRL_FUNC_DESC("pon", pon), ++ PINCTRL_FUNC_DESC("tod_1pps", tod_1pps), ++ PINCTRL_FUNC_DESC("sipo", sipo), ++ PINCTRL_FUNC_DESC("mdio", an7583_mdio), ++ PINCTRL_FUNC_DESC("uart", uart), ++ PINCTRL_FUNC_DESC("i2c", i2c), ++ PINCTRL_FUNC_DESC("jtag", jtag), ++ PINCTRL_FUNC_DESC("pcm", pcm), ++ PINCTRL_FUNC_DESC("spi", spi), ++ PINCTRL_FUNC_DESC("pcm_spi", an7583_pcm_spi), ++ PINCTRL_FUNC_DESC("emmc", emmc), ++ PINCTRL_FUNC_DESC("pnand", pnand), ++ PINCTRL_FUNC_DESC("pcie_reset", an7583_pcie_reset), ++ PINCTRL_FUNC_DESC("pwm", pwm), ++ PINCTRL_FUNC_DESC("phy1_led0", an7583_phy1_led0), ++ PINCTRL_FUNC_DESC("phy2_led0", an7583_phy2_led0), ++ PINCTRL_FUNC_DESC("phy3_led0", an7583_phy3_led0), ++ PINCTRL_FUNC_DESC("phy4_led0", an7583_phy4_led0), ++ PINCTRL_FUNC_DESC("phy1_led1", an7583_phy1_led1), ++ PINCTRL_FUNC_DESC("phy2_led1", an7583_phy2_led1), ++ PINCTRL_FUNC_DESC("phy3_led1", an7583_phy3_led1), ++ PINCTRL_FUNC_DESC("phy4_led1", an7583_phy4_led1), ++}; ++ + static const struct airoha_pinctrl_conf en7581_pinctrl_pullup_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), +@@ -1355,6 +1827,62 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_PU, PCIE2_RESET_PU_MASK), + }; + ++static const struct airoha_pinctrl_conf an7583_pinctrl_pullup_conf[] = { ++ PINCTRL_CONF_DESC(2, REG_GPIO_L_PU, BIT(0)), ++ PINCTRL_CONF_DESC(3, REG_GPIO_L_PU, BIT(1)), ++ PINCTRL_CONF_DESC(4, REG_GPIO_L_PU, BIT(2)), ++ PINCTRL_CONF_DESC(5, REG_GPIO_L_PU, BIT(3)), ++ PINCTRL_CONF_DESC(6, REG_GPIO_L_PU, BIT(4)), ++ PINCTRL_CONF_DESC(7, REG_GPIO_L_PU, BIT(5)), ++ PINCTRL_CONF_DESC(8, REG_GPIO_L_PU, BIT(6)), ++ PINCTRL_CONF_DESC(9, REG_GPIO_L_PU, BIT(7)), ++ PINCTRL_CONF_DESC(10, REG_GPIO_L_PU, BIT(8)), ++ PINCTRL_CONF_DESC(11, REG_GPIO_L_PU, BIT(9)), ++ PINCTRL_CONF_DESC(12, REG_GPIO_L_PU, BIT(10)), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_PU, BIT(11)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_PU, BIT(12)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_PU, BIT(13)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_PU, BIT(14)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_PU, BIT(15)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_PU, BIT(16)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_PU, BIT(17)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_PU, BIT(18)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_PU, BIT(18)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_PU, BIT(20)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_PU, BIT(21)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_PU, BIT(22)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_PU, BIT(23)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_PU, BIT(24)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_PU, BIT(25)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_PU, BIT(26)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_PU, BIT(27)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_PU, BIT(28)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_PU, BIT(29)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_PU, BIT(30)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_PU, BIT(31)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_H_PU, BIT(0)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_H_PU, BIT(1)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_H_PU, BIT(2)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_H_PU, BIT(3)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_H_PU, BIT(4)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_H_PU, BIT(5)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_H_PU, BIT(6)), ++ PINCTRL_CONF_DESC(41, REG_I2C_SDA_PU, I2C_SCL_PU_MASK), ++ PINCTRL_CONF_DESC(42, REG_I2C_SDA_PU, I2C_SDA_PU_MASK), ++ PINCTRL_CONF_DESC(43, REG_I2C_SDA_PU, AN7583_I2C1_SCL_PU_MASK), ++ PINCTRL_CONF_DESC(44, REG_I2C_SDA_PU, AN7583_I2C1_SDA_PU_MASK), ++ PINCTRL_CONF_DESC(45, REG_I2C_SDA_PU, SPI_CLK_PU_MASK), ++ PINCTRL_CONF_DESC(46, REG_I2C_SDA_PU, SPI_CS0_PU_MASK), ++ PINCTRL_CONF_DESC(47, REG_I2C_SDA_PU, SPI_MOSI_PU_MASK), ++ PINCTRL_CONF_DESC(48, REG_I2C_SDA_PU, SPI_MISO_PU_MASK), ++ PINCTRL_CONF_DESC(49, REG_I2C_SDA_PU, UART1_TXD_PU_MASK), ++ PINCTRL_CONF_DESC(50, REG_I2C_SDA_PU, UART1_RXD_PU_MASK), ++ PINCTRL_CONF_DESC(51, REG_I2C_SDA_PU, PCIE0_RESET_PU_MASK), ++ PINCTRL_CONF_DESC(52, REG_I2C_SDA_PU, PCIE1_RESET_PU_MASK), ++ PINCTRL_CONF_DESC(53, REG_I2C_SDA_PU, AN7583_MDC_0_PU_MASK), ++ PINCTRL_CONF_DESC(54, REG_I2C_SDA_PU, AN7583_MDIO_0_PU_MASK), ++}; ++ + static const struct airoha_pinctrl_conf en7581_pinctrl_pulldown_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), +@@ -1416,6 +1944,62 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_PD, PCIE2_RESET_PD_MASK), + }; + ++static const struct airoha_pinctrl_conf an7583_pinctrl_pulldown_conf[] = { ++ PINCTRL_CONF_DESC(2, REG_GPIO_L_PD, BIT(0)), ++ PINCTRL_CONF_DESC(3, REG_GPIO_L_PD, BIT(1)), ++ PINCTRL_CONF_DESC(4, REG_GPIO_L_PD, BIT(2)), ++ PINCTRL_CONF_DESC(5, REG_GPIO_L_PD, BIT(3)), ++ PINCTRL_CONF_DESC(6, REG_GPIO_L_PD, BIT(4)), ++ PINCTRL_CONF_DESC(7, REG_GPIO_L_PD, BIT(5)), ++ PINCTRL_CONF_DESC(8, REG_GPIO_L_PD, BIT(6)), ++ PINCTRL_CONF_DESC(9, REG_GPIO_L_PD, BIT(7)), ++ PINCTRL_CONF_DESC(10, REG_GPIO_L_PD, BIT(8)), ++ PINCTRL_CONF_DESC(11, REG_GPIO_L_PD, BIT(9)), ++ PINCTRL_CONF_DESC(12, REG_GPIO_L_PD, BIT(10)), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_PD, BIT(11)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_PD, BIT(12)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_PD, BIT(13)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_PD, BIT(14)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_PD, BIT(15)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_PD, BIT(16)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_PD, BIT(17)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_PD, BIT(18)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_PD, BIT(18)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_PD, BIT(20)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_PD, BIT(21)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_PD, BIT(22)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_PD, BIT(23)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_PD, BIT(24)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_PD, BIT(25)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_PD, BIT(26)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_PD, BIT(27)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_PD, BIT(28)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_PD, BIT(29)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_PD, BIT(30)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_PD, BIT(31)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_H_PD, BIT(0)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_H_PD, BIT(1)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_H_PD, BIT(2)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_H_PD, BIT(3)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_H_PD, BIT(4)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_H_PD, BIT(5)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_H_PD, BIT(6)), ++ PINCTRL_CONF_DESC(41, REG_I2C_SDA_PD, I2C_SCL_PD_MASK), ++ PINCTRL_CONF_DESC(42, REG_I2C_SDA_PD, I2C_SDA_PD_MASK), ++ PINCTRL_CONF_DESC(43, REG_I2C_SDA_PD, AN7583_I2C1_SCL_PD_MASK), ++ PINCTRL_CONF_DESC(44, REG_I2C_SDA_PD, AN7583_I2C1_SDA_PD_MASK), ++ PINCTRL_CONF_DESC(45, REG_I2C_SDA_PD, SPI_CLK_PD_MASK), ++ PINCTRL_CONF_DESC(46, REG_I2C_SDA_PD, SPI_CS0_PD_MASK), ++ PINCTRL_CONF_DESC(47, REG_I2C_SDA_PD, SPI_MOSI_PD_MASK), ++ PINCTRL_CONF_DESC(48, REG_I2C_SDA_PD, SPI_MISO_PD_MASK), ++ PINCTRL_CONF_DESC(49, REG_I2C_SDA_PD, UART1_TXD_PD_MASK), ++ PINCTRL_CONF_DESC(50, REG_I2C_SDA_PD, UART1_RXD_PD_MASK), ++ PINCTRL_CONF_DESC(51, REG_I2C_SDA_PD, PCIE0_RESET_PD_MASK), ++ PINCTRL_CONF_DESC(52, REG_I2C_SDA_PD, PCIE1_RESET_PD_MASK), ++ PINCTRL_CONF_DESC(53, REG_I2C_SDA_PD, AN7583_MDC_0_PD_MASK), ++ PINCTRL_CONF_DESC(54, REG_I2C_SDA_PD, AN7583_MDIO_0_PD_MASK), ++}; ++ + static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e2_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), +@@ -1477,6 +2061,62 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_E2, PCIE2_RESET_E2_MASK), + }; + ++static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e2_conf[] = { ++ PINCTRL_CONF_DESC(2, REG_GPIO_L_E2, BIT(0)), ++ PINCTRL_CONF_DESC(3, REG_GPIO_L_E2, BIT(1)), ++ PINCTRL_CONF_DESC(4, REG_GPIO_L_E2, BIT(2)), ++ PINCTRL_CONF_DESC(5, REG_GPIO_L_E2, BIT(3)), ++ PINCTRL_CONF_DESC(6, REG_GPIO_L_E2, BIT(4)), ++ PINCTRL_CONF_DESC(7, REG_GPIO_L_E2, BIT(5)), ++ PINCTRL_CONF_DESC(8, REG_GPIO_L_E2, BIT(6)), ++ PINCTRL_CONF_DESC(9, REG_GPIO_L_E2, BIT(7)), ++ PINCTRL_CONF_DESC(10, REG_GPIO_L_E2, BIT(8)), ++ PINCTRL_CONF_DESC(11, REG_GPIO_L_E2, BIT(9)), ++ PINCTRL_CONF_DESC(12, REG_GPIO_L_E2, BIT(10)), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_E2, BIT(11)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_E2, BIT(12)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_E2, BIT(13)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_E2, BIT(14)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_E2, BIT(15)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_E2, BIT(16)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_E2, BIT(17)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_E2, BIT(18)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_E2, BIT(18)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_E2, BIT(20)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_E2, BIT(21)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_E2, BIT(22)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_E2, BIT(23)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_E2, BIT(24)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_E2, BIT(25)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_E2, BIT(26)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_E2, BIT(27)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_E2, BIT(28)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_E2, BIT(29)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_E2, BIT(30)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_E2, BIT(31)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_H_E2, BIT(0)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_H_E2, BIT(1)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_H_E2, BIT(2)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_H_E2, BIT(3)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_H_E2, BIT(4)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_H_E2, BIT(5)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_H_E2, BIT(6)), ++ PINCTRL_CONF_DESC(41, REG_I2C_SDA_E2, I2C_SCL_E2_MASK), ++ PINCTRL_CONF_DESC(42, REG_I2C_SDA_E2, I2C_SDA_E2_MASK), ++ PINCTRL_CONF_DESC(43, REG_I2C_SDA_E2, AN7583_I2C1_SCL_E2_MASK), ++ PINCTRL_CONF_DESC(44, REG_I2C_SDA_E2, AN7583_I2C1_SDA_E2_MASK), ++ PINCTRL_CONF_DESC(45, REG_I2C_SDA_E2, SPI_CLK_E2_MASK), ++ PINCTRL_CONF_DESC(46, REG_I2C_SDA_E2, SPI_CS0_E2_MASK), ++ PINCTRL_CONF_DESC(47, REG_I2C_SDA_E2, SPI_MOSI_E2_MASK), ++ PINCTRL_CONF_DESC(48, REG_I2C_SDA_E2, SPI_MISO_E2_MASK), ++ PINCTRL_CONF_DESC(49, REG_I2C_SDA_E2, UART1_TXD_E2_MASK), ++ PINCTRL_CONF_DESC(50, REG_I2C_SDA_E2, UART1_RXD_E2_MASK), ++ PINCTRL_CONF_DESC(51, REG_I2C_SDA_E2, PCIE0_RESET_E2_MASK), ++ PINCTRL_CONF_DESC(52, REG_I2C_SDA_E2, PCIE1_RESET_E2_MASK), ++ PINCTRL_CONF_DESC(53, REG_I2C_SDA_E2, AN7583_MDC_0_E2_MASK), ++ PINCTRL_CONF_DESC(54, REG_I2C_SDA_E2, AN7583_MDIO_0_E2_MASK), ++}; ++ + static const struct airoha_pinctrl_conf en7581_pinctrl_drive_e4_conf[] = { + PINCTRL_CONF_DESC(0, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), + PINCTRL_CONF_DESC(1, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), +@@ -1538,12 +2178,73 @@ static const struct airoha_pinctrl_conf + PINCTRL_CONF_DESC(63, REG_I2C_SDA_E4, PCIE2_RESET_E4_MASK), + }; + ++static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e4_conf[] = { ++ PINCTRL_CONF_DESC(2, REG_GPIO_L_E4, BIT(0)), ++ PINCTRL_CONF_DESC(3, REG_GPIO_L_E4, BIT(1)), ++ PINCTRL_CONF_DESC(4, REG_GPIO_L_E4, BIT(2)), ++ PINCTRL_CONF_DESC(5, REG_GPIO_L_E4, BIT(3)), ++ PINCTRL_CONF_DESC(6, REG_GPIO_L_E4, BIT(4)), ++ PINCTRL_CONF_DESC(7, REG_GPIO_L_E4, BIT(5)), ++ PINCTRL_CONF_DESC(8, REG_GPIO_L_E4, BIT(6)), ++ PINCTRL_CONF_DESC(9, REG_GPIO_L_E4, BIT(7)), ++ PINCTRL_CONF_DESC(10, REG_GPIO_L_E4, BIT(8)), ++ PINCTRL_CONF_DESC(11, REG_GPIO_L_E4, BIT(9)), ++ PINCTRL_CONF_DESC(12, REG_GPIO_L_E4, BIT(10)), ++ PINCTRL_CONF_DESC(13, REG_GPIO_L_E4, BIT(11)), ++ PINCTRL_CONF_DESC(14, REG_GPIO_L_E4, BIT(12)), ++ PINCTRL_CONF_DESC(15, REG_GPIO_L_E4, BIT(13)), ++ PINCTRL_CONF_DESC(16, REG_GPIO_L_E4, BIT(14)), ++ PINCTRL_CONF_DESC(17, REG_GPIO_L_E4, BIT(15)), ++ PINCTRL_CONF_DESC(18, REG_GPIO_L_E4, BIT(16)), ++ PINCTRL_CONF_DESC(19, REG_GPIO_L_E4, BIT(17)), ++ PINCTRL_CONF_DESC(20, REG_GPIO_L_E4, BIT(18)), ++ PINCTRL_CONF_DESC(21, REG_GPIO_L_E4, BIT(18)), ++ PINCTRL_CONF_DESC(22, REG_GPIO_L_E4, BIT(20)), ++ PINCTRL_CONF_DESC(23, REG_GPIO_L_E4, BIT(21)), ++ PINCTRL_CONF_DESC(24, REG_GPIO_L_E4, BIT(22)), ++ PINCTRL_CONF_DESC(25, REG_GPIO_L_E4, BIT(23)), ++ PINCTRL_CONF_DESC(26, REG_GPIO_L_E4, BIT(24)), ++ PINCTRL_CONF_DESC(27, REG_GPIO_L_E4, BIT(25)), ++ PINCTRL_CONF_DESC(28, REG_GPIO_L_E4, BIT(26)), ++ PINCTRL_CONF_DESC(29, REG_GPIO_L_E4, BIT(27)), ++ PINCTRL_CONF_DESC(30, REG_GPIO_L_E4, BIT(28)), ++ PINCTRL_CONF_DESC(31, REG_GPIO_L_E4, BIT(29)), ++ PINCTRL_CONF_DESC(32, REG_GPIO_L_E4, BIT(30)), ++ PINCTRL_CONF_DESC(33, REG_GPIO_L_E4, BIT(31)), ++ PINCTRL_CONF_DESC(34, REG_GPIO_H_E4, BIT(0)), ++ PINCTRL_CONF_DESC(35, REG_GPIO_H_E4, BIT(1)), ++ PINCTRL_CONF_DESC(36, REG_GPIO_H_E4, BIT(2)), ++ PINCTRL_CONF_DESC(37, REG_GPIO_H_E4, BIT(3)), ++ PINCTRL_CONF_DESC(38, REG_GPIO_H_E4, BIT(4)), ++ PINCTRL_CONF_DESC(39, REG_GPIO_H_E4, BIT(5)), ++ PINCTRL_CONF_DESC(40, REG_GPIO_H_E4, BIT(6)), ++ PINCTRL_CONF_DESC(41, REG_I2C_SDA_E4, I2C_SCL_E4_MASK), ++ PINCTRL_CONF_DESC(42, REG_I2C_SDA_E4, I2C_SDA_E4_MASK), ++ PINCTRL_CONF_DESC(43, REG_I2C_SDA_E4, AN7583_I2C1_SCL_E4_MASK), ++ PINCTRL_CONF_DESC(44, REG_I2C_SDA_E4, AN7583_I2C1_SDA_E4_MASK), ++ PINCTRL_CONF_DESC(45, REG_I2C_SDA_E4, SPI_CLK_E4_MASK), ++ PINCTRL_CONF_DESC(46, REG_I2C_SDA_E4, SPI_CS0_E4_MASK), ++ PINCTRL_CONF_DESC(47, REG_I2C_SDA_E4, SPI_MOSI_E4_MASK), ++ PINCTRL_CONF_DESC(48, REG_I2C_SDA_E4, SPI_MISO_E4_MASK), ++ PINCTRL_CONF_DESC(49, REG_I2C_SDA_E4, UART1_TXD_E4_MASK), ++ PINCTRL_CONF_DESC(50, REG_I2C_SDA_E4, UART1_RXD_E4_MASK), ++ PINCTRL_CONF_DESC(51, REG_I2C_SDA_E4, PCIE0_RESET_E4_MASK), ++ PINCTRL_CONF_DESC(52, REG_I2C_SDA_E4, PCIE1_RESET_E4_MASK), ++ PINCTRL_CONF_DESC(53, REG_I2C_SDA_E4, AN7583_MDC_0_E4_MASK), ++ PINCTRL_CONF_DESC(54, REG_I2C_SDA_E4, AN7583_MDIO_0_E4_MASK), ++}; ++ + static const struct airoha_pinctrl_conf en7581_pinctrl_pcie_rst_od_conf[] = { + PINCTRL_CONF_DESC(61, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), + PINCTRL_CONF_DESC(62, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), + PINCTRL_CONF_DESC(63, REG_PCIE_RESET_OD, PCIE2_RESET_OD_MASK), + }; + ++static const struct airoha_pinctrl_conf an7583_pinctrl_pcie_rst_od_conf[] = { ++ PINCTRL_CONF_DESC(51, REG_PCIE_RESET_OD, PCIE0_RESET_OD_MASK), ++ PINCTRL_CONF_DESC(52, REG_PCIE_RESET_OD, PCIE1_RESET_OD_MASK), ++}; ++ + static int airoha_convert_pin_to_reg_offset(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + int pin) +@@ -1709,7 +2410,7 @@ static const struct irq_chip airoha_gpio + }; + + static int airoha_pinctrl_add_gpiochip(struct airoha_pinctrl *pinctrl, +- struct platform_device *pdev) ++ struct platform_device *pdev) + { + struct airoha_pinctrl_gpiochip *chip = &pinctrl->gpiochip; + struct gpio_chip *gc = &chip->chip; +@@ -1744,7 +2445,7 @@ static int airoha_pinctrl_add_gpiochip(s + return irq; + + err = devm_request_irq(dev, irq, airoha_irq_handler, IRQF_SHARED, +- dev_name(dev), pinctrl); ++ dev_name(dev), pinctrl); + if (err) { + dev_err(dev, "error requesting irq %d: %d\n", irq, err); + return err; +@@ -1808,8 +2509,8 @@ static int airoha_pinmux_set_mux(struct + } + + static int airoha_pinmux_set_direction(struct pinctrl_dev *pctrl_dev, +- struct pinctrl_gpio_range *range, +- unsigned int p, bool input) ++ struct pinctrl_gpio_range *range, ++ unsigned int p, bool input) + { + struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + u32 mask, index; +@@ -1899,7 +2600,7 @@ static int airoha_pinctrl_set_conf(struc + + + if (regmap_update_bits(pinctrl->chip_scu, reg->offset, reg->mask, +- val << __ffs(reg->mask))) ++ val << __ffs(reg->mask))) + return -EINVAL; + + return 0; +@@ -2118,8 +2819,8 @@ static int airoha_pinconf_group_get(stru + + for (i = 0; i < pinctrl->grps[group].npins; i++) { + if (airoha_pinconf_get(pctrl_dev, +- pinctrl->grps[group].pins[i], +- config)) ++ pinctrl->grps[group].pins[i], ++ config)) + return -ENOTSUPP; + + if (i && cur_config != *config) +@@ -2280,8 +2981,40 @@ static const struct airoha_pinctrl_match + }, + }; + ++static const struct airoha_pinctrl_match_data an7583_pinctrl_match_data = { ++ .pins = an7583_pinctrl_pins, ++ .num_pins = ARRAY_SIZE(an7583_pinctrl_pins), ++ .grps = an7583_pinctrl_groups, ++ .num_grps = ARRAY_SIZE(an7583_pinctrl_groups), ++ .funcs = an7583_pinctrl_funcs, ++ .num_funcs = ARRAY_SIZE(an7583_pinctrl_funcs), ++ .confs_info = { ++ [AIROHA_PINCTRL_CONFS_PULLUP] = { ++ .confs = an7583_pinctrl_pullup_conf, ++ .num_confs = ARRAY_SIZE(an7583_pinctrl_pullup_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_PULLDOWN] = { ++ .confs = an7583_pinctrl_pulldown_conf, ++ .num_confs = ARRAY_SIZE(an7583_pinctrl_pulldown_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_DRIVE_E2] = { ++ .confs = en7581_pinctrl_drive_e2_conf, ++ .num_confs = ARRAY_SIZE(an7583_pinctrl_drive_e2_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_DRIVE_E4] = { ++ .confs = an7583_pinctrl_drive_e4_conf, ++ .num_confs = ARRAY_SIZE(an7583_pinctrl_drive_e4_conf), ++ }, ++ [AIROHA_PINCTRL_CONFS_PCIE_RST_OD] = { ++ .confs = an7583_pinctrl_pcie_rst_od_conf, ++ .num_confs = ARRAY_SIZE(an7583_pinctrl_pcie_rst_od_conf), ++ }, ++ }, ++}; ++ + static const struct of_device_id airoha_pinctrl_of_match[] = { + { .compatible = "airoha,en7581-pinctrl", .data = &en7581_pinctrl_match_data }, ++ { .compatible = "airoha,an7583-pinctrl", .data = &an7583_pinctrl_match_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, airoha_pinctrl_of_match); diff --git a/target/linux/airoha/patches-6.12/110-v6.19-net-airoha-Do-not-loopback-traffic-to-GDM2-if-it-is-.patch b/target/linux/airoha/patches-6.12/110-v6.19-net-airoha-Do-not-loopback-traffic-to-GDM2-if-it-is-.patch new file mode 100644 index 00000000000..526243eed2c --- /dev/null +++ b/target/linux/airoha/patches-6.12/110-v6.19-net-airoha-Do-not-loopback-traffic-to-GDM2-if-it-is-.patch @@ -0,0 +1,31 @@ +From 8e0a754b0836d996802713bbebc87bc1cc17925c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 13 Nov 2025 18:19:38 +0100 +Subject: [PATCH] net: airoha: Do not loopback traffic to GDM2 if it is + available on the device + +Airoha_eth driver forwards offloaded uplink traffic (packets received +on GDM1 and forwarded to GDM{3,4}) to GDM2 in order to apply hw QoS. +This is correct if the device does not support a dedicated GDM2 port. +In this case, in order to enable hw offloading for uplink traffic, +the packets should be sent to GDM{3,4} directly. + +Fixes: 9cd451d414f6 ("net: airoha: Add loopback support for GDM2") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251113-airoha-hw-offload-gdm2-fix-v1-1-7e4ca300872f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -308,7 +308,7 @@ static int airoha_ppe_foe_entry_prepare( + if (!airoha_is_valid_gdm_port(eth, port)) + return -EINVAL; + +- if (dsa_port >= 0) ++ if (dsa_port >= 0 || eth->ports[1]) + pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 + : port->id; + else diff --git a/target/linux/airoha/patches-6.12/111-v6.19-wifi-mt76-Introduce-the-NPU-generic-layer.patch b/target/linux/airoha/patches-6.12/111-v6.19-wifi-mt76-Introduce-the-NPU-generic-layer.patch new file mode 100644 index 00000000000..6225a92b4a8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/111-v6.19-wifi-mt76-Introduce-the-NPU-generic-layer.patch @@ -0,0 +1,34 @@ +From 62f1347fa5bf6e6c9c054aedb9e87e7205fa12ac Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Oct 2025 10:50:32 +0200 +Subject: [PATCH] wifi: mt76: Introduce the NPU generic layer + +Add the NPU generic layer in mt76 module. NPU will be used to enable +traffic forward offloading between the MT76 NIC and the Airoha ethernet one +available on the Airoha EN7581 SoC using Netfilter Flowtable APIs. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20251017-mt76-npu-devel-v2-4-ddaa90901723@kernel.org +Signed-off-by: Felix Fietkau +--- + drivers/net/wireless/mediatek/mt76/Kconfig | 4 + + drivers/net/wireless/mediatek/mt76/Makefile | 1 + + drivers/net/wireless/mediatek/mt76/dma.c | 41 +- + drivers/net/wireless/mediatek/mt76/dma.h | 36 ++ + drivers/net/wireless/mediatek/mt76/mac80211.c | 6 +- + drivers/net/wireless/mediatek/mt76/mt76.h | 135 +++++ + drivers/net/wireless/mediatek/mt76/npu.c | 494 ++++++++++++++++++ + include/linux/soc/airoha/airoha_offload.h | 1 + + 8 files changed, 711 insertions(+), 7 deletions(-) + create mode 100644 drivers/net/wireless/mediatek/mt76/npu.c + +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -6,6 +6,7 @@ + #ifndef AIROHA_OFFLOAD_H + #define AIROHA_OFFLOAD_H + ++#include + #include + #include + diff --git a/target/linux/airoha/patches-6.12/112-v6.19-pinctrl-airoha-fix-pinctrl-function-mismatch-issue.patch b/target/linux/airoha/patches-6.12/112-v6.19-pinctrl-airoha-fix-pinctrl-function-mismatch-issue.patch new file mode 100644 index 00000000000..e55475f81f4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/112-v6.19-pinctrl-airoha-fix-pinctrl-function-mismatch-issue.patch @@ -0,0 +1,38 @@ +From f2bd5a0f59d052d16749bccf637690e51947a5d6 Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Sat, 15 Nov 2025 18:00:00 +0800 +Subject: [PATCH] pinctrl: airoha: fix pinctrl function mismatch issue + +The blamed commit made the following changes: + +-#define PINCTRL_FUNC_DESC(id)... +- .desc = PINCTRL_PINFUNCTION(#id, ... ++#define PINCTRL_FUNC_DESC(id, table)... ++ .desc = PINCTRL_PINFUNCTION(#id, ... + +- PINCTRL_FUNC_DESC(pon)... ++ PINCTRL_FUNC_DESC("pon", pon)... + +It's clear that the id of funcs doesn't match the definition. +Remove redundant #string from the definition to fix this issue: +pinctrl-airoha ...: invalid function mdio in map table + +Fixes: 4043b0c45f85 ("pinctrl: airoha: generalize pins/group/function/confs handling") +Signed-off-by: Chukun Pan +Acked-by: Christian Marangi +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -35,7 +35,7 @@ + + #define PINCTRL_FUNC_DESC(id, table) \ + { \ +- .desc = PINCTRL_PINFUNCTION(#id, table##_groups, \ ++ .desc = PINCTRL_PINFUNCTION(id, table##_groups, \ + ARRAY_SIZE(table##_groups)),\ + .groups = table##_func_group, \ + .group_size = ARRAY_SIZE(table##_func_group), \ diff --git a/target/linux/airoha/patches-6.12/113-v6.19-pinctrl-airoha-Fix-AIROHA_PINCTRL_CONFS_DRIVE_E2.patch b/target/linux/airoha/patches-6.12/113-v6.19-pinctrl-airoha-Fix-AIROHA_PINCTRL_CONFS_DRIVE_E2.patch new file mode 100644 index 00000000000..c4044e3a32a --- /dev/null +++ b/target/linux/airoha/patches-6.12/113-v6.19-pinctrl-airoha-Fix-AIROHA_PINCTRL_CONFS_DRIVE_E2.patch @@ -0,0 +1,40 @@ +From 0341d1b1ebf10bcbb9f35e174e83dbb21068387d Mon Sep 17 00:00:00 2001 +From: Nathan Chancellor +Date: Wed, 12 Nov 2025 11:44:30 -0700 +Subject: [PATCH] pinctrl: airoha: Fix AIROHA_PINCTRL_CONFS_DRIVE_E2 in + an7583_pinctrl_match_data + +Clang warns (or errors with CONFIG_WERROR=y / W=e): + + pinctrl/mediatek/pinctrl-airoha.c:2064:41: error: variable 'an7583_pinctrl_drive_e2_conf' is not needed and will not be emitted [-Werror,-Wunneeded-internal-declaration] + 2064 | static const struct airoha_pinctrl_conf an7583_pinctrl_drive_e2_conf[] = { + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to a typo, an7583_pinctrl_drive_e2_conf is only used within +ARRAY_SIZE() (hence no instance of -Wunused-variable), which is +evaluated at compile time, so it will not be needed in the final object +file. + +Fix the .confs assignment for AIROHA_PINCTRL_CONFS_DRIVE_E2 in +an7583_pinctrl_match_data to clear up the warning. + +Closes: https://github.com/ClangBuiltLinux/linux/issues/2142 +Fixes: 3ffeb17a9a27 ("pinctrl: airoha: add support for Airoha AN7583 PINs") +Signed-off-by: Nathan Chancellor +Acked-by: Christian Marangi +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -2998,7 +2998,7 @@ static const struct airoha_pinctrl_match + .num_confs = ARRAY_SIZE(an7583_pinctrl_pulldown_conf), + }, + [AIROHA_PINCTRL_CONFS_DRIVE_E2] = { +- .confs = en7581_pinctrl_drive_e2_conf, ++ .confs = an7583_pinctrl_drive_e2_conf, + .num_confs = ARRAY_SIZE(an7583_pinctrl_drive_e2_conf), + }, + [AIROHA_PINCTRL_CONFS_DRIVE_E4] = { diff --git a/target/linux/airoha/patches-6.12/114-v7.0-hwrng-airoha-set-rng-quality-to-900.patch b/target/linux/airoha/patches-6.12/114-v7.0-hwrng-airoha-set-rng-quality-to-900.patch new file mode 100644 index 00000000000..7df5a18537c --- /dev/null +++ b/target/linux/airoha/patches-6.12/114-v7.0-hwrng-airoha-set-rng-quality-to-900.patch @@ -0,0 +1,57 @@ +From c0008a29a006091d7f9d288620c2456afa23ff27 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Mon, 5 Jan 2026 21:41:49 +0100 +Subject: [PATCH] hwrng: airoha - set rng quality to 900 + +Airoha uses RAW mode to collect noise from the TRNG. These appear to +be unprocessed oscillations from the tero loop. For this reason, they +do not have a perfect distribution and entropy. Simple noise compression +reduces its size by 9%, so setting the quality to 900 seems reasonable. +The same value is used by the downstream driver. + +Compare the size before and after compression: +$ ls -l random_airoha* +-rw-r--r-- 1 aleksander aleksander 76546048 Jan 3 23:43 random_airoha +-rw-rw-r-- 1 aleksander aleksander 69783562 Jan 5 20:23 random_airoha.zip + +FIPS test results: +$ cat random_airoha | rngtest -c 10000 +rngtest 2.6 +Copyright (c) 2004 by Henrique de Moraes Holschuh +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +rngtest: starting FIPS tests... +rngtest: bits received from input: 200000032 +rngtest: FIPS 140-2 successes: 0 +rngtest: FIPS 140-2 failures: 10000 +rngtest: FIPS 140-2(2001-10-10) Monobit: 9957 +rngtest: FIPS 140-2(2001-10-10) Poker: 10000 +rngtest: FIPS 140-2(2001-10-10) Runs: 10000 +rngtest: FIPS 140-2(2001-10-10) Long run: 4249 +rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 +rngtest: input channel speed: (min=953.674; avg=27698.935; max=19073.486)Mibits/s +rngtest: FIPS tests speed: (min=59.791; avg=298.028; max=328.853)Mibits/s +rngtest: Program run time: 647638 microseconds + +In general, these data look like real noise, but with lower entropy +than expected. + +Fixes: e53ca8efcc5e ("hwrng: airoha - add support for Airoha EN7581 TRNG") +Suggested-by: Benjamin Larsson +Signed-off-by: Aleksander Jan Bajkowski +Signed-off-by: Herbert Xu +--- + drivers/char/hw_random/airoha-trng.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/char/hw_random/airoha-trng.c ++++ b/drivers/char/hw_random/airoha-trng.c +@@ -212,6 +212,7 @@ static int airoha_trng_probe(struct plat + trng->rng.init = airoha_trng_init; + trng->rng.cleanup = airoha_trng_cleanup; + trng->rng.read = airoha_trng_read; ++ trng->rng.quality = 900; + + ret = devm_hwrng_register(dev, &trng->rng); + if (ret) { diff --git a/target/linux/airoha/patches-6.12/115-v7.0-net-airoha-Fix-npu-rx-DMA-definitions.patch b/target/linux/airoha/patches-6.12/115-v7.0-net-airoha-Fix-npu-rx-DMA-definitions.patch new file mode 100644 index 00000000000..1d29e5471fd --- /dev/null +++ b/target/linux/airoha/patches-6.12/115-v7.0-net-airoha-Fix-npu-rx-DMA-definitions.patch @@ -0,0 +1,34 @@ +From a7fc8c641cab855824c45e5e8877e40fd528b5df Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 2 Jan 2026 12:29:38 +0100 +Subject: [PATCH] net: airoha: Fix npu rx DMA definitions + +Fix typos in npu rx DMA descriptor definitions. + +Fixes: b3ef7bdec66fb ("net: airoha: Add airoha_offload.h header") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260102-airoha-npu-dma-rx-def-fixes-v1-1-205fc6bf7d94@kernel.org +Signed-off-by: Jakub Kicinski +--- + include/linux/soc/airoha/airoha_offload.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -71,12 +71,12 @@ static inline void airoha_ppe_dev_check_ + #define NPU_RX1_DESC_NUM 512 + + /* CTRL */ +-#define NPU_RX_DMA_DESC_LAST_MASK BIT(29) +-#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15) +-#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1) ++#define NPU_RX_DMA_DESC_LAST_MASK BIT(27) ++#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(26, 14) ++#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(13, 1) + #define NPU_RX_DMA_DESC_DONE_MASK BIT(0) + /* INFO */ +-#define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 28) ++#define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 29) + #define NPU_RX_DMA_PKT_ID_MASK GENMASK(28, 26) + #define NPU_RX_DMA_SRC_PORT_MASK GENMASK(25, 21) + #define NPU_RX_DMA_CRSN_MASK GENMASK(20, 16) diff --git a/target/linux/airoha/patches-6.12/116-v6.19-net-airoha-Move-net_devs-registration-in-a-dedicated.patch b/target/linux/airoha/patches-6.12/116-v6.19-net-airoha-Move-net_devs-registration-in-a-dedicated.patch new file mode 100644 index 00000000000..bd445bb71ad --- /dev/null +++ b/target/linux/airoha/patches-6.12/116-v6.19-net-airoha-Move-net_devs-registration-in-a-dedicated.patch @@ -0,0 +1,89 @@ +From 5e7365b5a1ac8f517a7a84442289d7de242deb76 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 14 Dec 2025 10:30:07 +0100 +Subject: [PATCH] net: airoha: Move net_devs registration in a dedicated + routine + +Since airoha_probe() is not executed under rtnl lock, there is small race +where a given device is configured by user-space while the remaining ones +are not completely loaded from the dts yet. This condition will allow a +hw device misconfiguration since there are some conditions (e.g. GDM2 check +in airoha_dev_init()) that require all device are properly loaded from the +device tree. Fix the issue moving net_devices registration at the end of +the airoha_probe routine. + +Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20251214-airoha-fix-dev-registration-v1-1-860e027ad4c6@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 39 ++++++++++++++++-------- + 1 file changed, 26 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2945,19 +2945,26 @@ static int airoha_alloc_gdm_port(struct + port->id = id; + eth->ports[p] = port; + +- err = airoha_metadata_dst_alloc(port); +- if (err) +- return err; ++ return airoha_metadata_dst_alloc(port); ++} + +- err = register_netdev(dev); +- if (err) +- goto free_metadata_dst; ++static int airoha_register_gdm_devices(struct airoha_eth *eth) ++{ ++ int i; + +- return 0; ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ int err; ++ ++ if (!port) ++ continue; ++ ++ err = register_netdev(port->dev); ++ if (err) ++ return err; ++ } + +-free_metadata_dst: +- airoha_metadata_dst_free(port); +- return err; ++ return 0; + } + + static int airoha_probe(struct platform_device *pdev) +@@ -3048,6 +3055,10 @@ static int airoha_probe(struct platform_ + } + } + ++ err = airoha_register_gdm_devices(eth); ++ if (err) ++ goto error_napi_stop; ++ + return 0; + + error_napi_stop: +@@ -3061,10 +3072,12 @@ error_hw_cleanup: + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + +- if (port && port->dev->reg_state == NETREG_REGISTERED) { ++ if (!port) ++ continue; ++ ++ if (port->dev->reg_state == NETREG_REGISTERED) + unregister_netdev(port->dev); +- airoha_metadata_dst_free(port); +- } ++ airoha_metadata_dst_free(port); + } + free_netdev(eth->napi_dev); + platform_set_drvdata(pdev, NULL); diff --git a/target/linux/airoha/patches-6.12/117-v7.0-net-airoha-Use-gdm-port-enum-value-whenever-possible.patch b/target/linux/airoha/patches-6.12/117-v7.0-net-airoha-Use-gdm-port-enum-value-whenever-possible.patch new file mode 100644 index 00000000000..fe03313dd64 --- /dev/null +++ b/target/linux/airoha/patches-6.12/117-v7.0-net-airoha-Use-gdm-port-enum-value-whenever-possible.patch @@ -0,0 +1,132 @@ +From 4d513329b87c1bd0546d9f0288794e244322daa6 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 5 Jan 2026 10:40:47 +0100 +Subject: [PATCH] net: airoha: Use gdm port enum value whenever possible + +Use AIROHA_GDMx_IDX enum value whenever possible. +This patch is just cosmetic changes and does not introduce any logic one. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260105-airoha-use-port-idx-enum-v1-1-503ca5763858@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 40 +++++++++++++----------- + 1 file changed, 21 insertions(+), 19 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -108,11 +108,11 @@ static int airoha_set_vip_for_gdm_port(s + u32 vip_port; + + switch (port->id) { +- case 3: ++ case AIROHA_GDM3_IDX: + /* FIXME: handle XSI_PCIE1_PORT */ + vip_port = XSI_PCIE0_VIP_PORT_MASK; + break; +- case 4: ++ case AIROHA_GDM4_IDX: + /* FIXME: handle XSI_USB_PORT */ + vip_port = XSI_ETH_VIP_PORT_MASK; + break; +@@ -514,8 +514,8 @@ static int airoha_fe_init(struct airoha_ + FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | + FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); + +- airoha_fe_set(eth, REG_GDM_FWD_CFG(3), GDM_PAD_EN_MASK); +- airoha_fe_set(eth, REG_GDM_FWD_CFG(4), GDM_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), GDM_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), GDM_PAD_EN_MASK); + + airoha_fe_crsn_qsel_init(eth); + +@@ -1711,27 +1711,29 @@ static int airhoha_set_gdm2_loopback(str + /* Forward the traffic to the proper GDM port */ + pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 + : FE_PSE_PORT_GDM4; +- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port); +- airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC_MASK); ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ pse_port); ++ airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ GDM_STRIP_CRC_MASK); + + /* Enable GDM2 loopback */ +- airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff); +- airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff); ++ airoha_fe_wr(eth, REG_GDM_TXCHN_EN(AIROHA_GDM2_IDX), 0xffffffff); ++ airoha_fe_wr(eth, REG_GDM_RXCHN_EN(AIROHA_GDM2_IDX), 0xffff); + + chan = port->id == AIROHA_GDM3_IDX ? airoha_is_7581(eth) ? 4 : 3 : 0; +- airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2), ++ airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX), + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK, + FIELD_PREP(LPBK_CHAN_MASK, chan) | + LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK | + LBK_CHAN_MODE_MASK | LPBK_EN_MASK); +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(2), ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX), + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); + + /* Disable VIP and IFC for GDM2 */ +- airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2)); +- airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2)); ++ airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); ++ airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); + + /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ + nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; +@@ -1767,8 +1769,8 @@ static int airoha_dev_init(struct net_de + airoha_set_macaddr(port, dev->dev_addr); + + switch (port->id) { +- case 3: +- case 4: ++ case AIROHA_GDM3_IDX: ++ case AIROHA_GDM4_IDX: + /* If GDM2 is active we can't enable loopback */ + if (!eth->ports[1]) { + int err; +@@ -1778,7 +1780,7 @@ static int airoha_dev_init(struct net_de + return err; + } + fallthrough; +- case 2: ++ case AIROHA_GDM2_IDX: + if (airoha_ppe_is_enabled(eth, 1)) { + /* For PPE2 always use secondary cpu port. */ + fe_cpu_port = FE_PSE_PORT_CDM2; +@@ -3121,14 +3123,14 @@ static const char * const en7581_xsi_rst + static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq) + { + switch (port->id) { +- case 3: ++ case AIROHA_GDM3_IDX: + /* 7581 SoC supports PCIe serdes on GDM3 port */ + if (nbq == 4) + return HSGMII_LAN_7581_PCIE0_SRCPORT; + if (nbq == 5) + return HSGMII_LAN_7581_PCIE1_SRCPORT; + break; +- case 4: ++ case AIROHA_GDM4_IDX: + /* 7581 SoC supports eth and usb serdes on GDM4 port */ + if (!nbq) + return HSGMII_LAN_7581_ETH_SRCPORT; +@@ -3152,12 +3154,12 @@ static const char * const an7583_xsi_rst + static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq) + { + switch (port->id) { +- case 3: ++ case AIROHA_GDM3_IDX: + /* 7583 SoC supports eth serdes on GDM3 port */ + if (!nbq) + return HSGMII_LAN_7583_ETH_SRCPORT; + break; +- case 4: ++ case AIROHA_GDM4_IDX: + /* 7583 SoC supports PCIe and USB serdes on GDM4 port */ + if (!nbq) + return HSGMII_LAN_7583_PCIE_SRCPORT; diff --git a/target/linux/airoha/patches-6.12/118-v7.0-net-airoha-npu-Dump-fw-version-during-probe.patch b/target/linux/airoha/patches-6.12/118-v7.0-net-airoha-npu-Dump-fw-version-during-probe.patch new file mode 100644 index 00000000000..36f8850c6cd --- /dev/null +++ b/target/linux/airoha/patches-6.12/118-v7.0-net-airoha-npu-Dump-fw-version-during-probe.patch @@ -0,0 +1,36 @@ +From e4bc5dd53bf5d46cd58f081ffccc3809e2be5373 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 5 Jan 2026 09:49:16 +0100 +Subject: [PATCH] net: airoha: npu: Dump fw version during probe + +Dump firmware version running on the npu during module probe. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260105-airoha-npu-dump-fw-v1-1-36d8326975f8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -658,6 +658,7 @@ static int airoha_npu_probe(struct platf + struct device_node *np; + void __iomem *base; + int i, irq, err; ++ u32 val; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) +@@ -757,6 +758,11 @@ static int airoha_npu_probe(struct platf + regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1); + msleep(100); + ++ if (!airoha_npu_wlan_msg_get(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION, ++ &val, sizeof(val), GFP_KERNEL)) ++ dev_info(dev, "NPU fw version: %0d.%d\n", ++ (val >> 16) & 0xffff, val & 0xffff); ++ + platform_set_drvdata(pdev, npu); + + return 0; diff --git a/target/linux/airoha/patches-6.12/119-v6.19-net-airoha-Fix-schedule-while-atomic-in-airoha_ppe_d.patch b/target/linux/airoha/patches-6.12/119-v6.19-net-airoha-Fix-schedule-while-atomic-in-airoha_ppe_d.patch new file mode 100644 index 00000000000..2a7ce09156c --- /dev/null +++ b/target/linux/airoha/patches-6.12/119-v6.19-net-airoha-Fix-schedule-while-atomic-in-airoha_ppe_d.patch @@ -0,0 +1,41 @@ +From 6abcf751bc084804a9e5b3051442e8a2ce67f48a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 5 Jan 2026 09:43:31 +0100 +Subject: [PATCH] net: airoha: Fix schedule while atomic in airoha_ppe_deinit() + +airoha_ppe_deinit() runs airoha_npu_ppe_deinit() in atomic context. +airoha_npu_ppe_deinit routine allocates ppe_data buffer with GFP_KERNEL +flag. Rely on rcu_replace_pointer in airoha_ppe_deinit routine in order +to fix schedule while atomic issue in airoha_npu_ppe_deinit() since we +do not need atomic context there. + +Fixes: 00a7678310fe3 ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260105-airoha-fw-ethtool-v2-1-3b32b158cc31@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1547,13 +1547,16 @@ void airoha_ppe_deinit(struct airoha_eth + { + struct airoha_npu *npu; + +- rcu_read_lock(); +- npu = rcu_dereference(eth->npu); ++ mutex_lock(&flow_offload_mutex); ++ ++ npu = rcu_replace_pointer(eth->npu, NULL, ++ lockdep_is_held(&flow_offload_mutex)); + if (npu) { + npu->ops.ppe_deinit(npu); + airoha_npu_put(npu); + } +- rcu_read_unlock(); ++ ++ mutex_unlock(&flow_offload_mutex); + + rhashtable_destroy(ð->ppe->l2_flows); + rhashtable_destroy(ð->flow_table); diff --git a/target/linux/airoha/patches-6.12/120-v7.0-net-airoha-implement-get_link_ksettings.patch b/target/linux/airoha/patches-6.12/120-v7.0-net-airoha-implement-get_link_ksettings.patch new file mode 100644 index 00000000000..e08bd168fa0 --- /dev/null +++ b/target/linux/airoha/patches-6.12/120-v7.0-net-airoha-implement-get_link_ksettings.patch @@ -0,0 +1,26 @@ +From 50e194b6da721e4fa1fc6ebcf5969803c214929a Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Sat, 10 Jan 2026 18:02:05 +0100 +Subject: [PATCH] net: airoha: implement get_link_ksettings + +Implement the .get_link_ksettings to get the rate, duplex, and +auto-negotiation status. + +Signed-off-by: Aleksander Jan Bajkowski +Tested-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260110170212.570793-1-olek2@wp.pl +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2826,6 +2826,7 @@ static const struct ethtool_ops airoha_e + .get_drvinfo = airoha_ethtool_get_drvinfo, + .get_eth_mac_stats = airoha_ethtool_get_mac_stats, + .get_rmon_stats = airoha_ethtool_get_rmon_stats, ++ .get_link_ksettings = phy_ethtool_get_link_ksettings, + .get_link = ethtool_op_get_link, + }; + diff --git a/target/linux/airoha/patches-6.12/121-v7.0-net-airoha-npu-Init-BA-memory-region-if-provided-via.patch b/target/linux/airoha/patches-6.12/121-v7.0-net-airoha-npu-Init-BA-memory-region-if-provided-via.patch new file mode 100644 index 00000000000..9283e18de6b --- /dev/null +++ b/target/linux/airoha/patches-6.12/121-v7.0-net-airoha-npu-Init-BA-memory-region-if-provided-via.patch @@ -0,0 +1,32 @@ +From 875a59c9a9e584d99d8e9e5aa8435ec9300bfe91 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 8 Jan 2026 16:05:08 +0100 +Subject: [PATCH] net: airoha: npu: Init BA memory region if provided via DTS + +Initialize NPU Block Ack memory region if reserved via DTS. +Block Ack memory region is used by NPU MT7996 (Eagle) offloading. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260108-airoha-ba-memory-region-v3-2-bf1814e5dcc4@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -519,6 +519,14 @@ static int airoha_npu_wlan_init_memory(s + if (err) + return err; + ++ if (of_property_match_string(npu->dev->of_node, "memory-region-names", ++ "ba") >= 0) { ++ cmd = WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "ba", cmd); ++ if (err) ++ return err; ++ } ++ + cmd = WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU; + return airoha_npu_wlan_msg_send(npu, 0, cmd, &val, sizeof(val), + GFP_KERNEL); diff --git a/target/linux/airoha/patches-6.12/122-v7.0-net-airoha_eth-increase-max-MTU-to-9220-for-DSA-jumb.patch b/target/linux/airoha/patches-6.12/122-v7.0-net-airoha_eth-increase-max-MTU-to-9220-for-DSA-jumb.patch new file mode 100644 index 00000000000..ddd02ed2b9d --- /dev/null +++ b/target/linux/airoha/patches-6.12/122-v7.0-net-airoha_eth-increase-max-MTU-to-9220-for-DSA-jumb.patch @@ -0,0 +1,37 @@ +From 6406fc709ace081575de2a8a7eee12e63d4c96c6 Mon Sep 17 00:00:00 2001 +From: Sayantan Nandy +Date: Mon, 19 Jan 2026 13:06:58 +0530 +Subject: [PATCH] net: airoha_eth: increase max MTU to 9220 for DSA jumbo + frames + +The industry standard jumbo frame MTU is 9216 bytes. When using the DSA +subsystem, a 4-byte tag is added to each Ethernet frame. + +Increase AIROHA_MAX_MTU to 9220 bytes (9216 + 4) so that users can set a +standard 9216-byte MTU on DSA ports. + +The underlying hardware supports significantly larger frame sizes +(approximately 16K). However, the maximum MTU is limited to 9220 bytes +for now, as this is sufficient to support standard jumbo frames and does +not incur additional memory allocation overhead. + +Signed-off-by: Sayantan Nandy +Reviewed-by: Andrew Lunn +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260119073658.6216-1-sayantann11@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -21,7 +21,7 @@ + #define AIROHA_MAX_NUM_IRQ_BANKS 4 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 +-#define AIROHA_MAX_MTU 9216 ++#define AIROHA_MAX_MTU 9220 + #define AIROHA_MAX_PACKET_SIZE 2048 + #define AIROHA_NUM_QOS_CHANNELS 4 + #define AIROHA_NUM_QOS_QUEUES 8 diff --git a/target/linux/airoha/patches-6.12/123-v7.0-net-airoha-npu-Add-the-capability-to-read-firmware-n.patch b/target/linux/airoha/patches-6.12/123-v7.0-net-airoha-npu-Add-the-capability-to-read-firmware-n.patch new file mode 100644 index 00000000000..35ad950f420 --- /dev/null +++ b/target/linux/airoha/patches-6.12/123-v7.0-net-airoha-npu-Add-the-capability-to-read-firmware-n.patch @@ -0,0 +1,119 @@ +From 3847173525e307ebcd23bd4863da943ea78b0057 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 20 Jan 2026 11:17:18 +0100 +Subject: [PATCH] net: airoha: npu: Add the capability to read firmware names + from dts + +Introduce the capability to read the firmware binary names from device-tree +using the firmware-name property if available. +This patch is needed because NPU firmware binaries are board specific since +they depend on the MediaTek WiFi chip used on the board (e.g. MT7996 or +MT7992) and the WiFi chip version info is not available in the NPU driver. +This is a preliminary patch to enable MT76 NPU offloading if the Airoha SoC +is equipped with MT7996 (Eagle) WiFi chipset. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260120-airoha-npu-firmware-name-v4-2-88999628b4c1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 46 ++++++++++++++++++++---- + 1 file changed, 40 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -16,6 +16,8 @@ + + #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" + #define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin" ++#define NPU_EN7581_7996_FIRMWARE_DATA "airoha/en7581_MT7996_npu_data.bin" ++#define NPU_EN7581_7996_FIRMWARE_RV32 "airoha/en7581_MT7996_npu_rv32.bin" + #define NPU_AN7583_FIRMWARE_DATA "airoha/an7583_npu_data.bin" + #define NPU_AN7583_FIRMWARE_RV32 "airoha/an7583_npu_rv32.bin" + #define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE 0x200000 +@@ -195,18 +197,18 @@ static int airoha_npu_send_msg(struct ai + } + + static int airoha_npu_load_firmware(struct device *dev, void __iomem *addr, +- const struct airoha_npu_fw *fw_info) ++ const char *fw_name, int fw_max_size) + { + const struct firmware *fw; + int ret; + +- ret = request_firmware(&fw, fw_info->name, dev); ++ ret = request_firmware(&fw, fw_name, dev); + if (ret) + return ret == -ENOENT ? -EPROBE_DEFER : ret; + +- if (fw->size > fw_info->max_size) { ++ if (fw->size > fw_max_size) { + dev_err(dev, "%s: fw size too overlimit (%zu)\n", +- fw_info->name, fw->size); ++ fw_name, fw->size); + ret = -E2BIG; + goto out; + } +@@ -218,6 +220,28 @@ out: + return ret; + } + ++static int ++airoha_npu_load_firmware_from_dts(struct device *dev, void __iomem *addr, ++ void __iomem *base) ++{ ++ const char *fw_names[2]; ++ int ret; ++ ++ ret = of_property_read_string_array(dev->of_node, "firmware-name", ++ fw_names, ARRAY_SIZE(fw_names)); ++ if (ret != ARRAY_SIZE(fw_names)) ++ return -EINVAL; ++ ++ ret = airoha_npu_load_firmware(dev, addr, fw_names[0], ++ NPU_EN7581_FIRMWARE_RV32_MAX_SIZE); ++ if (ret) ++ return ret; ++ ++ return airoha_npu_load_firmware(dev, base + REG_NPU_LOCAL_SRAM, ++ fw_names[1], ++ NPU_EN7581_FIRMWARE_DATA_MAX_SIZE); ++} ++ + static int airoha_npu_run_firmware(struct device *dev, void __iomem *base, + struct reserved_mem *rmem) + { +@@ -233,14 +257,22 @@ static int airoha_npu_run_firmware(struc + if (IS_ERR(addr)) + return PTR_ERR(addr); + ++ /* Try to load firmware images using the firmware names provided via ++ * dts if available. ++ */ ++ if (of_find_property(dev->of_node, "firmware-name", NULL)) ++ return airoha_npu_load_firmware_from_dts(dev, addr, base); ++ + /* Load rv32 npu firmware */ +- ret = airoha_npu_load_firmware(dev, addr, &soc->fw_rv32); ++ ret = airoha_npu_load_firmware(dev, addr, soc->fw_rv32.name, ++ soc->fw_rv32.max_size); + if (ret) + return ret; + + /* Load data npu firmware */ + return airoha_npu_load_firmware(dev, base + REG_NPU_LOCAL_SRAM, +- &soc->fw_data); ++ soc->fw_data.name, ++ soc->fw_data.max_size); + } + + static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance) +@@ -797,6 +829,8 @@ module_platform_driver(airoha_npu_driver + + MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_DATA); + MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_RV32); ++MODULE_FIRMWARE(NPU_EN7581_7996_FIRMWARE_DATA); ++MODULE_FIRMWARE(NPU_EN7581_7996_FIRMWARE_RV32); + MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_DATA); + MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_RV32); + MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/124-v7.1-net-airoha-fix-typo-in-function-name.patch b/target/linux/airoha/patches-6.12/124-v7.1-net-airoha-fix-typo-in-function-name.patch new file mode 100644 index 00000000000..72644140b8d --- /dev/null +++ b/target/linux/airoha/patches-6.12/124-v7.1-net-airoha-fix-typo-in-function-name.patch @@ -0,0 +1,53 @@ +From aebf15e8eb09b01e99f043e9f5d423798aac9d32 Mon Sep 17 00:00:00 2001 +From: Zhengping Zhang +Date: Thu, 26 Feb 2026 10:37:08 +0800 +Subject: [PATCH] net: airoha: fix typo in function name + +Corrected the typo in the function name from + `airhoa_is_lan_gdm_port` to `airoha_is_lan_gdm_port`. This change ensures + consistency in the API naming convention. + +Signed-off-by: Zhengping Zhang +Reviewed-by: Simon Horman +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/tencent_E4FD5D6BC0131E617D848896F5F9FCED6E0A@qq.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -76,7 +76,7 @@ static void airoha_set_macaddr(struct ai + struct airoha_eth *eth = port->qdma->eth; + u32 val, reg; + +- reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H ++ reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H + : REG_FE_WAN_MAC_H; + val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; + airoha_fe_wr(eth, reg, val); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -626,7 +626,7 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + +-static inline bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) ++static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port) + { + /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. + * GDM{2,3,4} can be used as wan port connected to an external +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -321,7 +321,7 @@ static int airoha_ppe_foe_entry_prepare( + /* For downlink traffic consume SRAM memory for hw + * forwarding descriptors queue. + */ +- if (airhoa_is_lan_gdm_port(port)) ++ if (airoha_is_lan_gdm_port(port)) + val |= AIROHA_FOE_IB2_FAST_PATH; + if (dsa_port >= 0) + val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, diff --git a/target/linux/airoha/patches-6.12/125-v7.1-net-airoha-Rely-__field_prep-for-non-constant-masks.patch b/target/linux/airoha/patches-6.12/125-v7.1-net-airoha-Rely-__field_prep-for-non-constant-masks.patch new file mode 100644 index 00000000000..78dc75eb0a8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/125-v7.1-net-airoha-Rely-__field_prep-for-non-constant-masks.patch @@ -0,0 +1,44 @@ +From 7600fb3b41dd6ab65ed61169df1b6099044edf97 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 4 Mar 2026 11:56:47 +0100 +Subject: [PATCH] net: airoha: Rely __field_prep for non-constant masks + +Rely on __field_prep macros for non-constant masks preparing the values +for register updates instead of open-coding. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260304-airoha-__field_prep-v1-1-b185facc4e2f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1748,7 +1748,7 @@ static int airhoha_set_gdm2_loopback(str + airoha_fe_rmw(eth, + REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)), + SP_CPORT_MASK(val), +- FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val))); ++ __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + + if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth)) + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, +@@ -1802,7 +1802,7 @@ static int airoha_dev_init(struct net_de + ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0; + airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id), + DFT_CPORT_MASK(port->id), +- fe_cpu_port << __ffs(DFT_CPORT_MASK(port->id))); ++ __field_prep(DFT_CPORT_MASK(port->id), fe_cpu_port)); + + return 0; + } +@@ -2159,7 +2159,7 @@ static int airoha_qdma_set_chan_tx_sched + + airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), + CHAN_QOS_MODE_MASK(channel), +- mode << __ffs(CHAN_QOS_MODE_MASK(channel))); ++ __field_prep(CHAN_QOS_MODE_MASK(channel), mode)); + + return 0; + } diff --git a/target/linux/airoha/patches-6.12/126-v7.1-net-airoha-Make-flow-control-source-port-mapping-dep.patch b/target/linux/airoha/patches-6.12/126-v7.1-net-airoha-Make-flow-control-source-port-mapping-dep.patch new file mode 100644 index 00000000000..9e4684bddc9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/126-v7.1-net-airoha-Make-flow-control-source-port-mapping-dep.patch @@ -0,0 +1,52 @@ +From bf3471e6e6c02137dc0d26caa783ac1849f9aab8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 6 Mar 2026 09:07:27 +0100 +Subject: [PATCH] net: airoha: Make flow control source port mapping dependent + on nbq parameter + +Flow control source port mapping for USB serdes needs to be configured +according to the GDM port nbq parameter. This is a preliminary patch +since nbq parameter is specific for the given port serdes and needs to +be read from the DTS (in the current codebase is assigned statically). + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260306-airoha-fix-loopback-for-usb-serdes-v2-1-319de9c96826@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- + drivers/net/ethernet/airoha/airoha_regs.h | 5 +---- + 2 files changed, 7 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1750,10 +1750,12 @@ static int airhoha_set_gdm2_loopback(str + SP_CPORT_MASK(val), + __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + +- if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth)) +- airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, +- FC_ID_OF_SRC_PORT24_MASK, +- FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2)); ++ if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { ++ u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq); ++ ++ airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, ++ __field_prep(mask, AIROHA_GDM2_IDX)); ++ } + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -376,10 +376,7 @@ + #define SP_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2)) + + #define REG_SRC_PORT_FC_MAP6 0x2298 +-#define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24) +-#define FC_ID_OF_SRC_PORT26_MASK GENMASK(20, 16) +-#define FC_ID_OF_SRC_PORT25_MASK GENMASK(12, 8) +-#define FC_ID_OF_SRC_PORT24_MASK GENMASK(4, 0) ++#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) + + #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 + diff --git a/target/linux/airoha/patches-6.12/127-v7.1-net-airoha-Move-GDM-forward-port-configuration-in-nd.patch b/target/linux/airoha/patches-6.12/127-v7.1-net-airoha-Move-GDM-forward-port-configuration-in-nd.patch new file mode 100644 index 00000000000..d2458a9aa5d --- /dev/null +++ b/target/linux/airoha/patches-6.12/127-v7.1-net-airoha-Move-GDM-forward-port-configuration-in-nd.patch @@ -0,0 +1,86 @@ +From 46097d011f77f5758fb47b7059b4f1f2e7403940 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 6 Mar 2026 16:09:47 +0100 +Subject: [PATCH] net: airoha: Move GDM forward port configuration in + ndo_open/ndo_stop callbacks + +This change allows to set GDM forward port configuration to +FE_PSE_PORT_DROP stopping the network device. Hw design requires to stop +packet forwarding putting the interface down. Moreover, PPE firmware +requires to use PPE1 for GDM3 or GDM4. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260306-airoha-gdm-forward-ndo-open-stop-v1-1-7b7a20dd9ef0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 20 +++++++++++++++----- + 1 file changed, 15 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1632,6 +1632,7 @@ static int airoha_dev_open(struct net_de + int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN; + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; ++ u32 pse_port = FE_PSE_PORT_PPE1; + + netif_tx_start_all_queues(dev); + err = airoha_set_vip_for_gdm_port(port, true); +@@ -1655,6 +1656,14 @@ static int airoha_dev_open(struct net_de + GLOBAL_CFG_RX_DMA_EN_MASK); + atomic_inc(&qdma->users); + ++ if (port->id == AIROHA_GDM2_IDX && ++ airoha_ppe_is_enabled(qdma->eth, 1)) { ++ /* For PPE2 always use secondary cpu port. */ ++ pse_port = FE_PSE_PORT_PPE2; ++ } ++ airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), ++ pse_port); ++ + return 0; + } + +@@ -1672,6 +1681,9 @@ static int airoha_dev_stop(struct net_de + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) + netdev_tx_reset_subqueue(dev, i); + ++ airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), ++ FE_PSE_PORT_DROP); ++ + if (atomic_dec_and_test(&qdma->users)) { + airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | +@@ -1765,7 +1777,7 @@ static int airoha_dev_init(struct net_de + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; + struct airoha_eth *eth = qdma->eth; +- u32 pse_port, fe_cpu_port; ++ u32 fe_cpu_port; + u8 ppe_id; + + airoha_set_macaddr(port, dev->dev_addr); +@@ -1786,7 +1798,7 @@ static int airoha_dev_init(struct net_de + if (airoha_ppe_is_enabled(eth, 1)) { + /* For PPE2 always use secondary cpu port. */ + fe_cpu_port = FE_PSE_PORT_CDM2; +- pse_port = FE_PSE_PORT_PPE2; ++ ppe_id = 1; + break; + } + fallthrough; +@@ -1795,13 +1807,11 @@ static int airoha_dev_init(struct net_de + + /* For PPE1 select cpu port according to the running QDMA. */ + fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1; +- pse_port = FE_PSE_PORT_PPE1; ++ ppe_id = 0; + break; + } + } + +- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port); +- ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0; + airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id), + DFT_CPORT_MASK(port->id), + __field_prep(DFT_CPORT_MASK(port->id), fe_cpu_port)); diff --git a/target/linux/airoha/patches-6.12/129-v7.1-net-airoha-select-QDMA-block-according-LAN-WAN-confi.patch b/target/linux/airoha/patches-6.12/129-v7.1-net-airoha-select-QDMA-block-according-LAN-WAN-confi.patch new file mode 100644 index 00000000000..e0eecea69a1 --- /dev/null +++ b/target/linux/airoha/patches-6.12/129-v7.1-net-airoha-select-QDMA-block-according-LAN-WAN-confi.patch @@ -0,0 +1,131 @@ +From 8737d7194d6d5947c3d7d8813895b44a25b84477 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 13 Mar 2026 17:28:36 +0100 +Subject: [PATCH] net: airoha: select QDMA block according LAN/WAN + configuration + +Before this patch even GDM ports were assigned to QDMA0 while odd GDM +ports were using QDMA1, so, based on the DTS configuration, both QDMA0 +and QDMA1 can theoretically receive traffic destinated to the host cpu +from LAN or WAN GDM ports. +Airoha folks reported the hw design assumes the LAN traffic destinated +to the host cpu is be forwarded to QDMA0 while traffic received on WAN +GDM port is managed by QDMA1. For this reason, select QDMA block according +to the GDM port LAN or WAN configuration: +- QDMA0 is used for GDM LAN devices +- QDMA1 is used for GDM WAN device + +Assuming a device with three GDM ports, a typical configuration could be: +- MT7530 DSA switch -> GDM1 (eth0) -> QDMA0 (LAN traffic) +- External PHY -> GDM2 (eth1) -> QDMA1 (WAN traffic) +- External PHY -> GDM3 (eth2) -> QDMA0 (LAN traffic) + +We can then bridge eth0 DSA port (lanX) with eth2 since they all tx/rx +LAN traffic. + +Please note this patch introduces a change not visible to the user since +airoha_eth driver currently supports just the internal phy available via +the MT7530 DSA switch and there are no WAN interfaces officially supported +since PCS/external phy is not merged mainline yet (it will be posted with +following patches). + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260313-airoha-qdma-lan-wan-mode-v2-1-7d577db6e40c@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 18 ++++++++---------- + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + 2 files changed, 9 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1775,11 +1775,13 @@ static int airhoha_set_gdm2_loopback(str + static int airoha_dev_init(struct net_device *dev) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_qdma *qdma = port->qdma; +- struct airoha_eth *eth = qdma->eth; ++ struct airoha_eth *eth = port->eth; + u32 fe_cpu_port; + u8 ppe_id; + ++ /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ ++ port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; ++ port->dev->irq = port->qdma->irq_banks[0].irq; + airoha_set_macaddr(port, dev->dev_addr); + + switch (port->id) { +@@ -1803,7 +1805,7 @@ static int airoha_dev_init(struct net_de + } + fallthrough; + default: { +- u8 qdma_id = qdma - ð->qdma[0]; ++ u8 qdma_id = port->qdma - ð->qdma[0]; + + /* For PPE1 select cpu port according to the running QDMA. */ + fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1; +@@ -2887,11 +2889,10 @@ bool airoha_is_valid_gdm_port(struct air + } + + static int airoha_alloc_gdm_port(struct airoha_eth *eth, +- struct device_node *np, int index) ++ struct device_node *np) + { + const __be32 *id_ptr = of_get_property(np, "reg", NULL); + struct airoha_gdm_port *port; +- struct airoha_qdma *qdma; + struct net_device *dev; + int err, p; + u32 id; +@@ -2922,7 +2923,6 @@ static int airoha_alloc_gdm_port(struct + return -ENOMEM; + } + +- qdma = ð->qdma[index % AIROHA_MAX_NUM_QDMA]; + dev->netdev_ops = &airoha_netdev_ops; + dev->ethtool_ops = &airoha_ethtool_ops; + dev->max_mtu = AIROHA_MAX_MTU; +@@ -2934,7 +2934,6 @@ static int airoha_alloc_gdm_port(struct + dev->features |= dev->hw_features; + dev->vlan_features = dev->hw_features; + dev->dev.of_node = np; +- dev->irq = qdma->irq_banks[0].irq; + SET_NETDEV_DEV(dev, eth->dev); + + /* reserve hw queues for HTB offloading */ +@@ -2955,7 +2954,7 @@ static int airoha_alloc_gdm_port(struct + port = netdev_priv(dev); + u64_stats_init(&port->stats.syncp); + spin_lock_init(&port->stats.lock); +- port->qdma = qdma; ++ port->eth = eth; + port->dev = dev; + port->id = id; + eth->ports[p] = port; +@@ -3055,7 +3054,6 @@ static int airoha_probe(struct platform_ + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_qdma_start_napi(ð->qdma[i]); + +- i = 0; + for_each_child_of_node(pdev->dev.of_node, np) { + if (!of_device_is_compatible(np, "airoha,eth-mac")) + continue; +@@ -3063,7 +3061,7 @@ static int airoha_probe(struct platform_ + if (!of_device_is_available(np)) + continue; + +- err = airoha_alloc_gdm_port(eth, np, i++); ++ err = airoha_alloc_gdm_port(eth, np); + if (err) { + of_node_put(np); + goto error_napi_stop; +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -533,6 +533,7 @@ struct airoha_qdma { + + struct airoha_gdm_port { + struct airoha_qdma *qdma; ++ struct airoha_eth *eth; + struct net_device *dev; + int id; + diff --git a/target/linux/airoha/patches-6.12/130-v7.0-net-airoha-Fix-typo-in-airoha_ppe_setup_tc_block_cb-.patch b/target/linux/airoha/patches-6.12/130-v7.0-net-airoha-Fix-typo-in-airoha_ppe_setup_tc_block_cb-.patch new file mode 100644 index 00000000000..9620d1ecd49 --- /dev/null +++ b/target/linux/airoha/patches-6.12/130-v7.0-net-airoha-Fix-typo-in-airoha_ppe_setup_tc_block_cb-.patch @@ -0,0 +1,32 @@ +From dfdf774656205515b2d6ad94fce63c7ccbe92d91 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 9 Jan 2026 10:29:06 +0100 +Subject: [PATCH] net: airoha: Fix typo in airoha_ppe_setup_tc_block_cb + definition + +Fix Typo in airoha_ppe_dev_setup_tc_block_cb routine definition when +CONFIG_NET_AIROHA is not enabled. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202601090517.Fj6v501r-lkp@intel.com/ +Fixes: f45fc18b6de04 ("net: airoha: Add airoha_ppe_dev struct definition") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260109-airoha_ppe_dev_setup_tc_block_cb-typo-v1-1-282e8834a9f9@kernel.org +Signed-off-by: Jakub Kicinski +--- + include/linux/soc/airoha/airoha_offload.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -52,8 +52,8 @@ static inline void airoha_ppe_put_dev(st + { + } + +-static inline int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, +- void *type_data) ++static inline int airoha_ppe_dev_setup_tc_block_cb(struct airoha_ppe_dev *dev, ++ void *type_data) + { + return -EOPNOTSUPP; + } diff --git a/target/linux/airoha/patches-6.12/131-v7.0-net-phy-mediatek-enable-interrupts-on-AN7581.patch b/target/linux/airoha/patches-6.12/131-v7.0-net-phy-mediatek-enable-interrupts-on-AN7581.patch new file mode 100644 index 00000000000..a30014bec56 --- /dev/null +++ b/target/linux/airoha/patches-6.12/131-v7.0-net-phy-mediatek-enable-interrupts-on-AN7581.patch @@ -0,0 +1,27 @@ +From 2e229771543b2b20e1fe29da00df80c917469449 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski +Date: Fri, 2 Jan 2026 12:30:06 +0100 +Subject: [PATCH] net: phy: mediatek: enable interrupts on AN7581 + +Interrupts work just like on MT7988. + +Suggested-by: Benjamin Larsson +Signed-off-by: Aleksander Jan Bajkowski +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260102113222.3519900-1-olek2@wp.pl +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -1492,6 +1492,8 @@ static struct phy_driver mtk_socphy_driv + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_AN7581), + .name = "Airoha AN7581 PHY", ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, + .probe = an7581_phy_probe, + .led_blink_set = mt798x_phy_led_blink_set, + .led_brightness_set = mt798x_phy_led_brightness_set, diff --git a/target/linux/airoha/patches-6.12/132-v7.1-net-airoha-Reset-PPE-default-cput-port-in-airoha_ppe.patch b/target/linux/airoha/patches-6.12/132-v7.1-net-airoha-Reset-PPE-default-cput-port-in-airoha_ppe.patch new file mode 100644 index 00000000000..136c707f7f9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/132-v7.1-net-airoha-Reset-PPE-default-cput-port-in-airoha_ppe.patch @@ -0,0 +1,162 @@ +From df8e1e4a2eb5f8ecdef36c502601e8afbc6ad891 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 24 Dec 2025 17:29:33 +0100 +Subject: [PATCH] net: airoha: Reset PPE default cput port in + airoha_ppe_hw_init() + +Before this patch the default PPE cpu port used for a specific GDM +device was set running ndo_init() callback during device initialization. +The selected PPE cpu port configured for the specific GDM device depends +on the QDMA block assigned to the GDM device. The selected QDMA block +depends on LAN/WAN configuration as specified in commmit XXXX. +However, the user selected PPE cpu port can be different with respect to +the one hardcoded in the NPU firmware binary. The hardcoded PPE cput port +value is loaded initializing the PPE engine running npu ops ppe_init() +callback in airoha_ppe_offload_setup routine. +Reset the default value for PPE cpu ports in airoha_ppe_hw_init routine +in order to apply the user requested configuration according to the device +DTS setup. +Please note this patch is fixing an issue not visible to the user (so we +do not need to backport it) since airoha_eth driver currently supports just +the internal phy available via the MT7530 DSA switch and there are no WAN +interfaces officially supporte since PCS/external phy is not merged mainline +yet (it will be posted with following patches). + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 28 +++++------------------ + drivers/net/ethernet/airoha/airoha_eth.h | 2 ++ + drivers/net/ethernet/airoha/airoha_ppe.c | 23 ++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_regs.h | 7 +++--- + 4 files changed, 33 insertions(+), 27 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1776,8 +1776,7 @@ static int airoha_dev_init(struct net_de + { + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_eth *eth = port->eth; +- u32 fe_cpu_port; +- u8 ppe_id; ++ int i; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ + port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; +@@ -1795,28 +1794,13 @@ static int airoha_dev_init(struct net_de + if (err) + return err; + } +- fallthrough; +- case AIROHA_GDM2_IDX: +- if (airoha_ppe_is_enabled(eth, 1)) { +- /* For PPE2 always use secondary cpu port. */ +- fe_cpu_port = FE_PSE_PORT_CDM2; +- ppe_id = 1; +- break; +- } +- fallthrough; +- default: { +- u8 qdma_id = port->qdma - ð->qdma[0]; +- +- /* For PPE1 select cpu port according to the running QDMA. */ +- fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1; +- ppe_id = 0; + break; +- } ++ default: ++ break; + } + +- airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id), +- DFT_CPORT_MASK(port->id), +- __field_prep(DFT_CPORT_MASK(port->id), fe_cpu_port)); ++ for (i = 0; i < eth->soc->num_ppe; i++) ++ airoha_ppe_set_cpu_port(port, i); + + return 0; + } +@@ -1919,7 +1903,7 @@ static u32 airoha_get_dsa_tag(struct sk_ + #endif + } + +-static int airoha_get_fe_port(struct airoha_gdm_port *port) ++int airoha_get_fe_port(struct airoha_gdm_port *port) + { + struct airoha_qdma *qdma = port->qdma; + struct airoha_eth *eth = qdma->eth; +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -646,9 +646,11 @@ static inline bool airoha_is_7583(struct + return eth->soc->version == 0x7583; + } + ++int airoha_get_fe_port(struct airoha_gdm_port *port); + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + ++void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id); + bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); + void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, + u16 hash, bool rx_wlan); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -85,6 +85,20 @@ static u32 airoha_ppe_get_timestamp(stru + return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); + } + ++void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id) ++{ ++ struct airoha_qdma *qdma = port->qdma; ++ u8 fport = airoha_get_fe_port(port); ++ struct airoha_eth *eth = qdma->eth; ++ u8 qdma_id = qdma - ð->qdma[0]; ++ u32 fe_cpu_port; ++ ++ fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1; ++ airoha_fe_rmw(eth, REG_PPE_DFT_CPORT(ppe_id, fport), ++ DFT_CPORT_MASK(fport), ++ __field_prep(DFT_CPORT_MASK(fport), fe_cpu_port)); ++} ++ + static void airoha_ppe_hw_init(struct airoha_ppe *ppe) + { + u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries; +@@ -147,7 +161,9 @@ static void airoha_ppe_hw_init(struct ai + + airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); + +- for (p = 0; p < ARRAY_SIZE(eth->ports); p++) ++ for (p = 0; p < ARRAY_SIZE(eth->ports); p++) { ++ struct airoha_gdm_port *port = eth->ports[p]; ++ + airoha_fe_rmw(eth, REG_PPE_MTU(i, p), + FP0_EGRESS_MTU_MASK | + FP1_EGRESS_MTU_MASK, +@@ -155,6 +171,11 @@ static void airoha_ppe_hw_init(struct ai + AIROHA_MAX_MTU) | + FIELD_PREP(FP1_EGRESS_MTU_MASK, + AIROHA_MAX_MTU)); ++ if (!port) ++ continue; ++ ++ airoha_ppe_set_cpu_port(port, i); ++ } + } + } + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -312,10 +312,9 @@ + #define REG_PPE_HASH_SEED(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x244) + #define PPE_HASH_SEED 0x12345678 + +-#define REG_PPE_DFT_CPORT0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248) +-#define DFT_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2)) +- +-#define REG_PPE_DFT_CPORT1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x24c) ++#define REG_PPE_DFT_CPORT_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248) ++#define REG_PPE_DFT_CPORT(_m, _n) (REG_PPE_DFT_CPORT_BASE(_m) + (((_n) / 8) << 2)) ++#define DFT_CPORT_MASK(_n) GENMASK(3 + (((_n) % 8) << 2), (((_n) % 8) << 2)) + + #define REG_PPE_TB_HASH_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x250) + #define PPE_DRAM_HASH1_MODE_MASK GENMASK(31, 28) diff --git a/target/linux/airoha/patches-6.12/133-v7.1-net-airoha-Rework-the-code-flow-in-airoha_remove-and.patch b/target/linux/airoha/patches-6.12/133-v7.1-net-airoha-Rework-the-code-flow-in-airoha_remove-and.patch new file mode 100644 index 00000000000..37347c6af51 --- /dev/null +++ b/target/linux/airoha/patches-6.12/133-v7.1-net-airoha-Rework-the-code-flow-in-airoha_remove-and.patch @@ -0,0 +1,165 @@ +From b1c803d5c8167026791abfaed96fd3e6a1fcd750 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 21 Mar 2026 15:41:44 +0100 +Subject: [PATCH] net: airoha: Rework the code flow in airoha_remove() and in + airoha_probe() error path + +As suggested by Simon in [0], rework the code flow in airoha_remove() +and in the airoha_probe() error path in order to rely on a more common +approach un-registering configured net-devices first and destroying the +hw resources at the end of the code. +Introduce airoha_qdma_cleanup routine to release QDMA resources. + +[0] https://lore.kernel.org/netdev/20251214-airoha-fix-dev-registration-v1-1-860e027ad4c6@kernel.org/ + +Suggested-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260321-airoha-remove-rework-v2-1-16c7bade5fe5@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 76 ++++++++++++++---------- + 1 file changed, 44 insertions(+), 32 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1389,6 +1389,33 @@ static int airoha_qdma_init(struct platf + return airoha_qdma_hw_init(qdma); + } + ++static void airoha_qdma_cleanup(struct airoha_qdma *qdma) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ netif_napi_del(&qdma->q_rx[i].napi); ++ airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]); ++ if (qdma->q_rx[i].page_pool) { ++ page_pool_destroy(qdma->q_rx[i].page_pool); ++ qdma->q_rx[i].page_pool = NULL; ++ } ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) ++ netif_napi_del(&qdma->q_tx_irq[i].napi); ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { ++ if (!qdma->q_tx[i].ndesc) ++ continue; ++ ++ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); ++ } ++} ++ + static int airoha_hw_init(struct platform_device *pdev, + struct airoha_eth *eth) + { +@@ -1416,41 +1443,30 @@ static int airoha_hw_init(struct platfor + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { + err = airoha_qdma_init(pdev, eth, ð->qdma[i]); + if (err) +- return err; ++ goto error; + } + + err = airoha_ppe_init(eth); + if (err) +- return err; ++ goto error; + + set_bit(DEV_STATE_INITIALIZED, ð->state); + + return 0; ++error: ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_qdma_cleanup(ð->qdma[i]); ++ ++ return err; + } + +-static void airoha_hw_cleanup(struct airoha_qdma *qdma) ++static void airoha_hw_cleanup(struct airoha_eth *eth) + { + int i; + +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; +- +- netif_napi_del(&qdma->q_rx[i].napi); +- airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]); +- if (qdma->q_rx[i].page_pool) +- page_pool_destroy(qdma->q_rx[i].page_pool); +- } +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) +- netif_napi_del(&qdma->q_tx_irq[i].napi); +- +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +- if (!qdma->q_tx[i].ndesc) +- continue; +- +- airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]); +- } ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_qdma_cleanup(ð->qdma[i]); ++ airoha_ppe_deinit(eth); + } + + static void airoha_qdma_start_napi(struct airoha_qdma *qdma) +@@ -3033,7 +3049,7 @@ static int airoha_probe(struct platform_ + + err = airoha_hw_init(pdev, eth); + if (err) +- goto error_hw_cleanup; ++ goto error_netdev_free; + + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_qdma_start_napi(ð->qdma[i]); +@@ -3061,10 +3077,6 @@ static int airoha_probe(struct platform_ + error_napi_stop: + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_qdma_stop_napi(ð->qdma[i]); +- airoha_ppe_deinit(eth); +-error_hw_cleanup: +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) +- airoha_hw_cleanup(ð->qdma[i]); + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; +@@ -3076,6 +3088,8 @@ error_hw_cleanup: + unregister_netdev(port->dev); + airoha_metadata_dst_free(port); + } ++ airoha_hw_cleanup(eth); ++error_netdev_free: + free_netdev(eth->napi_dev); + platform_set_drvdata(pdev, NULL); + +@@ -3087,10 +3101,8 @@ static void airoha_remove(struct platfor + struct airoha_eth *eth = platform_get_drvdata(pdev); + int i; + +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) + airoha_qdma_stop_napi(ð->qdma[i]); +- airoha_hw_cleanup(ð->qdma[i]); +- } + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; +@@ -3101,9 +3113,9 @@ static void airoha_remove(struct platfor + unregister_netdev(port->dev); + airoha_metadata_dst_free(port); + } +- free_netdev(eth->napi_dev); ++ airoha_hw_cleanup(eth); + +- airoha_ppe_deinit(eth); ++ free_netdev(eth->napi_dev); + platform_set_drvdata(pdev, NULL); + } + diff --git a/target/linux/airoha/patches-6.12/134-v7.1-net-airoha-Delay-offloading-until-all-net_devices-ar.patch b/target/linux/airoha/patches-6.12/134-v7.1-net-airoha-Delay-offloading-until-all-net_devices-ar.patch new file mode 100644 index 00000000000..14be9c1925b --- /dev/null +++ b/target/linux/airoha/patches-6.12/134-v7.1-net-airoha-Delay-offloading-until-all-net_devices-ar.patch @@ -0,0 +1,63 @@ +From cedc1bf327de62ec30af9743bd1f601c2de30553 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 29 Mar 2026 12:32:27 +0200 +Subject: [PATCH] net: airoha: Delay offloading until all net_devices are fully + registered + +Netfilter flowtable can theoretically try to offload flower rules as soon +as a net_device is registered while all the other ones are not +registered or initialized, triggering a possible NULL pointer dereferencing +of qdma pointer in airoha_ppe_set_cpu_port routine. Moreover, if +register_netdev() fails for a particular net_device, there is a small +race if Netfilter tries to offload flowtable rules before all the +net_devices are properly unregistered in airoha_probe() error patch, +triggering a NULL pointer dereferencing in airoha_ppe_set_cpu_port +routine. In order to avoid any possible race, delay offloading until +all net_devices are registered in the networking subsystem. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260329-airoha-regiser-race-fix-v2-1-f4ebb139277b@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 ++ + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_ppe.c | 7 +++++++ + 3 files changed, 10 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2978,6 +2978,8 @@ static int airoha_register_gdm_devices(s + return err; + } + ++ set_bit(DEV_STATE_REGISTERED, ð->state); ++ + return 0; + } + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -88,6 +88,7 @@ enum { + + enum { + DEV_STATE_INITIALIZED, ++ DEV_STATE_REGISTERED, + }; + + enum { +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1387,6 +1387,13 @@ int airoha_ppe_setup_tc_block_cb(struct + struct airoha_eth *eth = ppe->eth; + int err = 0; + ++ /* Netfilter flowtable can try to offload flower rules while not all ++ * the net_devices are registered or initialized. Delay offloading ++ * until all net_devices are registered in the system. ++ */ ++ if (!test_bit(DEV_STATE_REGISTERED, ð->state)) ++ return -EBUSY; ++ + mutex_lock(&flow_offload_mutex); + + if (!eth->npu) diff --git a/target/linux/airoha/patches-6.12/136-v7.1-net-airoha-Add-dma_rmb-and-READ_ONCE-in-airoha_qdma_.patch b/target/linux/airoha/patches-6.12/136-v7.1-net-airoha-Add-dma_rmb-and-READ_ONCE-in-airoha_qdma_.patch new file mode 100644 index 00000000000..6cceacfbc65 --- /dev/null +++ b/target/linux/airoha/patches-6.12/136-v7.1-net-airoha-Add-dma_rmb-and-READ_ONCE-in-airoha_qdma_.patch @@ -0,0 +1,78 @@ +From 4ae0604a0673e11e2075b178387151fcad5111b5 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Apr 2026 08:48:04 +0200 +Subject: [PATCH] net: airoha: Add dma_rmb() and READ_ONCE() in + airoha_qdma_rx_process() + +Add missing dma_rmb() in airoha_qdma_rx_process routine to make sure the +DMA read operations are completed when the NIC reports the processing on +the current descriptor is done. Moreover, add missing READ_ONCE() in +airoha_qdma_rx_process() for DMA descriptor control fields in order to +avoid any compiler reordering. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260407-airoha_qdma_rx_process-fix-reordering-v3-1-91c36e9da31f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -584,7 +584,7 @@ static int airoha_qdma_fill_rx_queue(str + static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, + struct airoha_qdma_desc *desc) + { +- u32 port, sport, msg1 = le32_to_cpu(desc->msg1); ++ u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1)); + + sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); + switch (sport) { +@@ -612,21 +612,24 @@ static int airoha_qdma_rx_process(struct + while (done < budget) { + struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; +- u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); +- struct page *page = virt_to_head_page(e->buf); +- u32 desc_ctrl = le32_to_cpu(desc->ctrl); ++ u32 hash, reason, msg1, desc_ctrl; + struct airoha_gdm_port *port; + int data_len, len, p; ++ struct page *page; + ++ desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); + if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) + break; + ++ dma_rmb(); ++ + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + + dma_sync_single_for_cpu(eth->dev, e->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), dir); + ++ page = virt_to_head_page(e->buf); + len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); + data_len = q->skb ? q->buf_size + : SKB_WITH_OVERHEAD(q->buf_size); +@@ -670,8 +673,8 @@ static int airoha_qdma_rx_process(struct + * DMA descriptor. Report DSA tag to the DSA stack + * via skb dst info. + */ +- u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG, +- le32_to_cpu(desc->msg0)); ++ u32 msg0 = le32_to_cpu(READ_ONCE(desc->msg0)); ++ u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG, msg0); + + if (sptag < ARRAY_SIZE(port->dsa_meta) && + port->dsa_meta[sptag]) +@@ -679,6 +682,7 @@ static int airoha_qdma_rx_process(struct + &port->dsa_meta[sptag]->dst); + } + ++ msg1 = le32_to_cpu(READ_ONCE(desc->msg1)); + hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1); + if (hash != AIROHA_RXD4_FOE_ENTRY) + skb_set_hash(q->skb, jhash_1word(hash, 0), diff --git a/target/linux/airoha/patches-6.12/137-v7.1-net-airoha-Add-missing-PPE-configurations-in-airoha_.patch b/target/linux/airoha/patches-6.12/137-v7.1-net-airoha-Add-missing-PPE-configurations-in-airoha_.patch new file mode 100644 index 00000000000..a6089f9a9ef --- /dev/null +++ b/target/linux/airoha/patches-6.12/137-v7.1-net-airoha-Add-missing-PPE-configurations-in-airoha_.patch @@ -0,0 +1,62 @@ +From b9d8b856689d2b968495d79fe653d87fcb8ad98c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Apr 2026 10:43:26 +0200 +Subject: [PATCH] net: airoha: Add missing PPE configurations in + airoha_ppe_hw_init() + +Add the following PPE configuration in airoha_ppe_hw_init routine: +- 6RD hw offloading is currently not supported by Netfilter flowtable. + Disable explicitly PPE 6RD offloading in order to prevent PPE to learn + 6RD flows and eventually interrupt the traffic. +- Add missing PPE bind rate configuration for L3 and L2 traffic. + PPE bind rate configuration specifies the pps threshold to move a PPE + entry state from UNBIND to BIND. Without this configuration this value + is random. +- Set ageing thresholds to the values used in the vendor SDK in order to + improve connection stability under load and avoid packet loss caused by + fast aging. + +Fixes: 00a7678310fe3 ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260412-airoha_ppe_hw_init-missing-bits-v1-1-06ac670819e3@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -125,13 +125,13 @@ static void airoha_ppe_hw_init(struct ai + airoha_fe_rmw(eth, REG_PPE_BND_AGE0(i), + PPE_BIND_AGE0_DELTA_NON_L4 | + PPE_BIND_AGE0_DELTA_UDP, +- FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 1) | +- FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 12)); ++ FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 60) | ++ FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 60)); + airoha_fe_rmw(eth, REG_PPE_BND_AGE1(i), + PPE_BIND_AGE1_DELTA_TCP_FIN | + PPE_BIND_AGE1_DELTA_TCP, + FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP_FIN, 1) | +- FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 7)); ++ FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 60)); + + airoha_fe_rmw(eth, REG_PPE_TB_HASH_CFG(i), + PPE_SRAM_TABLE_EN_MASK | +@@ -159,7 +159,15 @@ static void airoha_ppe_hw_init(struct ai + FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK, + dram_num_entries)); + ++ airoha_fe_rmw(eth, REG_PPE_BIND_RATE(i), ++ PPE_BIND_RATE_L2B_BIND_MASK | ++ PPE_BIND_RATE_BIND_MASK, ++ FIELD_PREP(PPE_BIND_RATE_L2B_BIND_MASK, 0x1e) | ++ FIELD_PREP(PPE_BIND_RATE_BIND_MASK, 0x1e)); ++ + airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); ++ airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i), ++ PPE_FLOW_CFG_IP6_6RD_MASK); + + for (p = 0; p < ARRAY_SIZE(eth->ports); p++) { + struct airoha_gdm_port *port = eth->ports[p]; diff --git a/target/linux/airoha/patches-6.12/139-v7.1-net-airoha-Fix-FE_PSE_BUF_SET-configuration-if-PPE2-.patch b/target/linux/airoha/patches-6.12/139-v7.1-net-airoha-Fix-FE_PSE_BUF_SET-configuration-if-PPE2-.patch new file mode 100644 index 00000000000..2053e0eff99 --- /dev/null +++ b/target/linux/airoha/patches-6.12/139-v7.1-net-airoha-Fix-FE_PSE_BUF_SET-configuration-if-PPE2-.patch @@ -0,0 +1,45 @@ +From 02f72964395911e7a09bb2ea2fe6f79eda4ea2c2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 8 Apr 2026 12:20:09 +0200 +Subject: [PATCH] net: airoha: Fix FE_PSE_BUF_SET configuration if PPE2 is + available + +airoha_fe_set routine is used to set specified bits to 1 in the selected +register. In the FE_PSE_BUF_SET case this can due to a overestimation of +the required buffers for I/O queues since we can miss to set some bits +of PSE_ALLRSV_MASK subfield to 0. Fix the issue relying on airoha_fe_rmw +routine instead. + +Fixes: 8e38e08f2c560 ("net: airoha: fix PSE memory configuration in airoha_fe_pse_ports_init()") +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260408-airoha-reg_fe_pse_buf_set-v1-1-0c4fa8f4d1d9@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -293,16 +293,18 @@ static void airoha_fe_pse_ports_init(str + [FE_PSE_PORT_GDM4] = 2, + [FE_PSE_PORT_CDM5] = 2, + }; +- u32 all_rsv; + int q; + +- all_rsv = airoha_fe_get_pse_all_rsv(eth); + if (airoha_ppe_is_enabled(eth, 1)) { ++ u32 all_rsv; ++ + /* hw misses PPE2 oq rsv */ ++ all_rsv = airoha_fe_get_pse_all_rsv(eth); + all_rsv += PSE_RSV_PAGES * + pse_port_num_queues[FE_PSE_PORT_PPE2]; ++ airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK, ++ FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); + } +- airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv); + + /* CMD1 */ + for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) diff --git a/target/linux/airoha/patches-6.12/140-v7.1-net-airoha-Fix-memory-leak-in-airoha_qdma_rx_process.patch b/target/linux/airoha/patches-6.12/140-v7.1-net-airoha-Fix-memory-leak-in-airoha_qdma_rx_process.patch new file mode 100644 index 00000000000..9d331d8d811 --- /dev/null +++ b/target/linux/airoha/patches-6.12/140-v7.1-net-airoha-Fix-memory-leak-in-airoha_qdma_rx_process.patch @@ -0,0 +1,37 @@ +From 285fa6b1e03cff78ead0383e1b259c44b95faf90 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 2 Apr 2026 14:57:10 +0200 +Subject: [PATCH] net: airoha: Fix memory leak in airoha_qdma_rx_process() + +If an error occurs on the subsequents buffers belonging to the +non-linear part of the skb (e.g. due to an error in the payload length +reported by the NIC or if we consumed all the available fragments for +the skb), the page_pool fragment will not be linked to the skb so it will +not return to the pool in the airoha_qdma_rx_process() error path. Fix the +memory leak partially reverting commit 'd6d2b0e1538d ("net: airoha: Fix +page recycling in airoha_qdma_rx_process()")' and always running +page_pool_put_full_page routine in the airoha_qdma_rx_process() error +path. + +Fixes: d6d2b0e1538d ("net: airoha: Fix page recycling in airoha_qdma_rx_process()") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260402-airoha_qdma_rx_process-mem-leak-fix-v1-1-b5706f402d3c@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -703,9 +703,8 @@ free_frag: + if (q->skb) { + dev_kfree_skb(q->skb); + q->skb = NULL; +- } else { +- page_pool_put_full_page(q->page_pool, page, true); + } ++ page_pool_put_full_page(q->page_pool, page, true); + } + airoha_qdma_fill_rx_queue(q); + diff --git a/target/linux/airoha/patches-6.12/141-v7.1-net-airoha-Fix-VIP-configuration-for-AN7583-SoC.patch b/target/linux/airoha/patches-6.12/141-v7.1-net-airoha-Fix-VIP-configuration-for-AN7583-SoC.patch new file mode 100644 index 00000000000..6b659879b50 --- /dev/null +++ b/target/linux/airoha/patches-6.12/141-v7.1-net-airoha-Fix-VIP-configuration-for-AN7583-SoC.patch @@ -0,0 +1,173 @@ +From 1acdfbdb516b32165a8ecd1d5f8c68e4eac64637 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Apr 2026 09:57:29 +0200 +Subject: [PATCH] net: airoha: Fix VIP configuration for AN7583 SoC + +EN7581 and AN7583 SoCs have different VIP definitions. Introduce +get_vip_port callback in airoha_eth_soc_data struct in order to take +into account EN7581 and AN7583 VIP register layout and definition +differences. +Introduce nbq parameter in airoha_gdm_port struct. At the moment nbq +is set statically to value previously used in airhoha_set_gdm2_loopback +routine and it will be read from device tree in subsequent patches. + +Fixes: e4e5ce823bdd ("net: airoha: Add AN7583 SoC support") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260412-airoha-7583-vip-fix-v1-1-c35e02b054bb@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 66 ++++++++++++++++++------ + drivers/net/ethernet/airoha/airoha_eth.h | 2 + + 2 files changed, 51 insertions(+), 17 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -107,19 +107,7 @@ static int airoha_set_vip_for_gdm_port(s + struct airoha_eth *eth = port->qdma->eth; + u32 vip_port; + +- switch (port->id) { +- case AIROHA_GDM3_IDX: +- /* FIXME: handle XSI_PCIE1_PORT */ +- vip_port = XSI_PCIE0_VIP_PORT_MASK; +- break; +- case AIROHA_GDM4_IDX: +- /* FIXME: handle XSI_USB_PORT */ +- vip_port = XSI_ETH_VIP_PORT_MASK; +- break; +- default: +- return 0; +- } +- ++ vip_port = eth->soc->ops.get_vip_port(port, port->nbq); + if (enable) { + airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); + airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); +@@ -1738,7 +1726,7 @@ static int airoha_dev_set_macaddr(struct + static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) + { + struct airoha_eth *eth = port->qdma->eth; +- u32 val, pse_port, chan, nbq; ++ u32 val, pse_port, chan; + int src_port; + + /* Forward the traffic to the proper GDM port */ +@@ -1768,9 +1756,7 @@ static int airhoha_set_gdm2_loopback(str + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); + airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); + +- /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */ +- nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; +- src_port = eth->soc->ops.get_src_port_id(port, nbq); ++ src_port = eth->soc->ops.get_src_port_id(port, port->nbq); + if (src_port < 0) + return src_port; + +@@ -1784,7 +1770,7 @@ static int airhoha_set_gdm2_loopback(str + __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { +- u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq); ++ u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); + + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, + __field_prep(mask, AIROHA_GDM2_IDX)); +@@ -2962,6 +2948,8 @@ static int airoha_alloc_gdm_port(struct + port->eth = eth; + port->dev = dev; + port->id = id; ++ /* XXX: Read nbq from DTS */ ++ port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + eth->ports[p] = port; + + return airoha_metadata_dst_alloc(port); +@@ -3158,6 +3146,28 @@ static int airoha_en7581_get_src_port_id + return -EINVAL; + } + ++static u32 airoha_en7581_get_vip_port(struct airoha_gdm_port *port, int nbq) ++{ ++ switch (port->id) { ++ case AIROHA_GDM3_IDX: ++ if (nbq == 4) ++ return XSI_PCIE0_VIP_PORT_MASK; ++ if (nbq == 5) ++ return XSI_PCIE1_VIP_PORT_MASK; ++ break; ++ case AIROHA_GDM4_IDX: ++ if (!nbq) ++ return XSI_ETH_VIP_PORT_MASK; ++ if (nbq == 1) ++ return XSI_USB_VIP_PORT_MASK; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static const char * const an7583_xsi_rsts_names[] = { + "xsi-mac", + "hsi0-mac", +@@ -3187,6 +3197,26 @@ static int airoha_an7583_get_src_port_id + return -EINVAL; + } + ++static u32 airoha_an7583_get_vip_port(struct airoha_gdm_port *port, int nbq) ++{ ++ switch (port->id) { ++ case AIROHA_GDM3_IDX: ++ if (!nbq) ++ return XSI_ETH_VIP_PORT_MASK; ++ break; ++ case AIROHA_GDM4_IDX: ++ if (!nbq) ++ return XSI_PCIE0_VIP_PORT_MASK; ++ if (nbq == 1) ++ return XSI_USB_VIP_PORT_MASK; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static const struct airoha_eth_soc_data en7581_soc_data = { + .version = 0x7581, + .xsi_rsts_names = en7581_xsi_rsts_names, +@@ -3194,6 +3224,7 @@ static const struct airoha_eth_soc_data + .num_ppe = 2, + .ops = { + .get_src_port_id = airoha_en7581_get_src_port_id, ++ .get_vip_port = airoha_en7581_get_vip_port, + }, + }; + +@@ -3204,6 +3235,7 @@ static const struct airoha_eth_soc_data + .num_ppe = 1, + .ops = { + .get_src_port_id = airoha_an7583_get_src_port_id, ++ .get_vip_port = airoha_an7583_get_vip_port, + }, + }; + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -537,6 +537,7 @@ struct airoha_gdm_port { + struct airoha_eth *eth; + struct net_device *dev; + int id; ++ int nbq; + + struct airoha_hw_stats stats; + +@@ -577,6 +578,7 @@ struct airoha_eth_soc_data { + int num_ppe; + struct { + int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq); ++ u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.12/142-01-v7.1-net-airoha-Rely-on-net_device-pointer-in-airoha_dev_.patch b/target/linux/airoha/patches-6.12/142-01-v7.1-net-airoha-Rely-on-net_device-pointer-in-airoha_dev_.patch new file mode 100644 index 00000000000..d09272a8af6 --- /dev/null +++ b/target/linux/airoha/patches-6.12/142-01-v7.1-net-airoha-Rely-on-net_device-pointer-in-airoha_dev_.patch @@ -0,0 +1,64 @@ +From 360d745a5319f09849a94dee0974c8ead721e392 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Apr 2026 19:13:12 +0200 +Subject: [PATCH 1/4] net: airoha: Rely on net_device pointer in + airoha_dev_setup_tc_block signature + +Remove airoha_gdm_port dependency in airoha_dev_setup_tc_block routine +signature and rely on net_device pointer instead. Please note this patch +does not introduce any logical change and it is a preliminary patch to +support multiple net_devices connected to the GDM3 or GDM4 ports via an +external hw arbiter. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260412-airoha-multi-serdes-preliminary-patch-v1-1-08d5b670ca8f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2683,7 +2683,7 @@ static int airoha_dev_setup_tc_block_cb( + } + } + +-static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port, ++static int airoha_dev_setup_tc_block(struct net_device *dev, + struct flow_block_offload *f) + { + flow_setup_cb_t *cb = airoha_dev_setup_tc_block_cb; +@@ -2696,12 +2696,12 @@ static int airoha_dev_setup_tc_block(str + f->driver_block_list = &block_cb_list; + switch (f->command) { + case FLOW_BLOCK_BIND: +- block_cb = flow_block_cb_lookup(f->block, cb, port->dev); ++ block_cb = flow_block_cb_lookup(f->block, cb, dev); + if (block_cb) { + flow_block_cb_incref(block_cb); + return 0; + } +- block_cb = flow_block_cb_alloc(cb, port->dev, port->dev, NULL); ++ block_cb = flow_block_cb_alloc(cb, dev, dev, NULL); + if (IS_ERR(block_cb)) + return PTR_ERR(block_cb); + +@@ -2710,7 +2710,7 @@ static int airoha_dev_setup_tc_block(str + list_add_tail(&block_cb->driver_list, &block_cb_list); + return 0; + case FLOW_BLOCK_UNBIND: +- block_cb = flow_block_cb_lookup(f->block, cb, port->dev); ++ block_cb = flow_block_cb_lookup(f->block, cb, dev); + if (!block_cb) + return -ENOENT; + +@@ -2809,7 +2809,7 @@ static int airoha_dev_tc_setup(struct ne + return airoha_tc_setup_qdisc_htb(port, type_data); + case TC_SETUP_BLOCK: + case TC_SETUP_FT: +- return airoha_dev_setup_tc_block(port, type_data); ++ return airoha_dev_setup_tc_block(dev, type_data); + default: + return -EOPNOTSUPP; + } diff --git a/target/linux/airoha/patches-6.12/142-02-v7.1-net-airoha-Rely-on-net_device-pointer-in-HTB-callbac.patch b/target/linux/airoha/patches-6.12/142-02-v7.1-net-airoha-Rely-on-net_device-pointer-in-HTB-callbac.patch new file mode 100644 index 00000000000..76651dbe601 --- /dev/null +++ b/target/linux/airoha/patches-6.12/142-02-v7.1-net-airoha-Rely-on-net_device-pointer-in-HTB-callbac.patch @@ -0,0 +1,163 @@ +From 8baf4bf72ef94c955ef89d4644f1986603ee8320 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Apr 2026 19:13:13 +0200 +Subject: [PATCH 2/4] net: airoha: Rely on net_device pointer in HTB callbacks + +Remove airoha_gdm_port dependency in HTB tc callback signatures and rely +on net_device pointer instead. Please note this patch does not introduce +any logical change and it is a preliminary patch in order to support +multiple net_devices connected to the same GDM3 or GDM4 port via an +external hw arbiter. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260412-airoha-multi-serdes-preliminary-patch-v1-2-08d5b670ca8f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 45 +++++++++++++----------- + 1 file changed, 24 insertions(+), 21 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2488,10 +2488,11 @@ static int airoha_qdma_set_trtcm_token_b + mode, val); + } + +-static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port, ++static int airoha_qdma_set_tx_rate_limit(struct net_device *dev, + int channel, u32 rate, + u32 bucket_size) + { ++ struct airoha_gdm_port *port = netdev_priv(dev); + int i, err; + + for (i = 0; i <= TRTCM_PEAK_MODE; i++) { +@@ -2511,21 +2512,20 @@ static int airoha_qdma_set_tx_rate_limit + return 0; + } + +-static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port, ++static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev, + struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; + u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ +- struct net_device *dev = port->dev; +- int num_tx_queues = dev->real_num_tx_queues; +- int err; ++ int err, num_tx_queues = dev->real_num_tx_queues; ++ struct airoha_gdm_port *port = netdev_priv(dev); + + if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); + return -EINVAL; + } + +- err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum); ++ err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum); + if (err) { + NL_SET_ERR_MSG_MOD(opt->extack, + "failed configuring htb offload"); +@@ -2537,7 +2537,7 @@ static int airoha_tc_htb_alloc_leaf_queu + + err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); + if (err) { +- airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum); ++ airoha_qdma_set_tx_rate_limit(dev, channel, 0, opt->quantum); + NL_SET_ERR_MSG_MOD(opt->extack, + "failed setting real_num_tx_queues"); + return err; +@@ -2724,44 +2724,47 @@ static int airoha_dev_setup_tc_block(str + } + } + +-static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue) ++static void airoha_tc_remove_htb_queue(struct net_device *dev, int queue) + { +- struct net_device *dev = port->dev; ++ struct airoha_gdm_port *port = netdev_priv(dev); + + netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); +- airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0); ++ airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0); + clear_bit(queue, port->qos_sq_bmap); + } + +-static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port, ++static int airoha_tc_htb_delete_leaf_queue(struct net_device *dev, + struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ struct airoha_gdm_port *port = netdev_priv(dev); + + if (!test_bit(channel, port->qos_sq_bmap)) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); + return -EINVAL; + } + +- airoha_tc_remove_htb_queue(port, channel); ++ airoha_tc_remove_htb_queue(dev, channel); + + return 0; + } + +-static int airoha_tc_htb_destroy(struct airoha_gdm_port *port) ++static int airoha_tc_htb_destroy(struct net_device *dev) + { ++ struct airoha_gdm_port *port = netdev_priv(dev); + int q; + + for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) +- airoha_tc_remove_htb_queue(port, q); ++ airoha_tc_remove_htb_queue(dev, q); + + return 0; + } + +-static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port, ++static int airoha_tc_get_htb_get_leaf_queue(struct net_device *dev, + struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ struct airoha_gdm_port *port = netdev_priv(dev); + + if (!test_bit(channel, port->qos_sq_bmap)) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); +@@ -2773,23 +2776,23 @@ static int airoha_tc_get_htb_get_leaf_qu + return 0; + } + +-static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port, ++static int airoha_tc_setup_qdisc_htb(struct net_device *dev, + struct tc_htb_qopt_offload *opt) + { + switch (opt->command) { + case TC_HTB_CREATE: + break; + case TC_HTB_DESTROY: +- return airoha_tc_htb_destroy(port); ++ return airoha_tc_htb_destroy(dev); + case TC_HTB_NODE_MODIFY: + case TC_HTB_LEAF_ALLOC_QUEUE: +- return airoha_tc_htb_alloc_leaf_queue(port, opt); ++ return airoha_tc_htb_alloc_leaf_queue(dev, opt); + case TC_HTB_LEAF_DEL: + case TC_HTB_LEAF_DEL_LAST: + case TC_HTB_LEAF_DEL_LAST_FORCE: +- return airoha_tc_htb_delete_leaf_queue(port, opt); ++ return airoha_tc_htb_delete_leaf_queue(dev, opt); + case TC_HTB_LEAF_QUERY_QUEUE: +- return airoha_tc_get_htb_get_leaf_queue(port, opt); ++ return airoha_tc_get_htb_get_leaf_queue(dev, opt); + default: + return -EOPNOTSUPP; + } +@@ -2806,7 +2809,7 @@ static int airoha_dev_tc_setup(struct ne + case TC_SETUP_QDISC_ETS: + return airoha_tc_setup_qdisc_ets(port, type_data); + case TC_SETUP_QDISC_HTB: +- return airoha_tc_setup_qdisc_htb(port, type_data); ++ return airoha_tc_setup_qdisc_htb(dev, type_data); + case TC_SETUP_BLOCK: + case TC_SETUP_FT: + return airoha_dev_setup_tc_block(dev, type_data); diff --git a/target/linux/airoha/patches-6.12/142-03-v7.1-net-airoha-Rely-on-net_device-pointer-in-ETS-callbac.patch b/target/linux/airoha/patches-6.12/142-03-v7.1-net-airoha-Rely-on-net_device-pointer-in-ETS-callbac.patch new file mode 100644 index 00000000000..a8b15717c0b --- /dev/null +++ b/target/linux/airoha/patches-6.12/142-03-v7.1-net-airoha-Rely-on-net_device-pointer-in-ETS-callbac.patch @@ -0,0 +1,118 @@ +From ae32f80018f0f0f4ebc7a0a70d4092d08a1545e8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Apr 2026 19:13:14 +0200 +Subject: [PATCH 3/4] net: airoha: Rely on net_device pointer in ETS callbacks + +Remove airoha_gdm_port dependency in ETS tc callback signatures and rely +on net_device pointer instead. Please note this patch does not introduce +any logical change and it is a preliminary patch in order to support +multiple net_devices connected to the same GDM3 or GDM4 port via an +external hw arbiter. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260412-airoha-multi-serdes-preliminary-patch-v1-3-08d5b670ca8f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 30 +++++++++++------------- + 1 file changed, 14 insertions(+), 16 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2134,10 +2134,11 @@ airoha_ethtool_get_rmon_stats(struct net + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + +-static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port, ++static int airoha_qdma_set_chan_tx_sched(struct net_device *dev, + int channel, enum tx_sched_mode mode, + const u16 *weights, u8 n_weights) + { ++ struct airoha_gdm_port *port = netdev_priv(dev); + int i; + + for (i = 0; i < AIROHA_NUM_TX_RING; i++) +@@ -2169,17 +2170,15 @@ static int airoha_qdma_set_chan_tx_sched + return 0; + } + +-static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port, +- int channel) ++static int airoha_qdma_set_tx_prio_sched(struct net_device *dev, int channel) + { + static const u16 w[AIROHA_NUM_QOS_QUEUES] = {}; + +- return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w, ++ return airoha_qdma_set_chan_tx_sched(dev, channel, TC_SCH_SP, w, + ARRAY_SIZE(w)); + } + +-static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port, +- int channel, ++static int airoha_qdma_set_tx_ets_sched(struct net_device *dev, int channel, + struct tc_ets_qopt_offload *opt) + { + struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; +@@ -2220,20 +2219,21 @@ static int airoha_qdma_set_tx_ets_sched( + else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1) + mode = nstrict + 1; + +- return airoha_qdma_set_chan_tx_sched(port, channel, mode, w, ++ return airoha_qdma_set_chan_tx_sched(dev, channel, mode, w, + ARRAY_SIZE(w)); + } + +-static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port, +- int channel, ++static int airoha_qdma_get_tx_ets_stats(struct net_device *dev, int channel, + struct tc_ets_qopt_offload *opt) + { ++ struct airoha_gdm_port *port = netdev_priv(dev); + u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, + REG_CNTR_VAL(channel << 1)); + u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, + REG_CNTR_VAL((channel << 1) + 1)); + u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + + (fwd_tx_packets - port->fwd_tx_packets); ++ + _bstats_update(opt->stats.bstats, 0, tx_packets); + + port->cpu_tx_packets = cpu_tx_packets; +@@ -2242,7 +2242,7 @@ static int airoha_qdma_get_tx_ets_stats( + return 0; + } + +-static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, ++static int airoha_tc_setup_qdisc_ets(struct net_device *dev, + struct tc_ets_qopt_offload *opt) + { + int channel; +@@ -2255,12 +2255,12 @@ static int airoha_tc_setup_qdisc_ets(str + + switch (opt->command) { + case TC_ETS_REPLACE: +- return airoha_qdma_set_tx_ets_sched(port, channel, opt); ++ return airoha_qdma_set_tx_ets_sched(dev, channel, opt); + case TC_ETS_DESTROY: + /* PRIO is default qdisc scheduler */ +- return airoha_qdma_set_tx_prio_sched(port, channel); ++ return airoha_qdma_set_tx_prio_sched(dev, channel); + case TC_ETS_STATS: +- return airoha_qdma_get_tx_ets_stats(port, channel, opt); ++ return airoha_qdma_get_tx_ets_stats(dev, channel, opt); + default: + return -EOPNOTSUPP; + } +@@ -2803,11 +2803,9 @@ static int airoha_tc_setup_qdisc_htb(str + static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, + void *type_data) + { +- struct airoha_gdm_port *port = netdev_priv(dev); +- + switch (type) { + case TC_SETUP_QDISC_ETS: +- return airoha_tc_setup_qdisc_ets(port, type_data); ++ return airoha_tc_setup_qdisc_ets(dev, type_data); + case TC_SETUP_QDISC_HTB: + return airoha_tc_setup_qdisc_htb(dev, type_data); + case TC_SETUP_BLOCK: diff --git a/target/linux/airoha/patches-6.12/142-04-v7.1-net-airoha-Remove-PCE_MC_EN_MASK-bit-in-REG_FE_PCE_C.patch b/target/linux/airoha/patches-6.12/142-04-v7.1-net-airoha-Remove-PCE_MC_EN_MASK-bit-in-REG_FE_PCE_C.patch new file mode 100644 index 00000000000..3067915a3cf --- /dev/null +++ b/target/linux/airoha/patches-6.12/142-04-v7.1-net-airoha-Remove-PCE_MC_EN_MASK-bit-in-REG_FE_PCE_C.patch @@ -0,0 +1,33 @@ +From 34e1a98ff2a87cf4b8de3ccebe9d45273f014aeb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sun, 12 Apr 2026 11:56:25 +0200 +Subject: [PATCH 4/4] net: airoha: Remove PCE_MC_EN_MASK bit in REG_FE_PCE_CFG + configuration + +PCE_MC_EN_MASK bit in REG_FE_PCE_CFG configuration performed in +airoha_fe_init() is used to duplicate multicast packets and send a copy +to the CPU when the traffic is offloaded. This is necessary just if +it is requested by the user. Disable multicast packets duplication by +default. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260412-airoha_fe_init_remove_mc_en_bit-v1-1-7b6a5a25a74d@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -448,9 +448,8 @@ static int airoha_fe_init(struct airoha_ + FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) | + FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34)); + +- /* enable FE copy engine for MC/KA/DPI */ +- airoha_fe_wr(eth, REG_FE_PCE_CFG, +- PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK); ++ /* enable FE copy engine for KA/DPI */ ++ airoha_fe_wr(eth, REG_FE_PCE_CFG, PCE_DPI_EN_MASK | PCE_KA_EN_MASK); + /* set vip queue selection to ring 1 */ + airoha_fe_rmw(eth, REG_CDM_FWD_CFG(1), CDM_VIP_QSEL_MASK, + FIELD_PREP(CDM_VIP_QSEL_MASK, 0x4)); diff --git a/target/linux/airoha/patches-6.12/143-v7.1-net-airoha-Fix-typo-in-airoha_set_gdm2_loopback-rout.patch b/target/linux/airoha/patches-6.12/143-v7.1-net-airoha-Fix-typo-in-airoha_set_gdm2_loopback-rout.patch new file mode 100644 index 00000000000..dcbc223ca1b --- /dev/null +++ b/target/linux/airoha/patches-6.12/143-v7.1-net-airoha-Fix-typo-in-airoha_set_gdm2_loopback-rout.patch @@ -0,0 +1,35 @@ +From a94ddc191f19579a7e0a5da2c012f1048ce10262 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 30 Mar 2026 00:03:49 +0200 +Subject: [PATCH] net: airoha: Fix typo in airoha_set_gdm2_loopback routine + name + +Rename airhoha_set_gdm2_loopback() in airoha_set_gdm2_loopback() + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260330-airoha_set_gdm2_loopback-fix-typo-v1-1-a1320ff6b6cc@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1722,7 +1722,7 @@ static int airoha_dev_set_macaddr(struct + return 0; + } + +-static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) ++static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) + { + struct airoha_eth *eth = port->qdma->eth; + u32 val, pse_port, chan; +@@ -1796,7 +1796,7 @@ static int airoha_dev_init(struct net_de + if (!eth->ports[1]) { + int err; + +- err = airhoha_set_gdm2_loopback(port); ++ err = airoha_set_gdm2_loopback(port); + if (err) + return err; + } diff --git a/target/linux/airoha/patches-6.12/144-v7.1-net-airoha-Set-REG_RX_CPU_IDX-once-in-airoha_qdma_fi.patch b/target/linux/airoha/patches-6.12/144-v7.1-net-airoha-Set-REG_RX_CPU_IDX-once-in-airoha_qdma_fi.patch new file mode 100644 index 00000000000..8a982330b26 --- /dev/null +++ b/target/linux/airoha/patches-6.12/144-v7.1-net-airoha-Set-REG_RX_CPU_IDX-once-in-airoha_qdma_fi.patch @@ -0,0 +1,34 @@ +From 269389ba539834ec80e4d55583fca2cd70e4dc9c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 31 Mar 2026 12:33:24 +0200 +Subject: [PATCH] net: airoha: Set REG_RX_CPU_IDX() once in + airoha_qdma_fill_rx_queue() + +It is not necessary to update REG_RX_CPU_IDX register for each iteration +of the descriptor loop in airoha_qdma_fill_rx_queue routine. +Move REG_RX_CPU_IDX configuration out of the descriptor loop and rely on +the last queue head value updated in the descriptor loop. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260331-airoha-cpu-idx-out-off-loop-v1-1-75c66b428f50@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -561,11 +561,12 @@ static int airoha_qdma_fill_rx_queue(str + WRITE_ONCE(desc->msg1, 0); + WRITE_ONCE(desc->msg2, 0); + WRITE_ONCE(desc->msg3, 0); ++ } + ++ if (nframes) + airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), + RX_RING_CPU_IDX_MASK, + FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); +- } + + return nframes; + } diff --git a/target/linux/airoha/patches-6.12/145-01-v7.1-net-airoha-Move-ndesc-initialization-at-end-of-airoh.patch b/target/linux/airoha/patches-6.12/145-01-v7.1-net-airoha-Move-ndesc-initialization-at-end-of-airoh.patch new file mode 100644 index 00000000000..c4045cc8af4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/145-01-v7.1-net-airoha-Move-ndesc-initialization-at-end-of-airoh.patch @@ -0,0 +1,55 @@ +From f329924bb49458c65297f1361f545816a5b90998 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Apr 2026 08:36:31 +0200 +Subject: [PATCH 1/2] net: airoha: Move ndesc initialization at end of + airoha_qdma_init_tx() + +If queue entry list allocation fails in airoha_qdma_init_tx_queue routine, +airoha_qdma_cleanup_tx_queue() will trigger a NULL pointer dereference +accessing the queue entry array. The issue is due to the early ndesc +initialization in airoha_qdma_init_tx_queue(). Fix the issue moving ndesc +initialization at end of airoha_qdma_init_tx routine. + +Fixes: 3f47e67dff1f7 ("net: airoha: Add the capability to consume out-of-order DMA tx descriptors") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260417-airoha_qdma_cleanup_tx_queue-fix-net-v4-1-e04bcc2c9642@kernel.org +Reviewed-by: Simon Horman +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -954,27 +954,27 @@ static int airoha_qdma_init_tx_queue(str + dma_addr_t dma_addr; + + spin_lock_init(&q->lock); +- q->ndesc = size; + q->qdma = qdma; + q->free_thr = 1 + MAX_SKB_FRAGS; + INIT_LIST_HEAD(&q->tx_list); + +- q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), ++ q->entry = devm_kzalloc(eth->dev, size * sizeof(*q->entry), + GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + +- q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), ++ q->desc = dmam_alloc_coherent(eth->dev, size * sizeof(*q->desc), + &dma_addr, GFP_KERNEL); + if (!q->desc) + return -ENOMEM; + +- for (i = 0; i < q->ndesc; i++) { ++ for (i = 0; i < size; i++) { + u32 val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); + + list_add_tail(&q->entry[i].list, &q->tx_list); + WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); + } ++ q->ndesc = size; + + /* xmit ring drop default setting */ + airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), diff --git a/target/linux/airoha/patches-6.12/145-02-v7.1-net-airoha-Add-missing-bits-in-airoha_qdma_cleanup_t.patch b/target/linux/airoha/patches-6.12/145-02-v7.1-net-airoha-Add-missing-bits-in-airoha_qdma_cleanup_t.patch new file mode 100644 index 00000000000..424a45c50b3 --- /dev/null +++ b/target/linux/airoha/patches-6.12/145-02-v7.1-net-airoha-Add-missing-bits-in-airoha_qdma_cleanup_t.patch @@ -0,0 +1,73 @@ +From 3309965fe44c00fd65af7cef5016e9e782c021a7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Apr 2026 08:36:32 +0200 +Subject: [PATCH 2/2] net: airoha: Add missing bits in + airoha_qdma_cleanup_tx_queue() + +Similar to airoha_qdma_cleanup_rx_queue(), reset DMA TX descriptors in +airoha_qdma_cleanup_tx_queue routine. Moreover, reset TX_DMA_IDX to +TX_CPU_IDX to notify the NIC the QDMA TX ring is empty. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260417-airoha_qdma_cleanup_tx_queue-fix-net-v4-2-e04bcc2c9642@kernel.org +Reviewed-by: Simon Horman +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 32 ++++++++++++++++++++++-- + 1 file changed, 30 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1039,12 +1039,15 @@ static int airoha_qdma_init_tx(struct ai + + static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) + { +- struct airoha_eth *eth = q->qdma->eth; +- int i; ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ int i, qid = q - &qdma->q_tx[0]; ++ u16 index = 0; + + spin_lock_bh(&q->lock); + for (i = 0; i < q->ndesc; i++) { + struct airoha_queue_entry *e = &q->entry[i]; ++ struct airoha_qdma_desc *desc = &q->desc[i]; + + if (!e->dma_addr) + continue; +@@ -1055,8 +1058,33 @@ static void airoha_qdma_cleanup_tx_queue + e->dma_addr = 0; + e->skb = NULL; + list_add_tail(&e->list, &q->tx_list); ++ ++ /* Reset DMA descriptor */ ++ WRITE_ONCE(desc->ctrl, 0); ++ WRITE_ONCE(desc->addr, 0); ++ WRITE_ONCE(desc->data, 0); ++ WRITE_ONCE(desc->msg0, 0); ++ WRITE_ONCE(desc->msg1, 0); ++ WRITE_ONCE(desc->msg2, 0); ++ + q->queued--; + } ++ ++ if (!list_empty(&q->tx_list)) { ++ struct airoha_queue_entry *e; ++ ++ e = list_first_entry(&q->tx_list, struct airoha_queue_entry, ++ list); ++ index = e - q->entry; ++ } ++ /* Set TX_DMA_IDX to TX_CPU_IDX to notify the hw the QDMA TX ring is ++ * empty. ++ */ ++ airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, ++ FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); ++ airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, ++ FIELD_PREP(TX_RING_DMA_IDX_MASK, index)); ++ + spin_unlock_bh(&q->lock); + } + diff --git a/target/linux/airoha/patches-6.12/146-v7.1-net-airoha-Wait-for-NPU-PPE-configuration-to-complet.patch b/target/linux/airoha/patches-6.12/146-v7.1-net-airoha-Wait-for-NPU-PPE-configuration-to-complet.patch new file mode 100644 index 00000000000..d7b4680b52a --- /dev/null +++ b/target/linux/airoha/patches-6.12/146-v7.1-net-airoha-Wait-for-NPU-PPE-configuration-to-complet.patch @@ -0,0 +1,63 @@ +From f3206328bb52c2787197d80d7cbd687946047d5f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 14 Apr 2026 16:08:52 +0200 +Subject: [PATCH] net: airoha: Wait for NPU PPE configuration to complete in + airoha_ppe_offload_setup() + +In order to properly enable flowtable hw offloading, poll +REG_PPE_FLOW_CFG register in airoha_ppe_offload_setup routine and +wait for NPU PPE configuration triggered by ppe_init callback to complete +before running airoha_ppe_hw_init(). + +Fixes: 00a7678310fe3 ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260414-airoha-wait-for-npu-config-offload-setup-v2-1-5a9bf6d43aee@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 28 ++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1354,6 +1354,29 @@ static struct airoha_npu *airoha_ppe_npu + return npu; + } + ++static int airoha_ppe_wait_for_npu_init(struct airoha_eth *eth) ++{ ++ int err; ++ u32 val; ++ ++ /* PPE_FLOW_CFG default register value is 0. Since we reset FE ++ * during the device probe we can just check the configured value ++ * is not 0 here. ++ */ ++ err = read_poll_timeout(airoha_fe_rr, val, val, USEC_PER_MSEC, ++ 100 * USEC_PER_MSEC, false, eth, ++ REG_PPE_PPE_FLOW_CFG(0)); ++ if (err) ++ return err; ++ ++ if (airoha_ppe_is_enabled(eth, 1)) ++ err = read_poll_timeout(airoha_fe_rr, val, val, USEC_PER_MSEC, ++ 100 * USEC_PER_MSEC, false, eth, ++ REG_PPE_PPE_FLOW_CFG(1)); ++ ++ return err; ++} ++ + static int airoha_ppe_offload_setup(struct airoha_eth *eth) + { + struct airoha_npu *npu = airoha_ppe_npu_get(eth); +@@ -1367,6 +1390,11 @@ static int airoha_ppe_offload_setup(stru + if (err) + goto error_npu_put; + ++ /* Wait for NPU PPE configuration to complete */ ++ err = airoha_ppe_wait_for_npu_init(eth); ++ if (err) ++ goto error_npu_put; ++ + ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); + if (ppe_num_stats_entries > 0) { + err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma, diff --git a/target/linux/airoha/patches-6.12/147-v7.1-net-airoha-Fix-PPE-cpu-port-configuration-for-GDM2-l.patch b/target/linux/airoha/patches-6.12/147-v7.1-net-airoha-Fix-PPE-cpu-port-configuration-for-GDM2-l.patch new file mode 100644 index 00000000000..39b0fdfd909 --- /dev/null +++ b/target/linux/airoha/patches-6.12/147-v7.1-net-airoha-Fix-PPE-cpu-port-configuration-for-GDM2-l.patch @@ -0,0 +1,91 @@ +From d647f2545219754603b2064de948425cdfd93fba Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 17 Apr 2026 17:24:41 +0200 +Subject: [PATCH] net: airoha: Fix PPE cpu port configuration for GDM2 loopback + path + +When QoS loopback is enabled for GDM3 or GDM4, incoming packets are +forwarded to GDM2. However, the PPE cpu port for GDM2 is not configured +in this path, causing traffic originating from GDM3/GDM4, which may +be set up as WAN ports backed by QDMA1, to be incorrectly directed +to QDMA0 instead. +Configure the PPE cpu port for GDM2 when QoS loopback is active on +GDM3 or GDM4 to ensure traffic is routed to the correct QDMA instance. + +Fixes: 9cd451d414f6 ("net: airoha: Add loopback support for GDM2") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260417-airoha-ppe-cpu-port-for-gdm2-loopback-v1-1-c7a9de0f6f57@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++++-- + drivers/net/ethernet/airoha/airoha_eth.h | 3 ++- + drivers/net/ethernet/airoha/airoha_ppe.c | 6 +++--- + 3 files changed, 11 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1755,7 +1755,7 @@ static int airoha_set_gdm2_loopback(stru + { + struct airoha_eth *eth = port->qdma->eth; + u32 val, pse_port, chan; +- int src_port; ++ int i, src_port; + + /* Forward the traffic to the proper GDM port */ + pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 +@@ -1797,6 +1797,9 @@ static int airoha_set_gdm2_loopback(stru + SP_CPORT_MASK(val), + __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + ++ for (i = 0; i < eth->soc->num_ppe; i++) ++ airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX); ++ + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { + u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); + +@@ -1835,7 +1838,8 @@ static int airoha_dev_init(struct net_de + } + + for (i = 0; i < eth->soc->num_ppe; i++) +- airoha_ppe_set_cpu_port(port, i); ++ airoha_ppe_set_cpu_port(port, i, ++ airoha_get_fe_port(port)); + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -653,7 +653,8 @@ int airoha_get_fe_port(struct airoha_gdm + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id); ++void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, ++ u8 fport); + bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); + void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, + u16 hash, bool rx_wlan); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -85,10 +85,9 @@ static u32 airoha_ppe_get_timestamp(stru + return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); + } + +-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id) ++void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport) + { + struct airoha_qdma *qdma = port->qdma; +- u8 fport = airoha_get_fe_port(port); + struct airoha_eth *eth = qdma->eth; + u8 qdma_id = qdma - ð->qdma[0]; + u32 fe_cpu_port; +@@ -182,7 +181,8 @@ static void airoha_ppe_hw_init(struct ai + if (!port) + continue; + +- airoha_ppe_set_cpu_port(port, i); ++ airoha_ppe_set_cpu_port(port, i, ++ airoha_get_fe_port(port)); + } + } + } diff --git a/target/linux/airoha/patches-6.12/148-v7.1-net-airoha-Fix-possible-TX-queue-stall-in-airoha_qdm.patch b/target/linux/airoha/patches-6.12/148-v7.1-net-airoha-Fix-possible-TX-queue-stall-in-airoha_qdm.patch new file mode 100644 index 00000000000..fa0d9064ef2 --- /dev/null +++ b/target/linux/airoha/patches-6.12/148-v7.1-net-airoha-Fix-possible-TX-queue-stall-in-airoha_qdm.patch @@ -0,0 +1,104 @@ +From b94769eb2f30e61e86cd8551c084c34134290d89 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 16 Apr 2026 12:30:12 +0200 +Subject: [PATCH] net: airoha: Fix possible TX queue stall in + airoha_qdma_tx_napi_poll() + +Since multiple net_device TX queues can share the same hw QDMA TX queue, +there is no guarantee we have inflight packets queued in hw belonging to a +net_device TX queue stopped in the xmit path because hw QDMA TX queue +can be full. In this corner case the net_device TX queue will never be +re-activated. In order to avoid any potential net_device TX queue stall, +we need to wake all the net_device TX queues feeding the same hw QDMA TX +queue in airoha_qdma_tx_napi_poll routine. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260416-airoha-txq-potential-stall-v2-1-42c732074540@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 37 ++++++++++++++++++++---- + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + 2 files changed, 33 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -843,6 +843,21 @@ static int airoha_qdma_init_rx(struct ai + return 0; + } + ++static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) ++{ ++ struct airoha_qdma *qdma = q->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ ++ if (port && port->qdma == qdma) ++ netif_tx_wake_all_queues(port->dev); ++ } ++ q->txq_stopped = false; ++} ++ + static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_tx_irq_queue *irq_q; +@@ -919,12 +934,21 @@ static int airoha_qdma_tx_napi_poll(stru + + txq = netdev_get_tx_queue(skb->dev, queue); + netdev_tx_completed_queue(txq, 1, skb->len); +- if (netif_tx_queue_stopped(txq) && +- q->ndesc - q->queued >= q->free_thr) +- netif_tx_wake_queue(txq); +- + dev_kfree_skb_any(skb); + } ++ ++ if (q->txq_stopped && q->ndesc - q->queued >= q->free_thr) { ++ /* Since multiple net_device TX queues can share the ++ * same hw QDMA TX queue, there is no guarantee we have ++ * inflight packets queued in hw belonging to a ++ * net_device TX queue stopped in the xmit path. ++ * In order to avoid any potential net_device TX queue ++ * stall, we need to wake all the net_device TX queues ++ * feeding the same hw QDMA TX queue. ++ */ ++ airoha_qdma_wake_netdev_txqs(q); ++ } ++ + unlock: + spin_unlock_bh(&q->lock); + } +@@ -2016,6 +2040,7 @@ static netdev_tx_t airoha_dev_xmit(struc + if (q->queued + nr_frags >= q->ndesc) { + /* not enough space in the queue */ + netif_tx_stop_queue(txq); ++ q->txq_stopped = true; + spin_unlock_bh(&q->lock); + return NETDEV_TX_BUSY; + } +@@ -2071,8 +2096,10 @@ static netdev_tx_t airoha_dev_xmit(struc + TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + +- if (q->ndesc - q->queued < q->free_thr) ++ if (q->ndesc - q->queued < q->free_thr) { + netif_tx_stop_queue(txq); ++ q->txq_stopped = true; ++ } + + spin_unlock_bh(&q->lock); + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -193,6 +193,7 @@ struct airoha_queue { + int ndesc; + int free_thr; + int buf_size; ++ bool txq_stopped; + + struct napi_struct napi; + struct page_pool *page_pool; diff --git a/target/linux/airoha/patches-6.12/150-v7.1-net-airoha-Add-size-check-for-TX-NAPIs-in-airoha_qdm.patch b/target/linux/airoha/patches-6.12/150-v7.1-net-airoha-Add-size-check-for-TX-NAPIs-in-airoha_qdm.patch new file mode 100644 index 00000000000..ea5adb1b1b7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/150-v7.1-net-airoha-Add-size-check-for-TX-NAPIs-in-airoha_qdm.patch @@ -0,0 +1,57 @@ +From 4b91cb65789b794bfc8d50554b8994f8e0f16309 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 20 Apr 2026 10:07:48 +0200 +Subject: [PATCH] net: airoha: Add size check for TX NAPIs in + airoha_qdma_cleanup() + +If airoha_qdma_init routine fails before airoha_qdma_tx_irq_init() runs +successfully for all TX NAPIs, airoha_qdma_cleanup() will +unconditionally runs netif_napi_del() on TX NAPIs, triggering a NULL +pointer dereference. Fix the issue relying on q_tx_irq size value to +check if the TX NAPIs is properly initialized in airoha_qdma_cleanup(). +Moreover, run netif_napi_add_tx() just if irq_q queue is properly +allocated. + +Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260420-airoha_qdma_init_rx_queue-fix-v2-2-d99347e5c18d@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1020,8 +1020,6 @@ static int airoha_qdma_tx_irq_init(struc + struct airoha_eth *eth = qdma->eth; + dma_addr_t dma_addr; + +- netif_napi_add_tx(eth->napi_dev, &irq_q->napi, +- airoha_qdma_tx_napi_poll); + irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), + &dma_addr, GFP_KERNEL); + if (!irq_q->q) +@@ -1031,6 +1029,9 @@ static int airoha_qdma_tx_irq_init(struc + irq_q->size = size; + irq_q->qdma = qdma; + ++ netif_napi_add_tx(eth->napi_dev, &irq_q->napi, ++ airoha_qdma_tx_napi_poll); ++ + airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, + FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); +@@ -1450,8 +1451,12 @@ static void airoha_qdma_cleanup(struct a + } + } + +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) ++ for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { ++ if (!qdma->q_tx_irq[i].size) ++ continue; ++ + netif_napi_del(&qdma->q_tx_irq[i].napi); ++ } + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { + if (!qdma->q_tx[i].ndesc) diff --git a/target/linux/airoha/patches-6.12/151-v7.1-net-airoha-fix-BQL-imbalance-in-TX-path.patch b/target/linux/airoha/patches-6.12/151-v7.1-net-airoha-fix-BQL-imbalance-in-TX-path.patch new file mode 100644 index 00000000000..81286079139 --- /dev/null +++ b/target/linux/airoha/patches-6.12/151-v7.1-net-airoha-fix-BQL-imbalance-in-TX-path.patch @@ -0,0 +1,59 @@ +From 2d9f5a118205da2683ffcec78b9347f1f01a820e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 21 Apr 2026 08:35:11 +0200 +Subject: [PATCH] net: airoha: fix BQL imbalance in TX path + +Fix a possible BQL imbalance in airoha_dev_xmit(), where inflight +packets are accounted only for the AIROHA_NUM_TX_RING netdev TX +queues. The queue index is computed as: + + qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx) + txq = netdev_get_tx_queue(dev, qid); + +However, airoha_qdma_tx_napi_poll() accounts completions across all +netdev TX queues (num_tx_queues), leading to inconsistent BQL +accounting. + +Also reset all netdev TX queues in the ndo_stop callback. + +Fixes: 1d304174106c ("net: airoha: Implement BQL support") +Fixes: c9f947769b77 ("net: airoha: Reset BQL stopping the netdevice") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260421-airoha-fix-bql-v1-1-f135afe4275b@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -929,10 +929,9 @@ static int airoha_qdma_tx_napi_poll(stru + q->queued--; + + if (skb) { +- u16 queue = skb_get_queue_mapping(skb); + struct netdev_queue *txq; + +- txq = netdev_get_tx_queue(skb->dev, queue); ++ txq = skb_get_tx_queue(skb->dev, skb); + netdev_tx_completed_queue(txq, 1, skb->len); + dev_kfree_skb_any(skb); + } +@@ -1744,7 +1743,7 @@ static int airoha_dev_stop(struct net_de + if (err) + return err; + +- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) ++ for (i = 0; i < dev->num_tx_queues; i++) + netdev_tx_reset_subqueue(dev, i); + + airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), +@@ -2039,7 +2038,7 @@ static netdev_tx_t airoha_dev_xmit(struc + + spin_lock_bh(&q->lock); + +- txq = netdev_get_tx_queue(dev, qid); ++ txq = skb_get_tx_queue(dev, skb); + nr_frags = 1 + skb_shinfo(skb)->nr_frags; + + if (q->queued + nr_frags >= q->ndesc) { diff --git a/target/linux/airoha/patches-6.12/152-v7.1-net-airoha-stop-net_device-TX-queue-before-updating-.patch b/target/linux/airoha/patches-6.12/152-v7.1-net-airoha-stop-net_device-TX-queue-before-updating-.patch new file mode 100644 index 00000000000..f8a031e0d40 --- /dev/null +++ b/target/linux/airoha/patches-6.12/152-v7.1-net-airoha-stop-net_device-TX-queue-before-updating-.patch @@ -0,0 +1,46 @@ +From 3854de7b38be742cf7558476956d12414cb274f2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 21 Apr 2026 08:43:07 +0200 +Subject: [PATCH] net: airoha: stop net_device TX queue before updating CPU + index + +Currently, airoha_eth driver updates the CPU index register prior of +verifying whether the number of free descriptors has fallen below the +threshold. +Move net_device TX queue length check before updating the TX CPU index +in order to update TX CPU index even if there are more packets to be +transmitted but the net_device TX queue is going to be stopped +accounting the inflight packets. + +Fixes: 1d304174106c ("net: airoha: Implement BQL support") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260421-airoha-xmit-stop-condition-v1-1-e670d6a48467@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2094,17 +2094,16 @@ static netdev_tx_t airoha_dev_xmit(struc + + skb_tx_timestamp(skb); + netdev_tx_sent_queue(txq, skb->len); ++ if (q->ndesc - q->queued < q->free_thr) { ++ netif_tx_stop_queue(txq); ++ q->txq_stopped = true; ++ } + + if (netif_xmit_stopped(txq) || !netdev_xmit_more()) + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), + TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + +- if (q->ndesc - q->queued < q->free_thr) { +- netif_tx_stop_queue(txq); +- q->txq_stopped = true; +- } +- + spin_unlock_bh(&q->lock); + + return NETDEV_TX_OK; diff --git a/target/linux/airoha/patches-6.12/153-v7.1-net-airoha-Do-not-wake-all-netdev-TX-queues-in-airoh.patch b/target/linux/airoha/patches-6.12/153-v7.1-net-airoha-Do-not-wake-all-netdev-TX-queues-in-airoh.patch new file mode 100644 index 00000000000..28a9e26e95b --- /dev/null +++ b/target/linux/airoha/patches-6.12/153-v7.1-net-airoha-Do-not-wake-all-netdev-TX-queues-in-airoh.patch @@ -0,0 +1,75 @@ +From e070aac63b42bf81f4dc565f9f841ff47e6c992f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 21 Apr 2026 10:53:33 +0200 +Subject: [PATCH] net: airoha: Do not wake all netdev TX queues in + airoha_qdma_wake_netdev_txqs() + +Do not wake every netdev TX queue across all ports sharing the QDMA +running netif_tx_wake_all_queues routine in airoha_qdma_wake_netdev_txqs() +but only the ones that are mapped the specific QDMA stopped hw TX queue. +This patch can potentially avoid waking already stopped netdev TX queues +that are mapped to a different QDMA hw TX queue. +Introduce airoha_qdma_get_txq utility routine. + +Fixes: b94769eb2f30 ("net: airoha: Fix possible TX queue stall in airoha_qdma_tx_napi_poll()") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260421-airoha-wake_netdev_txqs-optmization-v1-1-e0be95115d53@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 19 +++++++++++++++---- + drivers/net/ethernet/airoha/airoha_eth.h | 5 +++++ + 2 files changed, 20 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -847,13 +847,24 @@ static void airoha_qdma_wake_netdev_txqs + { + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; +- int i; ++ int i, qid = q - &qdma->q_tx[0]; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; ++ int j; + +- if (port && port->qdma == qdma) +- netif_tx_wake_all_queues(port->dev); ++ if (!port) ++ continue; ++ ++ if (port->qdma != qdma) ++ continue; ++ ++ for (j = 0; j < port->dev->num_tx_queues; j++) { ++ if (airoha_qdma_get_txq(qdma, j) != qid) ++ continue; ++ ++ netif_wake_subqueue(port->dev, j); ++ } + } + q->txq_stopped = false; + } +@@ -2001,7 +2012,7 @@ static netdev_tx_t airoha_dev_xmit(struc + u16 index; + u8 fport; + +- qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); ++ qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); + tag = airoha_get_dsa_tag(skb, dev); + + msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -631,6 +631,11 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + ++static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid) ++{ ++ return qid % ARRAY_SIZE(qdma->q_tx); ++} ++ + static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port) + { + /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. diff --git a/target/linux/airoha/patches-6.12/154-v7.1-net-airoha-Do-not-read-uninitialized-fragment-addres.patch b/target/linux/airoha/patches-6.12/154-v7.1-net-airoha-Do-not-read-uninitialized-fragment-addres.patch new file mode 100644 index 00000000000..0980e5d31c5 --- /dev/null +++ b/target/linux/airoha/patches-6.12/154-v7.1-net-airoha-Do-not-read-uninitialized-fragment-addres.patch @@ -0,0 +1,64 @@ +From bde34e84edc8b5571fbde7e941e175a4293ee1eb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 24 Apr 2026 11:00:28 +0200 +Subject: [PATCH] net: airoha: Do not read uninitialized fragment address in + airoha_dev_xmit() + +The transmit loop in airoha_dev_xmit() reads fragment address and length +during its final iteration, when the loop index equals +skb_shinfo(skb)->nr_frags, at which point the fragment data is +uninitialized. While these values are never consumed, the read itself is +unsafe and may trigger a page fault. Fix this by avoiding the fragment +read on the last iteration. +Additionally, move the skb pointer from the first to the last used packet +descriptor, so that airoha_qdma_tx_napi_poll() defers freeing the skb +until the final descriptor is processed. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260424-airoha-xmit-fix-read-frag-v1-1-fdc0a83c79e8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2007,8 +2007,8 @@ static netdev_tx_t airoha_dev_xmit(struc + struct netdev_queue *txq; + struct airoha_queue *q; + LIST_HEAD(tx_list); ++ int i = 0, qid; + void *data; +- int i, qid; + u16 index; + u8 fport; + +@@ -2067,7 +2067,7 @@ static netdev_tx_t airoha_dev_xmit(struc + list); + index = e - q->entry; + +- for (i = 0; i < nr_frags; i++) { ++ while (true) { + struct airoha_qdma_desc *desc = &q->desc[index]; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t addr; +@@ -2079,7 +2079,7 @@ static netdev_tx_t airoha_dev_xmit(struc + goto error_unmap; + + list_move_tail(&e->list, &tx_list); +- e->skb = i ? NULL : skb; ++ e->skb = i == nr_frags - 1 ? skb : NULL; + e->dma_addr = addr; + e->dma_len = len; + +@@ -2098,6 +2098,9 @@ static netdev_tx_t airoha_dev_xmit(struc + WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); + WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); + ++ if (++i == nr_frags) ++ break; ++ + data = skb_frag_address(frag); + len = skb_frag_size(frag); + } diff --git a/target/linux/airoha/patches-6.12/155-v7.2-net-airoha-Rename-get_src_port_id-callback-in-get_sp.patch b/target/linux/airoha/patches-6.12/155-v7.2-net-airoha-Rename-get_src_port_id-callback-in-get_sp.patch new file mode 100644 index 00000000000..425ee19eef9 --- /dev/null +++ b/target/linux/airoha/patches-6.12/155-v7.2-net-airoha-Rename-get_src_port_id-callback-in-get_sp.patch @@ -0,0 +1,75 @@ +From c06a2f2903f6fba0a3088ad05fc5cf61a66e5d89 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 28 Apr 2026 07:23:38 +0200 +Subject: [PATCH] net: airoha: Rename get_src_port_id callback in get_sport + +For code consistency, rename get_src_port_id callback in get_sport. +Please note this patch does not introduce any logical change and it is +just a cosmetic patch. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260428-airoha-get_src_port_id-callback-v1-1-3f765c91c1e8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 +++++----- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 2 files changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1823,7 +1823,7 @@ static int airoha_set_gdm2_loopback(stru + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); + airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); + +- src_port = eth->soc->ops.get_src_port_id(port, port->nbq); ++ src_port = eth->soc->ops.get_sport(port, port->nbq); + if (src_port < 0) + return src_port; + +@@ -3199,7 +3199,7 @@ static const char * const en7581_xsi_rst + "xfp-mac", + }; + +-static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq) ++static int airoha_en7581_get_sport(struct airoha_gdm_port *port, int nbq) + { + switch (port->id) { + case AIROHA_GDM3_IDX: +@@ -3252,7 +3252,7 @@ static const char * const an7583_xsi_rst + "xfp-mac", + }; + +-static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq) ++static int airoha_an7583_get_sport(struct airoha_gdm_port *port, int nbq) + { + switch (port->id) { + case AIROHA_GDM3_IDX: +@@ -3300,7 +3300,7 @@ static const struct airoha_eth_soc_data + .num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names), + .num_ppe = 2, + .ops = { +- .get_src_port_id = airoha_en7581_get_src_port_id, ++ .get_sport = airoha_en7581_get_sport, + .get_vip_port = airoha_en7581_get_vip_port, + }, + }; +@@ -3311,7 +3311,7 @@ static const struct airoha_eth_soc_data + .num_xsi_rsts = ARRAY_SIZE(an7583_xsi_rsts_names), + .num_ppe = 1, + .ops = { +- .get_src_port_id = airoha_an7583_get_src_port_id, ++ .get_sport = airoha_an7583_get_sport, + .get_vip_port = airoha_an7583_get_vip_port, + }, + }; +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -578,7 +578,7 @@ struct airoha_eth_soc_data { + int num_xsi_rsts; + int num_ppe; + struct { +- int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq); ++ int (*get_sport)(struct airoha_gdm_port *port, int nbq); + u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq); + } ops; + }; diff --git a/target/linux/airoha/patches-6.12/156-v7.2-net-airoha-Do-not-return-err-in-ndo_stop-callback.patch b/target/linux/airoha/patches-6.12/156-v7.2-net-airoha-Do-not-return-err-in-ndo_stop-callback.patch new file mode 100644 index 00000000000..a111990b22c --- /dev/null +++ b/target/linux/airoha/patches-6.12/156-v7.2-net-airoha-Do-not-return-err-in-ndo_stop-callback.patch @@ -0,0 +1,36 @@ +From 4ca01292ea2f2363660610a65ba0285d7c3309ed Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 28 Apr 2026 08:53:16 +0200 +Subject: [PATCH] net: airoha: Do not return err in ndo_stop() callback + +Always complete the airoha_dev_stop() routine regardless of the +airoha_set_vip_for_gdm_port() return value, since errors from +ndo_stop() are ignored by the networking stack and the interface is +always considered down after the call. + +Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260428-airoha-ndo-stop-not-err-v1-1-674506d29a91@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1747,13 +1747,10 @@ static int airoha_dev_stop(struct net_de + { + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; +- int i, err; ++ int i; + + netif_tx_disable(dev); +- err = airoha_set_vip_for_gdm_port(port, false); +- if (err) +- return err; +- ++ airoha_set_vip_for_gdm_port(port, false); + for (i = 0; i < dev->num_tx_queues; i++) + netdev_tx_reset_subqueue(dev, i); + diff --git a/target/linux/airoha/patches-6.12/157-v7.2-net-airoha-Move-entries-to-queue-head-in-case-of-DMA.patch b/target/linux/airoha/patches-6.12/157-v7.2-net-airoha-Move-entries-to-queue-head-in-case-of-DMA.patch new file mode 100644 index 00000000000..45d1a1932be --- /dev/null +++ b/target/linux/airoha/patches-6.12/157-v7.2-net-airoha-Move-entries-to-queue-head-in-case-of-DMA.patch @@ -0,0 +1,38 @@ +From 75df490c9e8457990c8b227650f6491218ce018b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 29 Apr 2026 14:02:31 +0200 +Subject: [PATCH] net: airoha: Move entries to queue head in case of DMA + mapping failure in airoha_dev_xmit() + +In order to respect the original descriptor order and avoid any +potential IOMMU fault or memory corruption, move pending queue entries +to the head of hw queue tx_list if the DMA mapping of current inflight +packet fails in airoha_dev_xmit routine. + +Fixes: 3f47e67dff1f7 ("net: airoha: Add the capability to consume out-of-order DMA tx descriptors") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260429-airoha-xmit-unmap-error-path-v2-1-32e43b7c6d25@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2120,14 +2120,12 @@ static netdev_tx_t airoha_dev_xmit(struc + return NETDEV_TX_OK; + + error_unmap: +- while (!list_empty(&tx_list)) { +- e = list_first_entry(&tx_list, struct airoha_queue_entry, +- list); ++ list_for_each_entry(e, &tx_list, list) { + dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); + e->dma_addr = 0; +- list_move_tail(&e->list, &q->tx_list); + } ++ list_splice(&tx_list, &q->tx_list); + + spin_unlock_bh(&q->lock); + error: diff --git a/target/linux/airoha/patches-6.12/158-v7.2-net-airoha-configure-QoS-channel-for-HW-accelerated-.patch b/target/linux/airoha/patches-6.12/158-v7.2-net-airoha-configure-QoS-channel-for-HW-accelerated-.patch new file mode 100644 index 00000000000..c9a6192110d --- /dev/null +++ b/target/linux/airoha/patches-6.12/158-v7.2-net-airoha-configure-QoS-channel-for-HW-accelerated-.patch @@ -0,0 +1,44 @@ +From 286efd34d1a1ef5d83f9441b5e59421a26738169 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 30 Apr 2026 10:47:38 +0200 +Subject: [PATCH] net: airoha: configure QoS channel for HW accelerated + flowtable traffic + +As done for the SW path, configure the QoS channel for HW accelerated +traffic according to the user port index when forwarding to a DSA port, +or rely on the GDM port identifier otherwise. This allows HTB shaping +to be applied to HW accelerated traffic. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260430-airoha-ppe-qos-channel-v1-1-5ef9221e85c1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -332,7 +332,7 @@ static int airoha_ppe_foe_entry_prepare( + info.wcid); + } else { + struct airoha_gdm_port *port = netdev_priv(dev); +- u8 pse_port; ++ u8 pse_port, channel; + + if (!airoha_is_valid_gdm_port(eth, port)) + return -EINVAL; +@@ -345,6 +345,14 @@ static int airoha_ppe_foe_entry_prepare( + * loopback + */ + ++ /* For traffic forwarded to DSA devices select QoS ++ * channel according to the DSA user port index, rely ++ * on port id otherwise. ++ */ ++ channel = dsa_port >= 0 ? dsa_port : port->id; ++ channel = channel % AIROHA_NUM_QOS_CHANNELS; ++ qdata |= FIELD_PREP(AIROHA_FOE_CHANNEL, channel); ++ + val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port) | + AIROHA_FOE_IB2_PSE_QOS; + /* For downlink traffic consume SRAM memory for hw diff --git a/target/linux/airoha/patches-6.12/159-v7.2-net-airoha-Introduce-airoha_fe_get-airoha_qdma_get-r.patch b/target/linux/airoha/patches-6.12/159-v7.2-net-airoha-Introduce-airoha_fe_get-airoha_qdma_get-r.patch new file mode 100644 index 00000000000..ad5dcc97b99 --- /dev/null +++ b/target/linux/airoha/patches-6.12/159-v7.2-net-airoha-Introduce-airoha_fe_get-airoha_qdma_get-r.patch @@ -0,0 +1,95 @@ +From 98e490930de3af9afa0bbb2d1d79d6d5b5513012 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 1 May 2026 09:49:11 +0200 +Subject: [PATCH] net: airoha: Introduce airoha_fe_get()/airoha_qdma_get() + register read helpers + +Add airoha_fe_get() and airoha_qdma_get() as utility routines for reading +a masked field from a specified register. +This is a non-functional refactor, no logical changes are introduced to +the existing codebase. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260501-airoha_fe_get-airoha_qdma_get-v3-1-126c6f647ccb@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 13 ++++--------- + drivers/net/ethernet/airoha/airoha_eth.h | 4 ++++ + drivers/net/ethernet/airoha/airoha_ppe.c | 5 ++--- + 3 files changed, 10 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -201,15 +201,13 @@ static void airoha_fe_vip_setup(struct a + static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth, + u32 port, u32 queue) + { +- u32 val; +- + airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, + PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK, + FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | + FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue)); +- val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL); + +- return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val); ++ return airoha_fe_get(eth, REG_FE_PSE_QUEUE_CFG_VAL, ++ PSE_CFG_OQ_RSV_MASK); + } + + static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, +@@ -227,9 +225,7 @@ static void airoha_fe_set_pse_queue_rsv_ + + static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth) + { +- u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); +- +- return FIELD_GET(PSE_ALLRSV_MASK, val); ++ return airoha_fe_get(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK); + } + + static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, +@@ -247,8 +243,7 @@ static int airoha_fe_set_pse_oq_rsv(stru + FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); + + /* modify hthd */ +- tmp = airoha_fe_rr(eth, PSE_FQ_CFG); +- fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp); ++ fq_limit = airoha_fe_get(eth, PSE_FQ_CFG, PSE_FQ_LIMIT_MASK); + tmp = fq_limit - all_rsv - 0x20; + airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, + PSE_SHARE_USED_HTHD_MASK, +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -619,6 +619,8 @@ u32 airoha_rmw(void __iomem *base, u32 o + airoha_rmw((eth)->fe_regs, (offset), 0, (val)) + #define airoha_fe_clear(eth, offset, val) \ + airoha_rmw((eth)->fe_regs, (offset), (val), 0) ++#define airoha_fe_get(eth, offset, mask) \ ++ FIELD_GET((mask), airoha_fe_rr((eth), (offset))) + + #define airoha_qdma_rr(qdma, offset) \ + airoha_rr((qdma)->regs, (offset)) +@@ -630,6 +632,8 @@ u32 airoha_rmw(void __iomem *base, u32 o + airoha_rmw((qdma)->regs, (offset), 0, (val)) + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) ++#define airoha_qdma_get(qdma, offset, mask) \ ++ FIELD_GET((mask), airoha_qdma_rr((qdma), (offset))) + + static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid) + { +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -80,9 +80,8 @@ bool airoha_ppe_is_enabled(struct airoha + + static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) + { +- u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS); +- +- return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); ++ return airoha_fe_get(ppe->eth, REG_FE_FOE_TS, ++ AIROHA_FOE_IB1_BIND_TIMESTAMP); + } + + void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport) diff --git a/target/linux/airoha/patches-6.12/160-v7.2-net-airoha-Reserve-RX-headroom-to-avoid-skb-realloca.patch b/target/linux/airoha/patches-6.12/160-v7.2-net-airoha-Reserve-RX-headroom-to-avoid-skb-realloca.patch new file mode 100644 index 00000000000..06d64438515 --- /dev/null +++ b/target/linux/airoha/patches-6.12/160-v7.2-net-airoha-Reserve-RX-headroom-to-avoid-skb-realloca.patch @@ -0,0 +1,73 @@ +From bbfb1983944f2eaa8ee192e0f7b59ecc0fda9981 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 13 May 2026 17:03:59 +0200 +Subject: [PATCH] net: airoha: Reserve RX headroom to avoid skb reallocation + +Reserve NET_SKB_PAD + NET_IP_ALIGN bytes of headroom for received packets +to avoid skb head reallocation when pushing protocol headers into the skb. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260513-airoha-rx-headroom-v1-1-bd87798e422d@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 14 ++++++++------ + drivers/net/ethernet/airoha/airoha_eth.h | 2 ++ + 2 files changed, 10 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -543,9 +543,10 @@ static int airoha_qdma_fill_rx_queue(str + q->queued++; + nframes++; + ++ offset += AIROHA_RX_HEADROOM; + e->buf = page_address(page) + offset; + e->dma_addr = page_pool_get_dma_addr(page) + offset; +- e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); ++ e->dma_len = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size)); + + val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); + WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); +@@ -611,13 +612,12 @@ static int airoha_qdma_rx_process(struct + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + +- dma_sync_single_for_cpu(eth->dev, e->dma_addr, +- SKB_WITH_OVERHEAD(q->buf_size), dir); ++ dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, ++ dir); + + page = virt_to_head_page(e->buf); + len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); +- data_len = q->skb ? q->buf_size +- : SKB_WITH_OVERHEAD(q->buf_size); ++ data_len = q->skb ? AIROHA_RX_LEN(q->buf_size) : e->dma_len; + if (!len || data_len < len) + goto free_frag; + +@@ -627,10 +627,12 @@ static int airoha_qdma_rx_process(struct + + port = eth->ports[p]; + if (!q->skb) { /* first buffer */ +- q->skb = napi_build_skb(e->buf, q->buf_size); ++ q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM, ++ q->buf_size); + if (!q->skb) + goto free_frag; + ++ skb_reserve(q->skb, AIROHA_RX_HEADROOM); + __skb_put(q->skb, len); + skb_mark_for_recycle(q->skb); + q->skb->dev = port->dev; +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -32,6 +32,8 @@ + #define AIROHA_FE_MC_MAX_VLAN_TABLE 64 + #define AIROHA_FE_MC_MAX_VLAN_PORT 16 + #define AIROHA_NUM_TX_IRQ 2 ++#define AIROHA_RX_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN) ++#define AIROHA_RX_LEN(_n) ((_n) - AIROHA_RX_HEADROOM) + #define HW_DSCP_NUM 2048 + #define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) + #define TX_DSCP_NUM 1024 diff --git a/target/linux/airoha/patches-6.12/161-v7.2-net-airoha-Disable-GDM2-forwarding-before-configurin.patch b/target/linux/airoha/patches-6.12/161-v7.2-net-airoha-Disable-GDM2-forwarding-before-configurin.patch new file mode 100644 index 00000000000..a6c9e9eef3f --- /dev/null +++ b/target/linux/airoha/patches-6.12/161-v7.2-net-airoha-Disable-GDM2-forwarding-before-configurin.patch @@ -0,0 +1,45 @@ +From 985d4a55e64e43bd86eeb896b81ceba453301989 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 20 May 2026 15:12:02 +0200 +Subject: [PATCH] net: airoha: Disable GDM2 forwarding before configuring GDM2 + loopback + +Hw design requires to disable GDM2 forwarding before configuring GDM2 +loopback in airoha_set_gdm2_loopback routine. + +Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") +Tested-by: Madhur Agrawal +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20260520-airoha-disable-gdm2-fwd-v1-1-1eeea5dffc2f@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1790,11 +1790,8 @@ static int airoha_set_gdm2_loopback(stru + u32 val, pse_port, chan; + int i, src_port; + +- /* Forward the traffic to the proper GDM port */ +- pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 +- : FE_PSE_PORT_GDM4; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), +- pse_port); ++ FE_PSE_PORT_DROP); + airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + GDM_STRIP_CRC_MASK); + +@@ -1812,6 +1809,11 @@ static int airoha_set_gdm2_loopback(stru + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); ++ /* Forward the traffic to the proper GDM port */ ++ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 ++ : FE_PSE_PORT_GDM4; ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ pse_port); + + /* Disable VIP and IFC for GDM2 */ + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); diff --git a/target/linux/airoha/patches-6.12/200-02-ASoC-airoha-Add-AFE-and-I2S-driver-for-Airoha-AN7581.patch b/target/linux/airoha/patches-6.12/200-02-ASoC-airoha-Add-AFE-and-I2S-driver-for-Airoha-AN7581.patch new file mode 100644 index 00000000000..9984b531e57 --- /dev/null +++ b/target/linux/airoha/patches-6.12/200-02-ASoC-airoha-Add-AFE-and-I2S-driver-for-Airoha-AN7581.patch @@ -0,0 +1,1312 @@ +From 131f599fd0464f8e685610d9e24dadd8fbb4ba76 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 31 Jul 2025 15:32:32 +0200 +Subject: [PATCH] ASoC: airoha: Add AFE and machine driver for Airoha AN7581 + +Add support for the Sound system present on Airoha AN7581 SoC. This is +based on the mediatek AFE drivers. + +Also add the machine driver to create an actual sound card for the AFE. + +Signed-off-by: Christian Marangi +--- + MAINTAINERS | 8 + + sound/soc/mediatek/Kconfig | 20 + + sound/soc/mediatek/Makefile | 1 + + sound/soc/mediatek/an7581/Makefile | 9 + + sound/soc/mediatek/an7581/an7581-afe-common.h | 39 ++ + sound/soc/mediatek/an7581/an7581-afe-pcm.c | 497 ++++++++++++++++++ + sound/soc/mediatek/an7581/an7581-dai-etdm.c | 407 ++++++++++++++ + sound/soc/mediatek/an7581/an7581-reg.h | 88 ++++ + sound/soc/mediatek/an7581/an7581-wm8960.c | 170 ++++++ + 9 files changed, 1239 insertions(+) + create mode 100644 sound/soc/mediatek/an7581/Makefile + create mode 100644 sound/soc/mediatek/an7581/an7581-afe-common.h + create mode 100644 sound/soc/mediatek/an7581/an7581-afe-pcm.c + create mode 100644 sound/soc/mediatek/an7581/an7581-dai-etdm.c + create mode 100644 sound/soc/mediatek/an7581/an7581-reg.h + create mode 100644 sound/soc/mediatek/an7581/an7581-wm8960.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -734,6 +734,14 @@ F: Documentation/devicetree/bindings/phy + F: drivers/phy/phy-airoha-pcie-regs.h + F: drivers/phy/phy-airoha-pcie.c + ++AIROHA SOUND DRIVER ++M: Christian Marangi ++L: linux-sound@vger.kernel.org ++S: Maintained ++F: Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml ++F: Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml ++F: sound/soc/mediatek/an7581/* ++ + AIROHA SPI SNFI DRIVER + M: Lorenzo Bianconi + M: Ray Liu +--- a/sound/soc/mediatek/Kconfig ++++ b/sound/soc/mediatek/Kconfig +@@ -3,6 +3,26 @@ config SND_SOC_MEDIATEK + tristate + select REGMAP_MMIO + ++config SND_SOC_AN7581 ++ tristate "ASoC support for Airoha AN7581 chip" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ select SND_SOC_MEDIATEK ++ help ++ This adds ASoC platform driver support for Airoha AN7581 chip ++ that can be used with other codecs. ++ Select Y if you have such device. ++ If unsure select "N". ++ ++config SND_SOC_AN7581_WM8960 ++ tristate "ASoc Audio driver for Airoha AN7581 with WM8960 codec" ++ depends on SND_SOC_AN7581 && I2C ++ select SND_SOC_WM8960 ++ help ++ This adds support for ASoC machine driver for Airoha AN7581 ++ boards with the WM8960 codecs. ++ Select Y if you have such device. ++ If unsure select "N". ++ + config SND_SOC_MT2701 + tristate "ASoC support for Mediatek MT2701 chip" + depends on ARCH_MEDIATEK +--- a/sound/soc/mediatek/Makefile ++++ b/sound/soc/mediatek/Makefile +@@ -1,5 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_SND_SOC_MEDIATEK) += common/ ++obj-$(CONFIG_SND_SOC_AN7581) += an7581/ + obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ + obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ + obj-$(CONFIG_SND_SOC_MT7986) += mt7986/ +--- /dev/null ++++ b/sound/soc/mediatek/an7581/Makefile +@@ -0,0 +1,9 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++# platform driver ++snd-soc-an7581-afe-y := \ ++ an7581-afe-pcm.o \ ++ an7581-dai-etdm.o ++ ++obj-$(CONFIG_SND_SOC_AN7581) += snd-soc-an7581-afe.o ++obj-$(CONFIG_SND_SOC_AN7581_WM8960) += an7581-wm8960.o +--- /dev/null ++++ b/sound/soc/mediatek/an7581/an7581-afe-common.h +@@ -0,0 +1,39 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * an7581-afe-common.h -- Airoha AN7581 audio driver definitions ++ */ ++ ++#ifndef _AN7581_AFE_COMMON_H_ ++#define _AN7581_AFE_COMMON_H_ ++ ++#include ++#include ++#include ++#include "../../mediatek/common/mtk-base-afe.h" ++ ++enum { ++ AN7581_MEMIF_DL1, ++ AN7581_MEMIF_UL1, ++ AN7581_MEMIF_NUM, ++ AN7581_DAI_ETDM = AN7581_MEMIF_NUM, ++ AN7581_DAI_NUM, ++}; ++ ++enum { ++ AN7581_IRQ_0, ++ AN7581_IRQ_1, ++ AN7581_IRQ_NUM, ++}; ++ ++struct an7581_afe_private { ++ /* dai */ ++ void *dai_priv[AN7581_DAI_NUM]; ++}; ++ ++unsigned int an7581_afe_rate_transform(struct device *dev, ++ unsigned int rate); ++ ++/* dai register */ ++int an7581_dai_etdm_register(struct mtk_base_afe *afe); ++ ++#endif +--- /dev/null ++++ b/sound/soc/mediatek/an7581/an7581-afe-pcm.c +@@ -0,0 +1,497 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Airoha ALSA SoC AFE platform driver for AN7581 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "an7581-afe-common.h" ++#include "an7581-reg.h" ++#include "../common/mtk-afe-platform-driver.h" ++#include "../common/mtk-afe-fe-dai.h" ++ ++enum { ++ ARH_AFE_RATE_8K = 0, ++ ARH_AFE_RATE_12K = 1, ++ ARH_AFE_RATE_16K = 2, ++ ARH_AFE_RATE_24K = 3, ++ ARH_AFE_RATE_32K = 4, ++ ARH_AFE_RATE_48K = 5, ++ ARH_AFE_RATE_96K = 6, ++ ARH_AFE_RATE_192K = 7, ++ ARH_AFE_RATE_384K = 8, ++ ARH_AFE_RATE_7K = 16, ++ ARH_AFE_RATE_11K = 17, ++ ARH_AFE_RATE_14K = 18, ++ ARH_AFE_RATE_22K = 19, ++ ARH_AFE_RATE_29K = 20, ++ ARH_AFE_RATE_44K = 21, ++ ARH_AFE_RATE_88K = 22, ++ ARH_AFE_RATE_176K = 23, ++ ARH_AFE_RATE_352K = 24, ++}; ++ ++unsigned int an7581_afe_rate_transform(struct device *dev, unsigned int rate) ++{ ++ switch (rate) { ++ case 7350: ++ return ARH_AFE_RATE_7K; ++ case 8000: ++ return ARH_AFE_RATE_8K; ++ case 11025: ++ return ARH_AFE_RATE_11K; ++ case 12000: ++ return ARH_AFE_RATE_12K; ++ case 14700: ++ return ARH_AFE_RATE_14K; ++ case 16000: ++ return ARH_AFE_RATE_16K; ++ case 22050: ++ return ARH_AFE_RATE_22K; ++ case 24000: ++ return ARH_AFE_RATE_24K; ++ case 29400: ++ return ARH_AFE_RATE_29K; ++ case 32000: ++ return ARH_AFE_RATE_32K; ++ case 44100: ++ return ARH_AFE_RATE_44K; ++ case 48000: ++ return ARH_AFE_RATE_48K; ++ case 88200: ++ return ARH_AFE_RATE_88K; ++ case 96000: ++ return ARH_AFE_RATE_96K; ++ case 176400: ++ return ARH_AFE_RATE_176K; ++ case 192000: ++ return ARH_AFE_RATE_192K; ++ case 352800: ++ return ARH_AFE_RATE_352K; ++ case 384000: ++ return ARH_AFE_RATE_384K; ++ default: ++ dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n", ++ __func__, rate, ARH_AFE_RATE_48K); ++ return ARH_AFE_RATE_48K; ++ } ++} ++ ++static const int an7581_memif_specified_irqs[AN7581_MEMIF_NUM] = { ++ [AN7581_MEMIF_DL1] = AN7581_IRQ_0, ++ [AN7581_MEMIF_UL1] = AN7581_IRQ_1, ++}; ++ ++static const struct snd_pcm_hardware an7581_afe_hardware = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_MMAP_VALID, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .period_bytes_min = 512, ++ .period_bytes_max = 128 * 1024, ++ .periods_min = 2, ++ .periods_max = 256, ++ .buffer_bytes_max = 256 * 1024, ++ .fifo_size = 0, ++}; ++ ++static int an7581_memif_fs(struct snd_pcm_substream *substream, ++ unsigned int rate) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); ++ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); ++ ++ return an7581_afe_rate_transform(afe->dev, rate); ++} ++ ++static int an7581_irq_fs(struct snd_pcm_substream *substream, ++ unsigned int rate) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); ++ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); ++ ++ return an7581_afe_rate_transform(afe->dev, rate); ++} ++ ++#define ARH_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static struct snd_soc_dai_driver an7581_memif_dai_driver[] = { ++ /* FE DAIs: memory intefaces to CPU */ ++ { ++ .name = "DL1", ++ .id = AN7581_MEMIF_DL1, ++ .playback = { ++ .stream_name = "DL1", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = ARH_PCM_FORMATS, ++ }, ++ .ops = &mtk_afe_fe_ops, ++ }, ++ { ++ .name = "UL1", ++ .id = AN7581_MEMIF_UL1, ++ .capture = { ++ .stream_name = "UL1", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = ARH_PCM_FORMATS, ++ }, ++ .ops = &mtk_afe_fe_ops, ++ }, ++}; ++ ++static const struct snd_soc_dapm_widget an7581_memif_widgets[] = { ++ /* DL */ ++ SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0), ++ SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0), ++ ++ /* UL */ ++ SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, NULL, 0), ++ SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, NULL, 0), ++}; ++ ++static const struct snd_soc_dapm_route an7581_memif_routes[] = { ++ {"I032", NULL, "DL1"}, ++ {"I033", NULL, "DL1"}, ++ {"UL1", NULL, "O018"}, ++ {"UL1", NULL, "O019"}, ++ {"O018", NULL, "I150"}, ++ {"O019", NULL, "I151"}, ++}; ++ ++static const struct snd_soc_component_driver an7581_afe_pcm_dai_component = { ++ .name = "an7581-afe-pcm-dai", ++}; ++ ++static const struct mtk_base_memif_data memif_data[AN7581_MEMIF_NUM] = { ++ [AN7581_MEMIF_DL1] = { ++ .name = "DL1", ++ .id = AN7581_MEMIF_DL1, ++ .reg_ofs_base = AFE_DL1_BASE, ++ .reg_ofs_cur = AFE_DL1_CUR, ++ .reg_ofs_end = AFE_DL1_END, ++ .fs_reg = -1, ++ .fs_shift = -1, ++ .fs_maskbit = -1, ++ .mono_reg = -1, ++ .mono_shift = -1, ++ .hd_reg = AFE_DL1_CON0, ++ .hd_shift = AFE_HD_SHIFT, ++ .enable_reg = AFE_DAC_CON0, ++ .enable_shift = AFE_DL1_ENABLE_SHIFT, ++ .msb_reg = -1, ++ .msb_shift = -1, ++ .agent_disable_reg = -1, ++ .agent_disable_shift = -1, ++ .pbuf_reg = AFE_DL1_CON0, ++ .pbuf_mask = AFE_PBUF_SIZE_MASK, ++ .pbuf_shift = AFE_PBUF_SIZE_SHIFT, ++ .minlen_reg = AFE_DL1_CON0, ++ .minlen_mask = AFE_MINLEN_MASK, ++ .minlen_shift = AFE_MINLEN_SHIFT, ++ }, ++ [AN7581_MEMIF_UL1] = { ++ .name = "UL1", ++ .id = AN7581_MEMIF_UL1, ++ .reg_ofs_base = AFE_UL1_BASE, ++ .reg_ofs_cur = AFE_UL1_CUR, ++ .reg_ofs_end = AFE_UL1_END, ++ .fs_reg = -1, ++ .fs_shift = -1, ++ .fs_maskbit = -1, ++ .mono_reg = -1, ++ .mono_shift = -1, ++ .hd_reg = AFE_UL1_CON0, ++ .hd_shift = AFE_HD_SHIFT, ++ .enable_reg = AFE_DAC_CON0, ++ .enable_shift = AFE_UL1_ENABLE_SHIFT, ++ .msb_reg = AFE_UL1_CON0, ++ .msb_shift = AFE_MSB_SHIFT, ++ .agent_disable_reg = -1, ++ .agent_disable_shift = -1, ++ }, ++}; ++ ++static const struct mtk_base_irq_data irq_data[AN7581_IRQ_NUM] = { ++ [AN7581_IRQ_0] = { ++ .id = AN7581_IRQ_0, ++ .irq_cnt_reg = AFE_IRQ_CNT, ++ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, ++ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, ++ .irq_en_reg = AFE_IRQ_CON0, ++ .irq_en_shift = AFE_IRQ_ON_SHIFT, ++ .irq_fs_reg = -1, ++ .irq_fs_shift = -1, ++ .irq_fs_maskbit = -1, ++ .irq_clr_reg = AFE_IRQ_CON0, ++ .irq_clr_shift = AFE_IRQ_CLR_SHIFT, ++ }, ++ [AN7581_IRQ_1] = { ++ .id = AN7581_IRQ_1, ++ .irq_cnt_reg = AFE_IRQ1_CNT, ++ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, ++ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, ++ .irq_en_reg = AFE_IRQ1_CON0, ++ .irq_en_shift = AFE_IRQ_ON_SHIFT, ++ .irq_fs_reg = -1, ++ .irq_fs_shift = -1, ++ .irq_fs_maskbit = -1, ++ .irq_clr_reg = AFE_IRQ1_CON0, ++ .irq_clr_shift = AFE_IRQ_CLR_SHIFT, ++ }, ++}; ++ ++static const struct regmap_config an7581_afe_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = AFE_MAX_REGISTER, ++ .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1), ++}; ++ ++static irqreturn_t an7581_afe_irq_handler(int irq_id, void *dev) ++{ ++ struct mtk_base_afe *afe = dev; ++ struct mtk_base_afe_irq *irq; ++ u32 status; ++ u32 reg; ++ int i; ++ ++ regmap_read(afe->regmap, AFE_IRQ_STS, &status); ++ ++ if (status & AFE_IRQ_STS_RECORD) ++ reg = AFE_IRQ1_CON0; ++ else ++ reg = AFE_IRQ_CON0; ++ ++ regmap_set_bits(afe->regmap, reg, BIT(2)); ++ regmap_clear_bits(afe->regmap, reg, BIT(2)); ++ ++ regmap_set_bits(afe->regmap, reg, BIT(3)); ++ regmap_clear_bits(afe->regmap, reg, BIT(3)); ++ ++ for (i = 0; i < AN7581_MEMIF_NUM; i++) { ++ struct mtk_base_afe_memif *memif = &afe->memif[i]; ++ ++ if (!memif->substream) ++ continue; ++ ++ if (memif->irq_usage < 0) ++ continue; ++ ++ irq = &afe->irqs[memif->irq_usage]; ++ ++ if (status & (1 << irq->irq_data->irq_clr_shift)) ++ snd_pcm_period_elapsed(memif->substream); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int an7581_afe_runtime_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int an7581_afe_runtime_resume(struct device *dev) ++{ ++ return 0; ++} ++ ++static int an7581_dai_memif_register(struct mtk_base_afe *afe) ++{ ++ struct mtk_base_afe_dai *dai; ++ ++ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); ++ if (!dai) ++ return -ENOMEM; ++ ++ list_add(&dai->list, &afe->sub_dais); ++ ++ dai->dai_drivers = an7581_memif_dai_driver; ++ dai->num_dai_drivers = ARRAY_SIZE(an7581_memif_dai_driver); ++ ++ dai->dapm_widgets = an7581_memif_widgets; ++ dai->num_dapm_widgets = ARRAY_SIZE(an7581_memif_widgets); ++ dai->dapm_routes = an7581_memif_routes; ++ dai->num_dapm_routes = ARRAY_SIZE(an7581_memif_routes); ++ ++ return 0; ++} ++ ++typedef int (*dai_register_cb)(struct mtk_base_afe *); ++static const dai_register_cb dai_register_cbs[] = { ++ an7581_dai_etdm_register, ++ an7581_dai_memif_register, ++}; ++ ++static int an7581_afe_pcm_dev_probe(struct platform_device *pdev) ++{ ++ struct an7581_afe_private *afe_priv; ++ struct reset_control *reset; ++ struct mtk_base_afe *afe; ++ struct device *dev; ++ int i, irq_id, ret; ++ ++ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); ++ if (!afe) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, afe); ++ ++ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), ++ GFP_KERNEL); ++ if (!afe->platform_priv) ++ return -ENOMEM; ++ ++ afe_priv = afe->platform_priv; ++ afe->dev = &pdev->dev; ++ dev = afe->dev; ++ ++ reset = devm_reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(reset)) ++ return PTR_ERR(reset); ++ ++ /* Global reset I2S */ ++ reset_control_assert(reset); ++ usleep_range(10, 20); ++ reset_control_deassert(reset); ++ ++ afe->base_addr = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(afe->base_addr)) ++ return PTR_ERR(afe->base_addr); ++ ++ ret = devm_pm_runtime_enable(dev); ++ if (ret) ++ return ret; ++ ++ pm_runtime_get_sync(&pdev->dev); ++ ++ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, ++ &an7581_afe_regmap_config); ++ ++ pm_runtime_put_sync(&pdev->dev); ++ if (IS_ERR(afe->regmap)) ++ return PTR_ERR(afe->regmap); ++ ++ mutex_init(&afe->irq_alloc_lock); ++ ++ /* irq initialize */ ++ afe->irqs_size = AN7581_IRQ_NUM; ++ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), ++ GFP_KERNEL); ++ if (!afe->irqs) ++ return -ENOMEM; ++ ++ for (i = 0; i < afe->irqs_size; i++) ++ afe->irqs[i].irq_data = &irq_data[i]; ++ ++ /* request irq */ ++ irq_id = platform_get_irq(pdev, 0); ++ if (irq_id < 0) ++ return irq_id; ++ ++ ret = devm_request_irq(dev, irq_id, an7581_afe_irq_handler, ++ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n"); ++ ++ /* init memif */ ++ afe->memif_size = AN7581_MEMIF_NUM; ++ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), ++ GFP_KERNEL); ++ if (!afe->memif) ++ return -ENOMEM; ++ ++ for (i = 0; i < afe->memif_size; i++) { ++ int sel_irq = an7581_memif_specified_irqs[i]; ++ ++ afe->memif[i].data = &memif_data[i]; ++ afe->memif[i].irq_usage = sel_irq; ++ afe->memif[i].const_irq = 1; ++ afe->irqs[sel_irq].irq_occupyed = true; ++ } ++ ++ /* init sub_dais */ ++ INIT_LIST_HEAD(&afe->sub_dais); ++ ++ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { ++ ret = dai_register_cbs[i](afe); ++ if (ret) ++ return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i); ++ } ++ ++ /* init dai_driver and component_driver */ ++ ret = mtk_afe_combine_sub_dai(afe); ++ if (ret) ++ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n"); ++ ++ afe->mtk_afe_hardware = &an7581_afe_hardware; ++ afe->memif_fs = an7581_memif_fs; ++ afe->irq_fs = an7581_irq_fs; ++ ++ afe->runtime_resume = an7581_afe_runtime_resume; ++ afe->runtime_suspend = an7581_afe_runtime_suspend; ++ ++ /* register component */ ++ ret = devm_snd_soc_register_component(&pdev->dev, ++ &mtk_afe_pcm_platform, ++ NULL, 0); ++ if (ret) ++ return dev_err_probe(dev, ret, "Cannot register AFE component\n"); ++ ++ ret = devm_snd_soc_register_component(afe->dev, ++ &an7581_afe_pcm_dai_component, ++ afe->dai_drivers, ++ afe->num_dai_drivers); ++ if (ret) ++ return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n"); ++ ++ return 0; ++} ++ ++static void an7581_afe_pcm_dev_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ if (!pm_runtime_status_suspended(&pdev->dev)) ++ an7581_afe_runtime_suspend(&pdev->dev); ++} ++ ++static const struct of_device_id an7581_afe_pcm_dt_match[] = { ++ { .compatible = "airoha,an7581-afe" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, an7581_afe_pcm_dt_match); ++ ++static const struct dev_pm_ops an7581_afe_pm_ops = { ++ RUNTIME_PM_OPS(an7581_afe_runtime_suspend, ++ an7581_afe_runtime_resume, NULL) ++}; ++ ++static struct platform_driver an7581_afe_pcm_driver = { ++ .driver = { ++ .name = "an7581-audio", ++ .of_match_table = an7581_afe_pcm_dt_match, ++ .pm = pm_ptr(&an7581_afe_pm_ops), ++ }, ++ .probe = an7581_afe_pcm_dev_probe, ++ .remove = an7581_afe_pcm_dev_remove, ++}; ++module_platform_driver(an7581_afe_pcm_driver); ++ ++MODULE_DESCRIPTION("Airoha SoC AFE platform driver for ALSA AN7581"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/soc/mediatek/an7581/an7581-dai-etdm.c +@@ -0,0 +1,407 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Airoha ALSA SoC Audio DAI eTDM Control ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "an7581-afe-common.h" ++#include "an7581-reg.h" ++ ++#define HOPPING_CLK 0 ++#define APLL_CLK 1 ++#define MTK_DAI_ETDM_FORMAT_I2S 0 ++#define MTK_DAI_ETDM_FORMAT_DSPA 4 ++#define MTK_DAI_ETDM_FORMAT_DSPB 5 ++ ++enum { ++ MTK_ETDM_RATE_8K = 0, ++ MTK_ETDM_RATE_12K = 1, ++ MTK_ETDM_RATE_16K = 2, ++ MTK_ETDM_RATE_24K = 3, ++ MTK_ETDM_RATE_32K = 4, ++ MTK_ETDM_RATE_48K = 5, ++ MTK_ETDM_RATE_96K = 6, ++ MTK_ETDM_RATE_192K = 7, ++ MTK_ETDM_RATE_384K = 8, ++ MTK_ETDM_RATE_7K = 16, ++ MTK_ETDM_RATE_11K = 17, ++ MTK_ETDM_RATE_14K = 18, ++ MTK_ETDM_RATE_22K = 19, ++ MTK_ETDM_RATE_29K = 20, ++ MTK_ETDM_RATE_44K = 21, ++ MTK_ETDM_RATE_88K = 22, ++ MTK_ETDM_RATE_176K = 23, ++ MTK_ETDM_RATE_352K = 24, ++}; ++ ++struct mtk_dai_etdm_priv { ++ bool bck_inv; ++ bool lrck_inv; ++ bool slave_mode; ++ unsigned int format; ++}; ++ ++static unsigned int an7581_etdm_rate_transform(struct device *dev, unsigned int rate) ++{ ++ switch (rate) { ++ case 7350: ++ return MTK_ETDM_RATE_7K; ++ case 8000: ++ return MTK_ETDM_RATE_8K; ++ case 11025: ++ return MTK_ETDM_RATE_11K; ++ case 12000: ++ return MTK_ETDM_RATE_12K; ++ case 14700: ++ return MTK_ETDM_RATE_14K; ++ case 16000: ++ return MTK_ETDM_RATE_16K; ++ case 22050: ++ return MTK_ETDM_RATE_22K; ++ case 24000: ++ return MTK_ETDM_RATE_24K; ++ case 29400: ++ return MTK_ETDM_RATE_29K; ++ case 32000: ++ return MTK_ETDM_RATE_32K; ++ case 44100: ++ return MTK_ETDM_RATE_44K; ++ case 48000: ++ return MTK_ETDM_RATE_48K; ++ case 88200: ++ return MTK_ETDM_RATE_88K; ++ case 96000: ++ return MTK_ETDM_RATE_96K; ++ case 176400: ++ return MTK_ETDM_RATE_176K; ++ case 192000: ++ return MTK_ETDM_RATE_192K; ++ case 352800: ++ return MTK_ETDM_RATE_352K; ++ case 384000: ++ return MTK_ETDM_RATE_384K; ++ default: ++ dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n", ++ __func__, rate, MTK_ETDM_RATE_48K); ++ return MTK_ETDM_RATE_48K; ++ } ++} ++ ++static int get_etdm_wlen(unsigned int bitwidth) ++{ ++ return bitwidth <= 16 ? 16 : 32; ++} ++ ++static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = { ++ /* DL */ ++ SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0), ++ SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0), ++ ++ /* UL */ ++ SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, NULL, 0), ++ SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, NULL, 0), ++}; ++ ++static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { ++ {"I150", NULL, "ETDM Capture"}, ++ {"I151", NULL, "ETDM Capture"}, ++ {"ETDM Playback", NULL, "O124"}, ++ {"ETDM Playback", NULL, "O125"}, ++ {"O124", NULL, "I032"}, ++ {"O125", NULL, "I033"}, ++}; ++ ++/* dai ops */ ++static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ return 0; ++} ++ ++static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++} ++ ++static unsigned int get_etdm_ch_fixup(unsigned int channels) ++{ ++ if (channels > 16) ++ return 24; ++ else if (channels > 8) ++ return 16; ++ else if (channels > 4) ++ return 8; ++ else if (channels > 2) ++ return 4; ++ else ++ return 2; ++} ++ ++static int mtk_dai_etdm_config(struct mtk_base_afe *afe, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai, ++ int stream) ++{ ++ struct an7581_afe_private *afe_priv = afe->platform_priv; ++ struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id]; ++ unsigned int rate = params_rate(params); ++ unsigned int etdm_rate = an7581_etdm_rate_transform(afe->dev, rate); ++ unsigned int channels = params_channels(params); ++ unsigned int bit_width = params_width(params); ++ unsigned int wlen = get_etdm_wlen(bit_width); ++ unsigned int val = 0; ++ unsigned int mask = 0; ++ ++ dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n", ++ __func__, stream, rate, bit_width); ++ ++ /* CON0 */ ++ mask |= ETDM_BIT_LEN; ++ val |= FIELD_PREP(ETDM_BIT_LEN, bit_width - 1); ++ mask |= ETDM_WRD_LEN; ++ val |= FIELD_PREP(ETDM_WRD_LEN, wlen - 1); ++ mask |= ETDM_FMT; ++ val |= FIELD_PREP(ETDM_FMT, etdm_data->format); ++ mask |= ETDM_CH_NUM; ++ val |= FIELD_PREP(ETDM_CH_NUM, get_etdm_ch_fixup(channels) - 1); ++ mask |= ETDM_MODE; ++ val |= ETDM_MODE; ++ ++ switch (stream) { ++ case SNDRV_PCM_STREAM_PLAYBACK: ++ /* set ETDM_OUT1_CON0 */ ++ regmap_update_bits(afe->regmap, ETDM_OUT1_CON0, mask, val); ++ ++ /* set ETDM_OUT1_CON1 */ ++ regmap_update_bits(afe->regmap, ETDM_OUT1_CON1, ++ EDTM_DIRECT_INPUT_MASTER_BCK | ++ EDTM_LRCK_AUTO_MODE | ++ EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF | ++ EDTM_INITIAL_POINT | EDTM_INITIAL_COUNT, ++ EDTM_DIRECT_INPUT_MASTER_BCK | ++ EDTM_LRCK_AUTO_MODE | ++ EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF | ++ FIELD_PREP(EDTM_INITIAL_POINT, 14) | ++ FIELD_PREP(EDTM_INITIAL_COUNT, 14)); ++ ++ /* set ETDM_OUT1_CON4 */ ++ regmap_update_bits(afe->regmap, ETDM_OUT1_CON4, OUT_SEL_FS, ++ FIELD_PREP(OUT_SEL_FS, etdm_rate)); ++ break; ++ case SNDRV_PCM_STREAM_CAPTURE: ++ /* set ETDM_IN1_CON0 */ ++ regmap_update_bits(afe->regmap, ETDM_IN1_CON0, mask, val); ++ regmap_set_bits(afe->regmap, ETDM_IN1_CON0, ETDM_SYNC); ++ ++ /* set ETDM_IN1_CON1 */ ++ regmap_update_bits(afe->regmap, ETDM_IN1_CON1, ++ EDTM_LRCK_AUTO_MODE | ++ EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF | ++ EDTM_INITIAL_POINT | EDTM_INITIAL_COUNT, ++ EDTM_LRCK_AUTO_MODE | ++ EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF | ++ FIELD_PREP(EDTM_INITIAL_POINT, 14) | ++ FIELD_PREP(EDTM_INITIAL_COUNT, 14)); ++ ++ /* set ETDM_IN1_CON3 */ ++ regmap_update_bits(afe->regmap, ETDM_IN1_CON3, IN_SEL_FS, ++ FIELD_PREP(IN_SEL_FS, etdm_rate)); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ unsigned int rate = params_rate(params); ++ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); ++ ++ regmap_update_bits(afe->regmap, ETDM_COWORK_CON0, ++ EDTM_IN1_SLAVE_SEL, FIELD_PREP(EDTM_IN1_SLAVE_SEL, 1)); ++ regmap_update_bits(afe->regmap, ETDM_COWORK_CON0, ++ EDTM_OUT1_SLAVE_SEL, FIELD_PREP(EDTM_OUT1_SLAVE_SEL, 1)); ++ regmap_update_bits(afe->regmap, ETDM_COWORK_CON1, ++ EDTM_IN1_SDATA0_SEL, FIELD_PREP(EDTM_IN1_SDATA0_SEL, 0)); ++ ++ switch (rate) { ++ case 8000: ++ case 12000: ++ case 16000: ++ case 24000: ++ case 32000: ++ case 48000: ++ case 96000: ++ case 192000: ++ mtk_dai_etdm_config(afe, params, dai, substream->stream); ++ return 0; ++ default: ++ dev_err(afe->dev, ++ "Sample rate %d invalid. Supported rates: 8/12/16/24/32/48/96/192 kHz\n", ++ rate); ++ return -EINVAL; ++ } ++} ++ ++static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); ++ ++ dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id); ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ regmap_set_bits(afe->regmap, ETDM_IN1_CON0, ETDM_EN); ++ regmap_set_bits(afe->regmap, ETDM_OUT1_CON0, ETDM_EN); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ regmap_clear_bits(afe->regmap, ETDM_IN1_CON0, ETDM_EN); ++ regmap_clear_bits(afe->regmap, ETDM_OUT1_CON0, ETDM_EN); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); ++ struct an7581_afe_private *afe_priv = afe->platform_priv; ++ struct mtk_dai_etdm_priv *etdm_data; ++ void *priv_data; ++ ++ switch (dai->id) { ++ case AN7581_DAI_ETDM: ++ break; ++ default: ++ dev_warn(afe->dev, "%s(), id %d not support\n", ++ __func__, dai->id); ++ return -EINVAL; ++ } ++ ++ priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv), ++ GFP_KERNEL); ++ if (!priv_data) ++ return -ENOMEM; ++ ++ afe_priv->dai_priv[dai->id] = priv_data; ++ etdm_data = afe_priv->dai_priv[dai->id]; ++ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ etdm_data->bck_inv = false; ++ etdm_data->lrck_inv = false; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ etdm_data->bck_inv = false; ++ etdm_data->lrck_inv = true; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ etdm_data->bck_inv = true; ++ etdm_data->lrck_inv = false; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ etdm_data->bck_inv = true; ++ etdm_data->lrck_inv = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBP_CFP: ++ etdm_data->slave_mode = true; ++ break; ++ case SND_SOC_DAIFMT_CBC_CFC: ++ etdm_data->slave_mode = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { ++ .startup = mtk_dai_etdm_startup, ++ .shutdown = mtk_dai_etdm_shutdown, ++ .hw_params = mtk_dai_etdm_hw_params, ++ .trigger = mtk_dai_etdm_trigger, ++ .set_fmt = mtk_dai_etdm_set_fmt, ++}; ++ ++/* dai driver */ ++#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ ++ SNDRV_PCM_FMTBIT_S24_LE |\ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { ++ { ++ .name = "ETDM", ++ .id = AN7581_DAI_ETDM, ++ .capture = { ++ .stream_name = "ETDM Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = MTK_ETDM_FORMATS, ++ }, ++ .playback = { ++ .stream_name = "ETDM Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = MTK_ETDM_FORMATS, ++ }, ++ .ops = &mtk_dai_etdm_ops, ++ .symmetric_rate = 1, ++ .symmetric_sample_bits = 1, ++ }, ++}; ++ ++int an7581_dai_etdm_register(struct mtk_base_afe *afe) ++{ ++ struct mtk_base_afe_dai *dai; ++ ++ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); ++ if (!dai) ++ return -ENOMEM; ++ ++ list_add(&dai->list, &afe->sub_dais); ++ ++ dai->dai_drivers = mtk_dai_etdm_driver; ++ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver); ++ ++ dai->dapm_widgets = mtk_dai_etdm_widgets; ++ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets); ++ dai->dapm_routes = mtk_dai_etdm_routes; ++ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes); ++ ++ return 0; ++} +--- /dev/null ++++ b/sound/soc/mediatek/an7581/an7581-reg.h +@@ -0,0 +1,88 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * an7581-reg.h -- Airoha AN7581 audio driver reg definition ++ */ ++ ++#ifndef _AN7581_REG_H_ ++#define _AN7581_REG_H_ ++ ++#define AFE_DAC_CON0 0x0 ++#define AFE_DL1_ENABLE_SHIFT 17 ++#define AFE_UL1_ENABLE_SHIFT 1 ++ ++#define ETDM_COWORK_CON0 0x4c ++#define EDTM_IN1_SLAVE_SEL GENMASK(27, 24) ++#define EDTM_OUT1_SLAVE_SEL GENMASK(11, 8) ++#define ETDM_COWORK_CON1 0x50 ++#define EDTM_IN1_SDATA0_SEL GENMASK(3, 0) ++#define ETDM_IN1_CON0 0x5c ++#define EDTM_LRCK_AUTO_MODE BIT(29) ++#define ETDM_CH_NUM GENMASK(27, 23) ++#define ETDM_WRD_LEN GENMASK(20, 16) ++#define ETDM_BIT_LEN GENMASK(15, 11) ++#define ETDM_FMT GENMASK(8, 6) ++#define ETDM_MODE BIT(5) ++#define ETDM_SYNC BIT(1) ++#define ETDM_EN BIT(0) ++#define ETDM_IN1_CON1 0x60 ++#define ETDM_IN1_CON2 0x64 ++#define IN_CLK_SRC GENMASK(12, 10) ++#define ETDM_IN1_CON3 0x68 ++#define IN_SEL_FS GENMASK(30, 26) ++#define ETDM_IN1_CON4 0x6c ++#define IN_RELATCH GENMASK(24, 20) ++#define IN_CLK_INV BIT(18) ++#define ETDM_IN1_CON5 0x70 ++#define ETDM_IN1_CON6 0x74 ++#define ETDM_OUT1_CON0 0x7c ++#define ETDM_OUT1_CON1 0x80 ++#define EDTM_DIRECT_INPUT_MASTER_BCK BIT(30) ++#define EDTM_CKEN_SEL BIT(12) ++#define EDTM_LRCK_AUTO_OFF BIT(10) ++#define EDTM_INITIAL_POINT GENMASK(9, 5) ++#define EDTM_INITIAL_COUNT GENMASK(4, 0) ++#define ETDM_OUT1_CON2 0x84 ++#define ETDM_OUT1_CON3 0x88 ++#define ETDM_OUT1_CON4 0x8c ++#define OUT_RELATCH GENMASK(28, 24) ++#define OUT_CLK_SRC GENMASK(8, 6) ++#define OUT_SEL_FS GENMASK(4, 0) ++#define ETDM_OUT1_CON5 0x90 ++#define ETDM_CLK_DIV BIT(12) ++#define OUT_CLK_INV BIT(9) ++#define ETDM_OUT1_CON6 0x94 ++#define ETDM_OUT1_CON7 0x98 ++ ++#define AFE_DL1_BASE 0xa8 ++#define AFE_DL1_END 0xb0 ++#define AFE_DL1_CUR 0xac ++#define AFE_DL1_CON0 0xb4 ++#define AFE_PBUF_SIZE_SHIFT 16 ++#define AFE_PBUF_SIZE_MASK GENMASK(1, 0) ++#define AFE_MINLEN_SHIFT 8 ++#define AFE_MINLEN_MASK GENMASK(3, 0) ++#define AFE_HD_SHIFT 5 ++ ++#define AFE_UL1_BASE 0xc4 ++#define AFE_UL1_END 0xc8 ++#define AFE_UL1_CUR 0xcc ++#define AFE_UL1_CON0 0xd0 ++#define AFE_MSB_SHIFT 6 ++ ++#define AFE_IRQ_CON0 0xe4 ++#define AFE_IRQ_ON_SHIFT 0 ++#define AFE_IRQ_CLR_SHIFT 1 ++#define AFE_IRQ_CNT 0xe8 ++#define AFE_IRQ_CNT_SHIFT 0 ++#define AFE_IRQ_CNT_MASK GENMASK(31, 0) ++ ++#define AFE_IRQ_STS 0xf8 ++#define AFE_IRQ_STS_PLAY BIT(1) ++#define AFE_IRQ_STS_RECORD BIT(0) ++ ++#define AFE_IRQ1_CON0 0x100 ++#define AFE_IRQ1_CNT 0x104 ++ ++#define AFE_MAX_REGISTER AFE_IRQ1_CON0 ++ ++#endif +--- /dev/null ++++ b/sound/soc/mediatek/an7581/an7581-wm8960.c +@@ -0,0 +1,170 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Airoha ALSA SoC I2S platform driver for AN7581 ++ * ++ */ ++ ++#include ++#include ++ ++#include "an7581-afe-common.h" ++ ++static const struct snd_soc_dapm_widget an7581_wm8960_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone", NULL), ++ SND_SOC_DAPM_MIC("AMIC", NULL), ++}; ++ ++static const struct snd_kcontrol_new an7581_wm8960_controls[] = { ++ SOC_DAPM_PIN_SWITCH("Headphone"), ++ SOC_DAPM_PIN_SWITCH("AMIC"), ++}; ++ ++SND_SOC_DAILINK_DEFS(playback, ++ DAILINK_COMP_ARRAY(COMP_CPU("DL1")), ++ DAILINK_COMP_ARRAY(COMP_DUMMY()), ++ DAILINK_COMP_ARRAY(COMP_EMPTY())); ++ ++SND_SOC_DAILINK_DEFS(capture, ++ DAILINK_COMP_ARRAY(COMP_CPU("UL1")), ++ DAILINK_COMP_ARRAY(COMP_DUMMY()), ++ DAILINK_COMP_ARRAY(COMP_EMPTY())); ++ ++SND_SOC_DAILINK_DEFS(codec, ++ DAILINK_COMP_ARRAY(COMP_CPU("ETDM")), ++ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), ++ DAILINK_COMP_ARRAY(COMP_EMPTY())); ++ ++static struct snd_soc_dai_link an7581_wm8960_dai_links[] = { ++ /* FE */ ++ { ++ .name = "wm8960-playback", ++ .stream_name = "wm8960-playback", ++ .trigger = {SND_SOC_DPCM_TRIGGER_POST, ++ SND_SOC_DPCM_TRIGGER_POST}, ++ .dynamic = 0, ++ .playback_only = 1, ++ SND_SOC_DAILINK_REG(playback), ++ }, ++ { ++ .name = "wm8960-capture", ++ .stream_name = "wm8960-capture", ++ .trigger = {SND_SOC_DPCM_TRIGGER_POST, ++ SND_SOC_DPCM_TRIGGER_POST}, ++ .dynamic = 0, ++ .capture_only = 1, ++ SND_SOC_DAILINK_REG(capture), ++ }, ++ /* BE */ ++ { ++ .name = "wm8960-codec", ++ .no_pcm = 1, ++ .dai_fmt = SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBC_CFC | ++ SND_SOC_DAIFMT_GATED, ++ SND_SOC_DAILINK_REG(codec), ++ }, ++}; ++ ++static struct snd_soc_card an7581_wm8960_card = { ++ .name = "an7581-wm8960", ++ .owner = THIS_MODULE, ++ .dai_link = an7581_wm8960_dai_links, ++ .num_links = ARRAY_SIZE(an7581_wm8960_dai_links), ++ .controls = an7581_wm8960_controls, ++ .num_controls = ARRAY_SIZE(an7581_wm8960_controls), ++ .dapm_widgets = an7581_wm8960_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(an7581_wm8960_widgets), ++}; ++ ++static int an7581_wm8960_machine_probe(struct platform_device *pdev) ++{ ++ struct device_node *platform_dai_node, *codec_dai_node; ++ struct snd_soc_card *card = &an7581_wm8960_card; ++ struct device_node *platform, *codec; ++ struct snd_soc_dai_link *dai_link; ++ int ret, i; ++ ++ card->dev = &pdev->dev; ++ ++ platform = of_get_child_by_name(pdev->dev.of_node, "platform"); ++ ++ if (platform) { ++ platform_dai_node = of_parse_phandle(platform, "sound-dai", 0); ++ of_node_put(platform); ++ ++ if (!platform_dai_node) { ++ dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n"); ++ return -EINVAL; ++ } ++ } else { ++ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); ++ return -EINVAL; ++ } ++ ++ for_each_card_prelinks(card, i, dai_link) { ++ if (dai_link->platforms->name) ++ continue; ++ dai_link->platforms->of_node = platform_dai_node; ++ } ++ ++ codec = of_get_child_by_name(pdev->dev.of_node, "codec"); ++ ++ if (codec) { ++ codec_dai_node = of_parse_phandle(codec, "sound-dai", 0); ++ of_node_put(codec); ++ ++ if (!codec_dai_node) { ++ of_node_put(platform_dai_node); ++ dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n"); ++ return -EINVAL; ++ } ++ } else { ++ of_node_put(platform_dai_node); ++ dev_err(&pdev->dev, "Property 'codec' missing or invalid\n"); ++ return -EINVAL; ++ } ++ ++ for_each_card_prelinks(card, i, dai_link) { ++ if (dai_link->codecs->name) ++ continue; ++ dai_link->codecs->of_node = codec_dai_node; ++ } ++ ++ ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret); ++ goto err_of_node_put; ++ } ++ ++ ret = devm_snd_soc_register_card(&pdev->dev, card); ++ if (ret) { ++ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__); ++ goto err_of_node_put; ++ } ++ ++ return 0; ++ ++err_of_node_put: ++ of_node_put(platform_dai_node); ++ of_node_put(codec_dai_node); ++ return ret; ++} ++ ++static const struct of_device_id an7581_wm8960_machine_dt_match[] = { ++ { .compatible = "airoha,an7581-wm8960-sound" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, an7581_wm8960_machine_dt_match); ++ ++static struct platform_driver an7581_wm8960_driver = { ++ .driver = { ++ .name = "an7581-wm8960", ++ .of_match_table = an7581_wm8960_machine_dt_match, ++ }, ++ .probe = an7581_wm8960_machine_probe, ++}; ++module_platform_driver(an7581_wm8960_driver); ++ ++MODULE_DESCRIPTION("Airoha SoC I2S platform driver for ALSA AN7581"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/201-crypto-Add-Mediatek-EIP-93-crypto-engine-support.patch b/target/linux/airoha/patches-6.12/201-crypto-Add-Mediatek-EIP-93-crypto-engine-support.patch new file mode 100644 index 00000000000..17923afd6ea --- /dev/null +++ b/target/linux/airoha/patches-6.12/201-crypto-Add-Mediatek-EIP-93-crypto-engine-support.patch @@ -0,0 +1,4206 @@ +From 45260ebcfb17a47bbad37055024dad50f2fcc5d0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Oct 2021 17:13:29 +0800 +Subject: [RFC PATCH v2 3/3] crypto: Add Mediatek EIP-93 crypto engine support + +Add support for the Mediatek EIP-93 crypto engine used on MT7621 and new +Airoha SoC. + +EIP-93 IP supports AES/DES/3DES ciphers in ECB/CBC and CTR modes as well as +authenc(HMAC(x), cipher(y)) using HMAC MD5, SHA1, SHA224 and SHA256. + +EIP-93 provide regs to signal support for specific chipers and the +driver dynamically register only the supported one by the chip. + +Signed-off-by: Richard van Schagen +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +--- +Changes v2: +- Rename all variables from mtk to eip93 +- Move to inside-secure directory +- Check DMA map errors +- Use guard API for spinlock +- Minor improvements to code + + drivers/crypto/Kconfig | 1 + + drivers/crypto/Makefile | 1 + + drivers/crypto/inside-secure/eip93/Kconfig | 20 + + drivers/crypto/inside-secure/eip93/Makefile | 5 + + .../crypto/inside-secure/eip93/eip93-aead.c | 702 ++++++++++++++ + .../crypto/inside-secure/eip93/eip93-aead.h | 38 + + .../crypto/inside-secure/eip93/eip93-aes.h | 16 + + .../crypto/inside-secure/eip93/eip93-cipher.c | 407 ++++++++ + .../crypto/inside-secure/eip93/eip93-cipher.h | 60 ++ + .../crypto/inside-secure/eip93/eip93-common.c | 824 ++++++++++++++++ + .../crypto/inside-secure/eip93/eip93-common.h | 25 + + .../crypto/inside-secure/eip93/eip93-des.h | 16 + + .../crypto/inside-secure/eip93/eip93-hash.c | 909 ++++++++++++++++++ + .../crypto/inside-secure/eip93/eip93-hash.h | 72 ++ + .../crypto/inside-secure/eip93/eip93-main.c | 502 ++++++++++ + .../crypto/inside-secure/eip93/eip93-main.h | 155 +++ + .../crypto/inside-secure/eip93/eip93-regs.h | 335 +++++++ + 17 files changed, 4088 insertions(+) + create mode 100644 drivers/crypto/inside-secure/eip93/Kconfig + create mode 100644 drivers/crypto/inside-secure/eip93/Makefile + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-aead.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-aead.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-aes.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-cipher.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-cipher.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-common.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-common.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-des.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-hash.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-hash.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-main.c + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-main.h + create mode 100644 drivers/crypto/inside-secure/eip93/eip93-regs.h + +--- a/drivers/crypto/Kconfig ++++ b/drivers/crypto/Kconfig +@@ -851,5 +851,6 @@ config CRYPTO_DEV_SA2UL + + source "drivers/crypto/aspeed/Kconfig" + source "drivers/crypto/starfive/Kconfig" ++source "drivers/crypto/inside-secure/eip93/Kconfig" + + endif # CRYPTO_HW +--- a/drivers/crypto/Makefile ++++ b/drivers/crypto/Makefile +@@ -52,3 +52,4 @@ obj-y += hisilicon/ + obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ + obj-y += intel/ + obj-y += starfive/ ++obj-y += inside-secure/eip93/ +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/Kconfig +@@ -0,0 +1,20 @@ ++# SPDX-License-Identifier: GPL-2.0 ++config CRYPTO_DEV_EIP93 ++ tristate "Support for EIP93 crypto HW accelerators" ++ depends on SOC_MT7621 || ARCH_AIROHA ||COMPILE_TEST ++ select CRYPTO_LIB_AES ++ select CRYPTO_LIB_DES ++ select CRYPTO_SKCIPHER ++ select CRYPTO_AEAD ++ select CRYPTO_AUTHENC ++ select CRYPTO_MD5 ++ select CRYPTO_SHA1 ++ select CRYPTO_SHA256 ++ help ++ EIP93 have various crypto HW accelerators. Select this if ++ you want to use the EIP93 modules for any of the crypto algorithms. ++ ++ If the IP supports it, this provide offload for AES - ECB, CBC and ++ CTR crypto. Also provide DES and 3DES ECB and CBC. ++ ++ Also provide AEAD authenc(hmac(x), cipher(y)) for supported algo. +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/Makefile +@@ -0,0 +1,5 @@ ++obj-$(CONFIG_CRYPTO_DEV_EIP93) += crypto-hw-eip93.o ++ ++crypto-hw-eip93-y += eip93-main.o eip93-common.o ++crypto-hw-eip93-y += eip93-cipher.o eip93-aead.o ++crypto-hw-eip93-y += eip93-hash.o +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-aead.c +@@ -0,0 +1,702 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "eip93-aead.h" ++#include "eip93-cipher.h" ++#include "eip93-common.h" ++#include "eip93-regs.h" ++ ++void eip93_aead_handle_result(struct crypto_async_request *async, int err) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); ++ struct eip93_device *mtk = ctx->mtk; ++ struct aead_request *req = aead_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ eip93_unmap_dma(mtk, rctx, req->src, req->dst); ++ eip93_handle_result(mtk, rctx, req->iv); ++ ++ aead_request_complete(req, err); ++} ++ ++static int eip93_aead_send_req(struct crypto_async_request *async) ++{ ++ struct aead_request *req = aead_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ int err; ++ ++ err = check_valid_request(rctx); ++ if (err) { ++ aead_request_complete(req, err); ++ return err; ++ } ++ ++ return eip93_send_req(async, req->iv, rctx); ++} ++ ++/* Crypto aead API functions */ ++static int eip93_aead_cra_init(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, alg.aead.base); ++ ++ crypto_aead_set_reqsize(__crypto_aead_cast(tfm), ++ sizeof(struct eip93_cipher_reqctx)); ++ ++ ctx->mtk = tmpl->mtk; ++ ctx->flags = tmpl->flags; ++ ctx->type = tmpl->type; ++ ctx->set_assoc = true; ++ ++ ctx->sa_record = kzalloc(sizeof(*ctx->sa_record), GFP_KERNEL); ++ if (!ctx->sa_record) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void eip93_aead_cra_exit(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ dma_unmap_single(ctx->mtk->dev, ctx->sa_record_base, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ kfree(ctx->sa_record); ++} ++ ++static int eip93_aead_setkey(struct crypto_aead *ctfm, const u8 *key, ++ unsigned int len) ++{ ++ struct crypto_tfm *tfm = crypto_aead_tfm(ctfm); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct crypto_authenc_keys keys; ++ struct crypto_aes_ctx aes; ++ struct sa_record *sa_record = ctx->sa_record; ++ u32 nonce = 0; ++ int ret; ++ ++ if (crypto_authenc_extractkeys(&keys, key, len)) ++ return -EINVAL; ++ ++ if (IS_RFC3686(ctx->flags)) { ++ if (keys.enckeylen < CTR_RFC3686_NONCE_SIZE) ++ return -EINVAL; ++ ++ keys.enckeylen -= CTR_RFC3686_NONCE_SIZE; ++ memcpy(&nonce, keys.enckey + keys.enckeylen, ++ CTR_RFC3686_NONCE_SIZE); ++ } ++ ++ switch ((ctx->flags & EIP93_ALG_MASK)) { ++ case EIP93_ALG_DES: ++ ret = verify_aead_des_key(ctfm, keys.enckey, keys.enckeylen); ++ break; ++ case EIP93_ALG_3DES: ++ if (keys.enckeylen != DES3_EDE_KEY_SIZE) ++ return -EINVAL; ++ ++ ret = verify_aead_des3_key(ctfm, keys.enckey, keys.enckeylen); ++ break; ++ case EIP93_ALG_AES: ++ ret = aes_expandkey(&aes, keys.enckey, keys.enckeylen); ++ } ++ if (ret) ++ return ret; ++ ++ ctx->blksize = crypto_aead_blocksize(ctfm); ++ /* Encryption key */ ++ eip93_set_sa_record(sa_record, keys.enckeylen, ctx->flags); ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_OPCODE; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_OPCODE, ++ EIP93_SA_CMD_OPCODE_BASIC_OUT_ENC_HASH); ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_DIGEST_LENGTH; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_DIGEST_LENGTH, ++ ctx->authsize / sizeof(u32)); ++ ++ memcpy(sa_record->sa_key, keys.enckey, keys.enckeylen); ++ ctx->sa_nonce = nonce; ++ sa_record->sa_nonce = nonce; ++ ++ /* authentication key */ ++ ret = eip93_authenc_setkey(ctfm, sa_record, keys.authkey, ++ keys.authkeylen); ++ ++ ctx->set_assoc = true; ++ ++ return ret; ++} ++ ++static int eip93_aead_setauthsize(struct crypto_aead *ctfm, ++ unsigned int authsize) ++{ ++ struct crypto_tfm *tfm = crypto_aead_tfm(ctfm); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ ctx->authsize = authsize; ++ ctx->sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_DIGEST_LENGTH; ++ ctx->sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_DIGEST_LENGTH, ++ ctx->authsize / sizeof(u32)); ++ ++ return 0; ++} ++ ++static void eip93_aead_setassoc(struct eip93_crypto_ctx *ctx, ++ struct aead_request *req) ++{ ++ struct sa_record *sa_record = ctx->sa_record; ++ ++ sa_record->sa_cmd1_word &= ~EIP93_SA_CMD_HASH_CRYPT_OFFSET; ++ sa_record->sa_cmd1_word |= FIELD_PREP(EIP93_SA_CMD_HASH_CRYPT_OFFSET, ++ req->assoclen / sizeof(u32)); ++ ++ ctx->assoclen = req->assoclen; ++} ++ ++static int eip93_aead_crypt(struct aead_request *req) ++{ ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ struct crypto_async_request *async = &req->base; ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ int ret; ++ ++ ctx->sa_record_base = dma_map_single(ctx->mtk->dev, ctx->sa_record, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ ret = dma_mapping_error(ctx->mtk->dev, ctx->sa_record_base); ++ if (ret) ++ return ret; ++ ++ rctx->textsize = req->cryptlen; ++ rctx->blksize = ctx->blksize; ++ rctx->assoclen = req->assoclen; ++ rctx->authsize = ctx->authsize; ++ rctx->sg_src = req->src; ++ rctx->sg_dst = req->dst; ++ rctx->ivsize = crypto_aead_ivsize(aead); ++ rctx->desc_flags = EIP93_DESC_AEAD; ++ rctx->sa_record_base = ctx->sa_record_base; ++ ++ if (IS_DECRYPT(rctx->flags)) ++ rctx->textsize -= rctx->authsize; ++ ++ return eip93_aead_send_req(async); ++} ++ ++static int eip93_aead_encrypt(struct aead_request *req) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ rctx->flags = ctx->flags; ++ rctx->flags |= EIP93_ENCRYPT; ++ if (ctx->set_assoc) { ++ eip93_aead_setassoc(ctx, req); ++ ctx->set_assoc = false; ++ } ++ ++ if (req->assoclen != ctx->assoclen) { ++ dev_err(ctx->mtk->dev, "Request AAD length error\n"); ++ return -EINVAL; ++ } ++ ++ return eip93_aead_crypt(req); ++} ++ ++static int eip93_aead_decrypt(struct aead_request *req) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct eip93_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ ctx->sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIRECTION_IN; ++ ctx->sa_record->sa_cmd1_word &= ~(EIP93_SA_CMD_COPY_PAD | ++ EIP93_SA_CMD_COPY_DIGEST); ++ ++ rctx->flags = ctx->flags; ++ rctx->flags |= EIP93_DECRYPT; ++ if (ctx->set_assoc) { ++ eip93_aead_setassoc(ctx, req); ++ ctx->set_assoc = false; ++ } ++ ++ if (req->assoclen != ctx->assoclen) { ++ dev_err(ctx->mtk->dev, "Request AAD length error\n"); ++ return -EINVAL; ++ } ++ ++ return eip93_aead_crypt(req); ++} ++ ++/* Available authenc algorithms in this module */ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93), cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_cbc_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = AES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),cbc(aes))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),cbc(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | ++ EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),rfc3686(ctr(aes)))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),rfc3686(ctr(aes-eip93)))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_cbc_des = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),cbc(des))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),cbc(des-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_md5_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = MD5_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(md5),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(md5-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha1_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA1_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha1),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(sha1-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha224_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA224_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha224),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(sha224-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_authenc_hmac_sha256_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_AEAD, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256 | EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.aead = { ++ .setkey = eip93_aead_setkey, ++ .encrypt = eip93_aead_encrypt, ++ .decrypt = eip93_aead_decrypt, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .setauthsize = eip93_aead_setauthsize, ++ .maxauthsize = SHA256_DIGEST_SIZE, ++ .base = { ++ .cra_name = "authenc(hmac(sha256),cbc(des3_ede))", ++ .cra_driver_name = ++ "authenc(hmac(sha256-eip93),cbc(des3_ede-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0x0, ++ .cra_init = eip93_aead_cra_init, ++ .cra_exit = eip93_aead_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-aead.h +@@ -0,0 +1,38 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ * Christian Marangi ++ * Christian Marangi ++#include ++#include ++#include ++ ++#include "eip93-cipher.h" ++#include "eip93-common.h" ++#include "eip93-regs.h" ++ ++void eip93_skcipher_handle_result(struct crypto_async_request *async, int err) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); ++ struct eip93_device *mtk = ctx->mtk; ++ struct skcipher_request *req = skcipher_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ ++ eip93_unmap_dma(mtk, rctx, req->src, req->dst); ++ eip93_handle_result(mtk, rctx, req->iv); ++ ++ skcipher_request_complete(req, err); ++} ++ ++static int eip93_skcipher_send_req(struct crypto_async_request *async) ++{ ++ struct skcipher_request *req = skcipher_request_cast(async); ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ int err; ++ ++ err = check_valid_request(rctx); ++ ++ if (err) { ++ skcipher_request_complete(req, err); ++ return err; ++ } ++ ++ return eip93_send_req(async, req->iv, rctx); ++} ++ ++/* Crypto skcipher API functions */ ++static int eip93_skcipher_cra_init(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, alg.skcipher.base); ++ ++ crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm), ++ sizeof(struct eip93_cipher_reqctx)); ++ ++ memset(ctx, 0, sizeof(*ctx)); ++ ++ ctx->mtk = tmpl->mtk; ++ ctx->type = tmpl->type; ++ ++ ctx->sa_record = kzalloc(sizeof(*ctx->sa_record), GFP_KERNEL); ++ if (!ctx->sa_record) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void eip93_skcipher_cra_exit(struct crypto_tfm *tfm) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ dma_unmap_single(ctx->mtk->dev, ctx->sa_record_base, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ kfree(ctx->sa_record); ++} ++ ++static int eip93_skcipher_setkey(struct crypto_skcipher *ctfm, const u8 *key, ++ unsigned int len) ++{ ++ struct crypto_tfm *tfm = crypto_skcipher_tfm(ctfm); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, ++ alg.skcipher.base); ++ struct sa_record *sa_record = ctx->sa_record; ++ unsigned int keylen = len; ++ u32 flags = tmpl->flags; ++ u32 nonce = 0; ++ int ret; ++ ++ if (!key || !keylen) ++ return -EINVAL; ++ ++ if (IS_RFC3686(flags)) { ++ if (len < CTR_RFC3686_NONCE_SIZE) ++ return -EINVAL; ++ ++ keylen = len - CTR_RFC3686_NONCE_SIZE; ++ memcpy(&nonce, key + keylen, CTR_RFC3686_NONCE_SIZE); ++ } ++ ++ if (flags & EIP93_ALG_DES) { ++ ctx->blksize = DES_BLOCK_SIZE; ++ ret = verify_skcipher_des_key(ctfm, key); ++ } ++ if (flags & EIP93_ALG_3DES) { ++ ctx->blksize = DES3_EDE_BLOCK_SIZE; ++ ret = verify_skcipher_des3_key(ctfm, key); ++ } ++ ++ if (flags & EIP93_ALG_AES) { ++ struct crypto_aes_ctx aes; ++ ++ ctx->blksize = AES_BLOCK_SIZE; ++ ret = aes_expandkey(&aes, key, keylen); ++ } ++ if (ret) ++ return ret; ++ ++ eip93_set_sa_record(sa_record, keylen, flags); ++ ++ memcpy(sa_record->sa_key, key, keylen); ++ ctx->sa_nonce = nonce; ++ sa_record->sa_nonce = nonce; ++ ++ return 0; ++} ++ ++static int eip93_skcipher_crypt(struct skcipher_request *req) ++{ ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ struct crypto_async_request *async = &req->base; ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); ++ int ret; ++ ++ if (!req->cryptlen) ++ return 0; ++ ++ /* ++ * ECB and CBC algorithms require message lengths to be ++ * multiples of block size. ++ */ ++ if (IS_ECB(rctx->flags) || IS_CBC(rctx->flags)) ++ if (!IS_ALIGNED(req->cryptlen, ++ crypto_skcipher_blocksize(skcipher))) ++ return -EINVAL; ++ ++ ctx->sa_record_base = dma_map_single(ctx->mtk->dev, ctx->sa_record, ++ sizeof(*ctx->sa_record), DMA_TO_DEVICE); ++ ret = dma_mapping_error(ctx->mtk->dev, ctx->sa_record_base); ++ if (ret) ++ return ret; ++ ++ rctx->assoclen = 0; ++ rctx->textsize = req->cryptlen; ++ rctx->authsize = 0; ++ rctx->sg_src = req->src; ++ rctx->sg_dst = req->dst; ++ rctx->ivsize = crypto_skcipher_ivsize(skcipher); ++ rctx->blksize = ctx->blksize; ++ rctx->desc_flags = EIP93_DESC_SKCIPHER; ++ rctx->sa_record_base = ctx->sa_record_base; ++ ++ return eip93_skcipher_send_req(async); ++} ++ ++static int eip93_skcipher_encrypt(struct skcipher_request *req) ++{ ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ struct eip93_alg_template *tmpl = container_of(req->base.tfm->__crt_alg, ++ struct eip93_alg_template, alg.skcipher.base); ++ ++ rctx->flags = tmpl->flags; ++ rctx->flags |= EIP93_ENCRYPT; ++ ++ return eip93_skcipher_crypt(req); ++} ++ ++static int eip93_skcipher_decrypt(struct skcipher_request *req) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(req->base.tfm); ++ struct eip93_cipher_reqctx *rctx = skcipher_request_ctx(req); ++ struct eip93_alg_template *tmpl = container_of(req->base.tfm->__crt_alg, ++ struct eip93_alg_template, alg.skcipher.base); ++ ++ ctx->sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIRECTION_IN; ++ ++ rctx->flags = tmpl->flags; ++ rctx->flags |= EIP93_DECRYPT; ++ ++ return eip93_skcipher_crypt(req); ++} ++ ++/* Available algorithms in this module */ ++struct eip93_alg_template eip93_alg_ecb_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_ECB | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = 0, ++ .base = { ++ .cra_name = "ecb(aes)", ++ .cra_driver_name = "ecb(aes-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_cbc_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CBC | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .base = { ++ .cra_name = "cbc(aes)", ++ .cra_driver_name = "cbc(aes-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_ctr_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CTR | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, ++ .base = { ++ .cra_name = "ctr(aes)", ++ .cra_driver_name = "ctr(aes-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_rfc3686_aes = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CTR | EIP93_MODE_RFC3686 | EIP93_ALG_AES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = AES_MIN_KEY_SIZE + CTR_RFC3686_NONCE_SIZE, ++ .max_keysize = AES_MAX_KEY_SIZE + CTR_RFC3686_NONCE_SIZE, ++ .ivsize = CTR_RFC3686_IV_SIZE, ++ .base = { ++ .cra_name = "rfc3686(ctr(aes))", ++ .cra_driver_name = "rfc3686(ctr(aes-eip93))", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = 1, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0xf, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_ecb_des = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_ECB | EIP93_ALG_DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .ivsize = 0, ++ .base = { ++ .cra_name = "ecb(des)", ++ .cra_driver_name = "ebc(des-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_cbc_des = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CBC | EIP93_ALG_DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES_KEY_SIZE, ++ .max_keysize = DES_KEY_SIZE, ++ .ivsize = DES_BLOCK_SIZE, ++ .base = { ++ .cra_name = "cbc(des)", ++ .cra_driver_name = "cbc(des-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_ecb_des3_ede = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_ECB | EIP93_ALG_3DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES3_EDE_KEY_SIZE, ++ .max_keysize = DES3_EDE_KEY_SIZE, ++ .ivsize = 0, ++ .base = { ++ .cra_name = "ecb(des3_ede)", ++ .cra_driver_name = "ecb(des3_ede-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_cbc_des3_ede = { ++ .type = EIP93_ALG_TYPE_SKCIPHER, ++ .flags = EIP93_MODE_CBC | EIP93_ALG_3DES, ++ .alg.skcipher = { ++ .setkey = eip93_skcipher_setkey, ++ .encrypt = eip93_skcipher_encrypt, ++ .decrypt = eip93_skcipher_decrypt, ++ .min_keysize = DES3_EDE_KEY_SIZE, ++ .max_keysize = DES3_EDE_KEY_SIZE, ++ .ivsize = DES3_EDE_BLOCK_SIZE, ++ .base = { ++ .cra_name = "cbc(des3_ede)", ++ .cra_driver_name = "cbc(des3_ede-eip93)", ++ .cra_priority = EIP93_CRA_PRIORITY, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY, ++ .cra_blocksize = DES3_EDE_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_crypto_ctx), ++ .cra_alignmask = 0, ++ .cra_init = eip93_skcipher_cra_init, ++ .cra_exit = eip93_skcipher_cra_exit, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++}; +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-cipher.h +@@ -0,0 +1,60 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-cipher.h" ++#include "eip93-hash.h" ++#include "eip93-common.h" ++#include "eip93-main.h" ++#include "eip93-regs.h" ++ ++int eip93_parse_ctrl_stat_err(struct eip93_device *mtk, int err) ++{ ++ u32 ext_err; ++ ++ if (!err) ++ return 0; ++ ++ switch (err & ~EIP93_PE_CTRL_PE_EXT_ERR_CODE) { ++ case EIP93_PE_CTRL_PE_AUTH_ERR: ++ case EIP93_PE_CTRL_PE_PAD_ERR: ++ return -EBADMSG; ++ /* let software handle anti-replay errors */ ++ case EIP93_PE_CTRL_PE_SEQNUM_ERR: ++ return 0; ++ case EIP93_PE_CTRL_PE_EXT_ERR: ++ break; ++ default: ++ dev_err(mtk->dev, "Unhandled error 0x%08x\n", err); ++ return -EINVAL; ++ } ++ ++ /* Parse additional ext errors */ ++ ext_err = FIELD_GET(EIP93_PE_CTRL_PE_EXT_ERR_CODE, err); ++ switch (ext_err) { ++ case EIP93_PE_CTRL_PE_EXT_ERR_BUS: ++ case EIP93_PE_CTRL_PE_EXT_ERR_PROCESSING: ++ return -EIO; ++ case EIP93_PE_CTRL_PE_EXT_ERR_DESC_OWNER: ++ return -EACCES; ++ case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_CRYPTO_OP: ++ case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_CRYPTO_ALGO: ++ case EIP93_PE_CTRL_PE_EXT_ERR_SPI: ++ return -EINVAL; ++ case EIP93_PE_CTRL_PE_EXT_ERR_ZERO_LENGTH: ++ case EIP93_PE_CTRL_PE_EXT_ERR_INVALID_PK_LENGTH: ++ case EIP93_PE_CTRL_PE_EXT_ERR_BLOCK_SIZE_ERR: ++ return -EBADMSG; ++ default: ++ dev_err(mtk->dev, "Unhandled ext error 0x%08x\n", ext_err); ++ return -EINVAL; ++ } ++} ++ ++static void *eip93_ring_next_wptr(struct eip93_device *mtk, ++ struct eip93_desc_ring *ring) ++{ ++ void *ptr = ring->write; ++ ++ if ((ring->write == ring->read - ring->offset) || ++ (ring->read == ring->base && ring->write == ring->base_end)) ++ return ERR_PTR(-ENOMEM); ++ ++ if (ring->write == ring->base_end) ++ ring->write = ring->base; ++ else ++ ring->write += ring->offset; ++ ++ return ptr; ++} ++ ++static void *eip93_ring_next_rptr(struct eip93_device *mtk, ++ struct eip93_desc_ring *ring) ++{ ++ void *ptr = ring->read; ++ ++ if (ring->write == ring->read) ++ return ERR_PTR(-ENOENT); ++ ++ if (ring->read == ring->base_end) ++ ring->read = ring->base; ++ else ++ ring->read += ring->offset; ++ ++ return ptr; ++} ++ ++int eip93_put_descriptor(struct eip93_device *mtk, ++ struct eip93_descriptor *desc) ++{ ++ struct eip93_descriptor *cdesc; ++ struct eip93_descriptor *rdesc; ++ ++ guard(spinlock_irqsave)(&mtk->ring->write_lock); ++ ++ rdesc = eip93_ring_next_wptr(mtk, &mtk->ring->rdr); ++ ++ if (IS_ERR(rdesc)) ++ return -ENOENT; ++ ++ cdesc = eip93_ring_next_wptr(mtk, &mtk->ring->cdr); ++ if (IS_ERR(cdesc)) ++ return -ENOENT; ++ ++ memset(rdesc, 0, sizeof(struct eip93_descriptor)); ++ ++ memcpy(cdesc, desc, sizeof(struct eip93_descriptor)); ++ ++ atomic_dec(&mtk->ring->free); ++ ++ return 0; ++} ++ ++void *eip93_get_descriptor(struct eip93_device *mtk) ++{ ++ struct eip93_descriptor *cdesc; ++ void *ptr; ++ ++ guard(spinlock_irqsave)(&mtk->ring->read_lock); ++ ++ cdesc = eip93_ring_next_rptr(mtk, &mtk->ring->cdr); ++ if (IS_ERR(cdesc)) ++ return ERR_PTR(-ENOENT); ++ ++ memset(cdesc, 0, sizeof(struct eip93_descriptor)); ++ ++ ptr = eip93_ring_next_rptr(mtk, &mtk->ring->rdr); ++ if (IS_ERR(ptr)) ++ return ERR_PTR(-ENOENT); ++ ++ atomic_inc(&mtk->ring->free); ++ ++ return ptr; ++} ++ ++static void eip93_free_sg_copy(const int len, struct scatterlist **sg) ++{ ++ if (!*sg || !len) ++ return; ++ ++ free_pages((unsigned long)sg_virt(*sg), get_order(len)); ++ kfree(*sg); ++ *sg = NULL; ++} ++ ++static int eip93_make_sg_copy(struct scatterlist *src, struct scatterlist **dst, ++ const u32 len, const bool copy) ++{ ++ void *pages; ++ ++ *dst = kmalloc(sizeof(**dst), GFP_KERNEL); ++ if (!*dst) ++ return -ENOMEM; ++ ++ pages = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, ++ get_order(len)); ++ if (!pages) { ++ kfree(*dst); ++ *dst = NULL; ++ return -ENOMEM; ++ } ++ ++ sg_init_table(*dst, 1); ++ sg_set_buf(*dst, pages, len); ++ ++ /* copy only as requested */ ++ if (copy) ++ sg_copy_to_buffer(src, sg_nents(src), pages, len); ++ ++ return 0; ++} ++ ++static bool eip93_is_sg_aligned(struct scatterlist *sg, u32 len, ++ const int blksize) ++{ ++ int nents; ++ ++ for (nents = 0; sg; sg = sg_next(sg), ++nents) { ++ if (!IS_ALIGNED(sg->offset, 4)) ++ return false; ++ ++ if (len <= sg->length) { ++ if (!IS_ALIGNED(len, blksize)) ++ return false; ++ ++ return true; ++ } ++ ++ if (!IS_ALIGNED(sg->length, blksize)) ++ return false; ++ ++ len -= sg->length; ++ } ++ return false; ++} ++ ++int check_valid_request(struct eip93_cipher_reqctx *rctx) ++{ ++ struct scatterlist *src = rctx->sg_src; ++ struct scatterlist *dst = rctx->sg_dst; ++ u32 src_nents, dst_nents; ++ u32 textsize = rctx->textsize; ++ u32 authsize = rctx->authsize; ++ u32 blksize = rctx->blksize; ++ u32 totlen_src = rctx->assoclen + rctx->textsize; ++ u32 totlen_dst = rctx->assoclen + rctx->textsize; ++ u32 copy_len; ++ bool src_align, dst_align; ++ int err = -EINVAL; ++ ++ if (!IS_CTR(rctx->flags)) { ++ if (!IS_ALIGNED(textsize, blksize)) ++ return err; ++ } ++ ++ if (authsize) { ++ if (IS_ENCRYPT(rctx->flags)) ++ totlen_dst += authsize; ++ else ++ totlen_src += authsize; ++ } ++ ++ src_nents = sg_nents_for_len(src, totlen_src); ++ dst_nents = sg_nents_for_len(dst, totlen_dst); ++ ++ if (src == dst) { ++ src_nents = max(src_nents, dst_nents); ++ dst_nents = src_nents; ++ if (unlikely((totlen_src || totlen_dst) && src_nents <= 0)) ++ return err; ++ ++ } else { ++ if (unlikely(totlen_src && src_nents <= 0)) ++ return err; ++ ++ if (unlikely(totlen_dst && dst_nents <= 0)) ++ return err; ++ } ++ ++ if (authsize) { ++ if (dst_nents == 1 && src_nents == 1) { ++ src_align = eip93_is_sg_aligned(src, totlen_src, blksize); ++ if (src == dst) ++ dst_align = src_align; ++ else ++ dst_align = eip93_is_sg_aligned(dst, totlen_dst, blksize); ++ } else { ++ src_align = false; ++ dst_align = false; ++ } ++ } else { ++ src_align = eip93_is_sg_aligned(src, totlen_src, blksize); ++ if (src == dst) ++ dst_align = src_align; ++ else ++ dst_align = eip93_is_sg_aligned(dst, totlen_dst, blksize); ++ } ++ ++ copy_len = max(totlen_src, totlen_dst); ++ if (!src_align) { ++ err = eip93_make_sg_copy(src, &rctx->sg_src, copy_len, true); ++ if (err) ++ return err; ++ } ++ ++ if (!dst_align) { ++ err = eip93_make_sg_copy(dst, &rctx->sg_dst, copy_len, false); ++ if (err) ++ return err; ++ } ++ ++ rctx->src_nents = sg_nents_for_len(rctx->sg_src, totlen_src); ++ rctx->dst_nents = sg_nents_for_len(rctx->sg_dst, totlen_dst); ++ ++ return 0; ++} ++ ++/* ++ * Set sa_record function: ++ * Even sa_record is set to "0", keep " = 0" for readability. ++ */ ++void eip93_set_sa_record(struct sa_record *sa_record, const unsigned int keylen, ++ const u32 flags) ++{ ++ /* Reset cmd word */ ++ sa_record->sa_cmd0_word = 0; ++ sa_record->sa_cmd1_word = 0; ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_IV_FROM_STATE; ++ if (!IS_ECB(flags)) ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_SAVE_IV; ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_OP_BASIC; ++ ++ switch ((flags & EIP93_ALG_MASK)) { ++ case EIP93_ALG_AES: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_AES; ++ sa_record->sa_cmd1_word |= FIELD_PREP(EIP93_SA_CMD_AES_KEY_LENGTH, ++ keylen >> 3); ++ break; ++ case EIP93_ALG_3DES: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_3DES; ++ break; ++ case EIP93_ALG_DES: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_DES; ++ break; ++ default: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_CIPHER_NULL; ++ } ++ ++ switch ((flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA256; ++ break; ++ case EIP93_HASH_SHA224: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA224; ++ break; ++ case EIP93_HASH_SHA1: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_SHA1; ++ break; ++ case EIP93_HASH_MD5: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_MD5; ++ break; ++ default: ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_NULL; ++ } ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_PAD_ZERO; ++ ++ switch ((flags & EIP93_MODE_MASK)) { ++ case EIP93_MODE_CBC: ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_CBC; ++ break; ++ case EIP93_MODE_CTR: ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_CTR; ++ break; ++ case EIP93_MODE_ECB: ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_CHIPER_MODE_ECB; ++ break; ++ } ++ ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_DIGEST_3WORD; ++ if (IS_HASH(flags)) { ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_PAD; ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_DIGEST; ++ } ++ ++ if (IS_HMAC(flags)) { ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_HMAC; ++ sa_record->sa_cmd1_word |= EIP93_SA_CMD_COPY_HEADER; ++ } ++ ++ sa_record->sa_spi = 0x0; ++ sa_record->sa_seqmum_mask[0] = 0xFFFFFFFF; ++ sa_record->sa_seqmum_mask[1] = 0x0; ++} ++ ++/* ++ * Poor mans Scatter/gather function: ++ * Create a Descriptor for every segment to avoid copying buffers. ++ * For performance better to wait for hardware to perform multiple DMA ++ */ ++static int eip93_scatter_combine(struct eip93_device *mtk, ++ struct eip93_cipher_reqctx *rctx, ++ u32 datalen, u32 split, int offsetin) ++{ ++ struct eip93_descriptor *cdesc = rctx->cdesc; ++ struct scatterlist *sgsrc = rctx->sg_src; ++ struct scatterlist *sgdst = rctx->sg_dst; ++ unsigned int remainin = sg_dma_len(sgsrc); ++ unsigned int remainout = sg_dma_len(sgdst); ++ dma_addr_t saddr = sg_dma_address(sgsrc); ++ dma_addr_t daddr = sg_dma_address(sgdst); ++ dma_addr_t state_addr; ++ u32 src_addr, dst_addr, len, n; ++ bool nextin = false; ++ bool nextout = false; ++ int offsetout = 0; ++ int ndesc_cdr = 0, err; ++ ++ if (IS_ECB(rctx->flags)) ++ rctx->sa_state_base = 0; ++ ++ if (split < datalen) { ++ state_addr = rctx->sa_state_ctr_base; ++ n = split; ++ } else { ++ state_addr = rctx->sa_state_base; ++ n = datalen; ++ } ++ ++ do { ++ if (nextin) { ++ sgsrc = sg_next(sgsrc); ++ remainin = sg_dma_len(sgsrc); ++ if (remainin == 0) ++ continue; ++ ++ saddr = sg_dma_address(sgsrc); ++ offsetin = 0; ++ nextin = false; ++ } ++ ++ if (nextout) { ++ sgdst = sg_next(sgdst); ++ remainout = sg_dma_len(sgdst); ++ if (remainout == 0) ++ continue; ++ ++ daddr = sg_dma_address(sgdst); ++ offsetout = 0; ++ nextout = false; ++ } ++ src_addr = saddr + offsetin; ++ dst_addr = daddr + offsetout; ++ ++ if (remainin == remainout) { ++ len = remainin; ++ if (len > n) { ++ len = n; ++ remainin -= n; ++ remainout -= n; ++ offsetin += n; ++ offsetout += n; ++ } else { ++ nextin = true; ++ nextout = true; ++ } ++ } else if (remainin < remainout) { ++ len = remainin; ++ if (len > n) { ++ len = n; ++ remainin -= n; ++ remainout -= n; ++ offsetin += n; ++ offsetout += n; ++ } else { ++ offsetout += len; ++ remainout -= len; ++ nextin = true; ++ } ++ } else { ++ len = remainout; ++ if (len > n) { ++ len = n; ++ remainin -= n; ++ remainout -= n; ++ offsetin += n; ++ offsetout += n; ++ } else { ++ offsetin += len; ++ remainin -= len; ++ nextout = true; ++ } ++ } ++ n -= len; ++ ++ cdesc->src_addr = src_addr; ++ cdesc->dst_addr = dst_addr; ++ cdesc->state_addr = state_addr; ++ cdesc->pe_length_word = FIELD_PREP(EIP93_PE_LENGTH_HOST_PE_READY, ++ EIP93_PE_LENGTH_HOST_READY); ++ cdesc->pe_length_word |= FIELD_PREP(EIP93_PE_LENGTH_LENGTH, len); ++ ++ if (n == 0) { ++ n = datalen - split; ++ split = datalen; ++ state_addr = rctx->sa_state_base; ++ } ++ ++ if (n == 0) ++ cdesc->user_id |= FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, ++ EIP93_DESC_LAST); ++ ++ /* ++ * Loop - Delay - No need to rollback ++ * Maybe refine by slowing down at EIP93_RING_BUSY ++ */ ++again: ++ err = eip93_put_descriptor(mtk, cdesc); ++ if (err) { ++ usleep_range(EIP93_RING_BUSY_DELAY, ++ EIP93_RING_BUSY_DELAY * 2); ++ goto again; ++ } ++ /* Writing new descriptor count starts DMA action */ ++ writel(1, mtk->base + EIP93_REG_PE_CD_COUNT); ++ ++ ndesc_cdr++; ++ } while (n); ++ ++ return -EINPROGRESS; ++} ++ ++int eip93_send_req(struct crypto_async_request *async, ++ const u8 *reqiv, struct eip93_cipher_reqctx *rctx) ++{ ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(async->tfm); ++ struct eip93_device *mtk = ctx->mtk; ++ struct scatterlist *src = rctx->sg_src; ++ struct scatterlist *dst = rctx->sg_dst; ++ struct sa_state *sa_state; ++ struct eip93_descriptor cdesc; ++ u32 flags = rctx->flags; ++ int offsetin = 0, err; ++ u32 datalen = rctx->assoclen + rctx->textsize; ++ u32 split = datalen; ++ u32 start, end, ctr, blocks; ++ u32 iv[AES_BLOCK_SIZE / sizeof(u32)]; ++ int crypto_async_idr; ++ ++ rctx->sa_state_ctr = NULL; ++ rctx->sa_state = NULL; ++ ++ if (IS_ECB(flags)) ++ goto skip_iv; ++ ++ memcpy(iv, reqiv, rctx->ivsize); ++ ++ rctx->sa_state = kzalloc(sizeof(*rctx->sa_state), GFP_KERNEL); ++ if (!rctx->sa_state) ++ return -ENOMEM; ++ ++ sa_state = rctx->sa_state; ++ ++ memcpy(sa_state->state_iv, iv, rctx->ivsize); ++ if (IS_RFC3686(flags)) { ++ sa_state->state_iv[0] = ctx->sa_nonce; ++ sa_state->state_iv[1] = iv[0]; ++ sa_state->state_iv[2] = iv[1]; ++ sa_state->state_iv[3] = cpu_to_be32(1); ++ } else if (!IS_HMAC(flags) && IS_CTR(flags)) { ++ /* Compute data length. */ ++ blocks = DIV_ROUND_UP(rctx->textsize, AES_BLOCK_SIZE); ++ ctr = be32_to_cpu(iv[3]); ++ /* Check 32bit counter overflow. */ ++ start = ctr; ++ end = start + blocks - 1; ++ if (end < start) { ++ split = AES_BLOCK_SIZE * -start; ++ /* ++ * Increment the counter manually to cope with ++ * the hardware counter overflow. ++ */ ++ iv[3] = 0xffffffff; ++ crypto_inc((u8 *)iv, AES_BLOCK_SIZE); ++ ++ rctx->sa_state_ctr = kzalloc(sizeof(*rctx->sa_state_ctr), ++ GFP_KERNEL); ++ if (!rctx->sa_state_ctr) ++ goto free_sa_state; ++ ++ memcpy(rctx->sa_state_ctr->state_iv, reqiv, rctx->ivsize); ++ memcpy(sa_state->state_iv, iv, rctx->ivsize); ++ ++ rctx->sa_state_ctr_base = dma_map_single(mtk->dev, rctx->sa_state_ctr, ++ sizeof(*rctx->sa_state_ctr), ++ DMA_TO_DEVICE); ++ err = dma_mapping_error(mtk->dev, rctx->sa_state_ctr_base); ++ if (err) ++ goto free_sa_state_ctr; ++ } ++ } ++ ++ rctx->sa_state_base = dma_map_single(mtk->dev, rctx->sa_state, ++ sizeof(*rctx->sa_state), DMA_TO_DEVICE); ++ err = dma_mapping_error(mtk->dev, rctx->sa_state_base); ++ if (err) ++ goto free_sa_state_ctr_dma; ++ ++skip_iv: ++ ++ cdesc.pe_ctrl_stat_word = FIELD_PREP(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, ++ EIP93_PE_CTRL_HOST_READY); ++ cdesc.sa_addr = rctx->sa_record_base; ++ cdesc.arc4_addr = 0; ++ ++ scoped_guard(spinlock_bh, &mtk->ring->idr_lock) ++ crypto_async_idr = idr_alloc(&mtk->ring->crypto_async_idr, async, 0, ++ EIP93_RING_NUM - 1, GFP_ATOMIC); ++ ++ cdesc.user_id = FIELD_PREP(EIP93_PE_USER_ID_CRYPTO_IDR, (u16)crypto_async_idr) | ++ FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, rctx->desc_flags); ++ ++ rctx->cdesc = &cdesc; ++ ++ /* map DMA_BIDIRECTIONAL to invalidate cache on destination ++ * implies __dma_cache_wback_inv ++ */ ++ if (!dma_map_sg(mtk->dev, dst, rctx->dst_nents, DMA_BIDIRECTIONAL)) { ++ err = -ENOMEM; ++ goto free_sa_state_ctr_dma; ++ } ++ ++ if (src != dst && ++ !dma_map_sg(mtk->dev, src, rctx->src_nents, DMA_TO_DEVICE)) { ++ err = -ENOMEM; ++ goto free_sg_dma; ++ } ++ ++ return eip93_scatter_combine(mtk, rctx, datalen, split, offsetin); ++ ++free_sg_dma: ++ dma_unmap_sg(mtk->dev, dst, rctx->dst_nents, DMA_BIDIRECTIONAL); ++free_sa_state_ctr_dma: ++ if (rctx->sa_state_ctr) ++ dma_unmap_single(mtk->dev, rctx->sa_state_ctr_base, ++ sizeof(*rctx->sa_state_ctr), ++ DMA_TO_DEVICE); ++free_sa_state_ctr: ++ kfree(rctx->sa_state_ctr); ++ if (rctx->sa_state) ++ dma_unmap_single(mtk->dev, rctx->sa_state_base, ++ sizeof(*rctx->sa_state), ++ DMA_TO_DEVICE); ++free_sa_state: ++ kfree(rctx->sa_state); ++ ++ return err; ++} ++ ++void eip93_unmap_dma(struct eip93_device *mtk, struct eip93_cipher_reqctx *rctx, ++ struct scatterlist *reqsrc, struct scatterlist *reqdst) ++{ ++ u32 len = rctx->assoclen + rctx->textsize; ++ u32 authsize = rctx->authsize; ++ u32 flags = rctx->flags; ++ u32 *otag; ++ int i; ++ ++ if (rctx->sg_src == rctx->sg_dst) { ++ dma_unmap_sg(mtk->dev, rctx->sg_dst, rctx->dst_nents, ++ DMA_BIDIRECTIONAL); ++ goto process_tag; ++ } ++ ++ dma_unmap_sg(mtk->dev, rctx->sg_src, rctx->src_nents, ++ DMA_TO_DEVICE); ++ ++ if (rctx->sg_src != reqsrc) ++ eip93_free_sg_copy(len + rctx->authsize, &rctx->sg_src); ++ ++ dma_unmap_sg(mtk->dev, rctx->sg_dst, rctx->dst_nents, ++ DMA_BIDIRECTIONAL); ++ ++ /* SHA tags need conversion from net-to-host */ ++process_tag: ++ if (IS_DECRYPT(flags)) ++ authsize = 0; ++ ++ if (authsize) { ++ if (!IS_HASH_MD5(flags)) { ++ otag = sg_virt(rctx->sg_dst) + len; ++ for (i = 0; i < (authsize / 4); i++) ++ otag[i] = be32_to_cpu(otag[i]); ++ } ++ } ++ ++ if (rctx->sg_dst != reqdst) { ++ sg_copy_from_buffer(reqdst, sg_nents(reqdst), ++ sg_virt(rctx->sg_dst), len + authsize); ++ eip93_free_sg_copy(len + rctx->authsize, &rctx->sg_dst); ++ } ++} ++ ++void eip93_handle_result(struct eip93_device *mtk, struct eip93_cipher_reqctx *rctx, ++ u8 *reqiv) ++{ ++ if (rctx->sa_state_ctr) ++ dma_unmap_single(mtk->dev, rctx->sa_state_ctr_base, ++ sizeof(*rctx->sa_state_ctr), ++ DMA_FROM_DEVICE); ++ ++ if (rctx->sa_state) ++ dma_unmap_single(mtk->dev, rctx->sa_state_base, ++ sizeof(*rctx->sa_state), ++ DMA_FROM_DEVICE); ++ ++ if (!IS_ECB(rctx->flags)) ++ memcpy(reqiv, rctx->sa_state->state_iv, rctx->ivsize); ++ ++ kfree(rctx->sa_state_ctr); ++ kfree(rctx->sa_state); ++} ++ ++/* basically this is set hmac - key */ ++int eip93_authenc_setkey(struct crypto_aead *aead, struct sa_record *sa, ++ const u8 *authkey, unsigned int authkeylen) ++{ ++ struct crypto_tfm *tfm = crypto_aead_tfm(aead); ++ struct eip93_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct crypto_ahash *ahash_tfm; ++ struct eip93_hash_reqctx *rctx; ++ struct scatterlist sg[1]; ++ struct ahash_request *req; ++ DECLARE_CRYPTO_WAIT(wait); ++ const char *alg_name; ++ u8 *ipad, *opad; ++ int i, ret; ++ ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ alg_name = "sha256-eip93"; ++ break; ++ case EIP93_HASH_SHA224: ++ alg_name = "sha224-eip93"; ++ break; ++ case EIP93_HASH_SHA1: ++ alg_name = "sha1-eip93"; ++ break; ++ case EIP93_HASH_MD5: ++ alg_name = "md5-eip93"; ++ break; ++ default: /* Impossible */ ++ return -EINVAL; ++ } ++ ++ ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); ++ if (IS_ERR(ahash_tfm)) ++ return PTR_ERR(ahash_tfm); ++ ++ req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); ++ if (!req) { ++ ret = -ENOMEM; ++ goto err_ahash; ++ } ++ ++ ipad = kcalloc(2, SHA256_BLOCK_SIZE, GFP_KERNEL); ++ if (!ipad) { ++ ret = -ENOMEM; ++ goto err_req; ++ } ++ opad = ipad + SHA256_BLOCK_SIZE; ++ ++ rctx = ahash_request_ctx(req); ++ crypto_init_wait(&wait); ++ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ++ crypto_req_done, &wait); ++ ++ /* Hash the key if > SHA256_BLOCK_SIZE */ ++ if (authkeylen > SHA256_BLOCK_SIZE) { ++ sg_init_one(&sg[0], authkey, authkeylen); ++ ++ ahash_request_set_crypt(req, sg, ipad, authkeylen); ++ ret = crypto_wait_req(crypto_ahash_digest(req), &wait); ++ ++ authkeylen = ctx->authsize; ++ } else { ++ memcpy(ipad, authkey, authkeylen); ++ } ++ ++ /* Copy to opad */ ++ memset(ipad + authkeylen, 0, SHA256_BLOCK_SIZE - authkeylen); ++ memcpy(opad, ipad, SHA256_BLOCK_SIZE); ++ ++ /* Pad with HMAC constants */ ++ for (i = 0; i < SHA256_BLOCK_SIZE; i++) { ++ ipad[i] ^= HMAC_IPAD_VALUE; ++ opad[i] ^= HMAC_OPAD_VALUE; ++ } ++ ++ /* Disable HASH_FINALIZE for ipad and opad hash */ ++ rctx->no_finalize = true; ++ ++ /* Hash ipad */ ++ sg_init_one(&sg[0], ipad, SHA256_BLOCK_SIZE); ++ ahash_request_set_crypt(req, sg, sa->sa_i_digest, SHA256_BLOCK_SIZE); ++ ret = crypto_ahash_init(req); ++ if (ret) ++ goto exit; ++ ++ /* Disable HASH_FINALIZE for ipad hash */ ++ rctx->no_finalize = true; ++ ++ ret = crypto_wait_req(crypto_ahash_finup(req), &wait); ++ if (ret) ++ goto exit; ++ ++ /* Hash opad */ ++ sg_init_one(&sg[0], opad, SHA256_BLOCK_SIZE); ++ ahash_request_set_crypt(req, sg, sa->sa_o_digest, SHA256_BLOCK_SIZE); ++ ret = crypto_ahash_init(req); ++ if (ret) ++ goto exit; ++ ++ /* Disable HASH_FINALIZE for opad hash */ ++ rctx->no_finalize = true; ++ ++ ret = crypto_wait_req(crypto_ahash_finup(req), &wait); ++ if (ret) ++ goto exit; ++ ++ if (!IS_HASH_MD5(ctx->flags)) { ++ for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++) { ++ u32 *ipad_hash = (u32 *)sa->sa_i_digest; ++ u32 *opad_hash = (u32 *)sa->sa_o_digest; ++ ++ ipad_hash[i] = cpu_to_be32(ipad_hash[i]); ++ opad_hash[i] = cpu_to_be32(opad_hash[i]); ++ } ++ } ++ ++exit: ++ kfree(ipad); ++err_req: ++ ahash_request_free(req); ++err_ahash: ++ crypto_free_ahash(ahash_tfm); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-common.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-cipher.h" ++#include "eip93-hash.h" ++#include "eip93-main.h" ++#include "eip93-common.h" ++#include "eip93-regs.h" ++ ++static void eip93_hash_free_data_blocks(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct mkt_hash_block *block; ++ ++ list_for_each_entry(block, &rctx->blocks, list) { ++ dma_unmap_single(rctx->mtk->dev, block->data_dma, ++ SHA256_BLOCK_SIZE, DMA_TO_DEVICE); ++ kfree(block); ++ } ++} ++ ++static void eip93_hash_free_sa_record(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ ++ if (IS_HMAC(ctx->flags)) { ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_record_hmac_base, ++ sizeof(*rctx->sa_record_hmac), DMA_TO_DEVICE); ++ kfree(rctx->sa_record_hmac); ++ } ++ ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_record_base, ++ sizeof(*rctx->sa_record), DMA_TO_DEVICE); ++ kfree(rctx->sa_record); ++} ++ ++static void eip93_hash_free_sa_state(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_state_base, ++ sizeof(*rctx->sa_state), DMA_TO_DEVICE); ++ kfree(rctx->sa_state); ++} ++ ++static struct sa_state *eip93_hash_get_sa_state(struct ahash_request *req, ++ dma_addr_t *sa_state_base) ++{ ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct eip93_device *mtk = ctx->mtk; ++ struct sa_state *sa_state; ++ int ret; ++ ++ sa_state = kzalloc(sizeof(*sa_state), GFP_KERNEL); ++ if (!sa_state) ++ return ERR_PTR(-ENOMEM); ++ ++ /* Init HASH constant */ ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ u32 sha256_init[] = { SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, ++ SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7 }; ++ ++ memcpy(sa_state->state_i_digest, sha256_init, sizeof(sha256_init)); ++ break; ++ case EIP93_HASH_SHA224: ++ u32 sha224_init[] = { SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, ++ SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7 }; ++ ++ memcpy(sa_state->state_i_digest, sha224_init, sizeof(sha224_init)); ++ break; ++ case EIP93_HASH_SHA1: ++ u32 sha1_init[] = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }; ++ ++ memcpy(sa_state->state_i_digest, sha1_init, sizeof(sha1_init)); ++ break; ++ case EIP93_HASH_MD5: ++ u32 md5_init[] = { MD5_H0, MD5_H1, MD5_H2, MD5_H3 }; ++ ++ memcpy(sa_state->state_i_digest, md5_init, sizeof(md5_init)); ++ break; ++ default: /* Impossible */ ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ *sa_state_base = dma_map_single(mtk->dev, sa_state, ++ sizeof(*sa_state), DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, *sa_state_base); ++ if (ret) { ++ kfree(sa_state); ++ return ERR_PTR(ret); ++ } ++ ++ return sa_state; ++} ++ ++static int _eip93_hash_init(struct ahash_request *req, struct sa_state *sa_state, ++ dma_addr_t sa_state_base) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct sa_record *sa_record, *sa_record_hmac; ++ struct eip93_device *mtk = rctx->mtk; ++ int digestsize; ++ int ret; ++ ++ sa_record = kzalloc(sizeof(*sa_record), GFP_KERNEL); ++ if (!sa_record) ++ return -ENOMEM; ++ ++ if (IS_HMAC(ctx->flags)) { ++ sa_record_hmac = kzalloc(sizeof(*sa_record_hmac), GFP_KERNEL); ++ if (!sa_record_hmac) { ++ ret = -ENOMEM; ++ goto free_sa_record; ++ } ++ } ++ ++ digestsize = crypto_ahash_digestsize(ahash); ++ ++ eip93_set_sa_record(sa_record, 0, ctx->flags); ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_HASH_FROM_STATE; ++ sa_record->sa_cmd0_word |= EIP93_SA_CMD_SAVE_HASH; ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_OPCODE; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_OPCODE, ++ EIP93_SA_CMD_OPCODE_BASIC_OUT_HASH); ++ sa_record->sa_cmd0_word &= ~EIP93_SA_CMD_DIGEST_LENGTH; ++ sa_record->sa_cmd0_word |= FIELD_PREP(EIP93_SA_CMD_DIGEST_LENGTH, ++ digestsize / sizeof(u32)); ++ ++ /* ++ * HMAC special handling ++ * Enabling CMD_HMAC force the inner hash to be always finalized. ++ * This cause problems on handling message > 64 byte as we ++ * need to produce intermediate inner hash on sending intermediate ++ * 64 bytes blocks. ++ * ++ * To handle this, enable CMD_HMAC only on the last block. ++ * We make a duplicate of sa_record and on the last descriptor, ++ * we pass a dedicated sa_record with CMD_HMAC enabled to make ++ * EIP93 apply the outer hash. ++ */ ++ if (IS_HMAC(ctx->flags)) { ++ memcpy(sa_record_hmac, sa_record, sizeof(*sa_record)); ++ /* Copy pre-hashed opad for HMAC */ ++ memcpy(sa_record_hmac->sa_o_digest, ctx->opad, SHA256_DIGEST_SIZE); ++ ++ /* Disable HMAC for hash normal sa_record */ ++ sa_record->sa_cmd1_word &= ~EIP93_SA_CMD_HMAC; ++ } ++ ++ rctx->mtk = ctx->mtk; ++ rctx->sa_record = sa_record; ++ rctx->sa_record_base = dma_map_single(mtk->dev, rctx->sa_record, ++ sizeof(*rctx->sa_record), ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, rctx->sa_record_base); ++ if (ret) ++ goto free_sa_record; ++ ++ if (IS_HMAC(ctx->flags)) { ++ rctx->sa_record_hmac = sa_record_hmac; ++ rctx->sa_record_hmac_base = dma_map_single(mtk->dev, ++ rctx->sa_record_hmac, ++ sizeof(*rctx->sa_record_hmac), ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, rctx->sa_record_hmac_base); ++ if (ret) ++ goto free_sa_record_base; ++ } ++ ++ rctx->sa_state = sa_state; ++ rctx->sa_state_base = sa_state_base; ++ ++ rctx->len = 0; ++ rctx->left_last = 0; ++ rctx->no_finalize = false; ++ INIT_LIST_HEAD(&rctx->blocks); ++ ++ return 0; ++ ++free_sa_record_base: ++ dma_unmap_single(mtk->dev, rctx->sa_record_base, sizeof(*rctx->sa_record), ++ DMA_TO_DEVICE); ++free_sa_record: ++ kfree(sa_record); ++ return ret; ++} ++ ++static int eip93_hash_init(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct sa_state *sa_state; ++ dma_addr_t sa_state_base; ++ int ret; ++ ++ sa_state = eip93_hash_get_sa_state(req, &sa_state_base); ++ if (IS_ERR(sa_state)) ++ return PTR_ERR(sa_state); ++ ++ ret = _eip93_hash_init(req, sa_state, sa_state_base); ++ if (ret) ++ eip93_hash_free_sa_state(req); ++ ++ /* For HMAC setup the initial block for ipad */ ++ if (IS_HMAC(ctx->flags)) { ++ struct mkt_hash_block *block; ++ ++ block = kzalloc(sizeof(*block), GFP_KERNEL); ++ if (!block) { ++ eip93_hash_free_sa_record(req); ++ eip93_hash_free_sa_state(req); ++ return -ENOMEM; ++ } ++ ++ memcpy(block->data, ctx->ipad, SHA256_BLOCK_SIZE); ++ ++ list_add(&block->list, &rctx->blocks); ++ ++ rctx->len += SHA256_BLOCK_SIZE; ++ } ++ ++ return ret; ++} ++ ++static void eip93_send_hash_req(struct crypto_async_request *async, dma_addr_t src_addr, ++ u32 len, bool last) ++{ ++ struct ahash_request *req = ahash_request_cast(async); ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct eip93_device *mtk = rctx->mtk; ++ struct eip93_descriptor cdesc = { }; ++ int ret; ++ ++ cdesc.pe_ctrl_stat_word = FIELD_PREP(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, ++ EIP93_PE_CTRL_HOST_READY); ++ cdesc.sa_addr = rctx->sa_record_base; ++ cdesc.arc4_addr = 0; ++ ++ cdesc.state_addr = rctx->sa_state_base; ++ cdesc.src_addr = src_addr; ++ cdesc.pe_length_word = FIELD_PREP(EIP93_PE_LENGTH_HOST_PE_READY, ++ EIP93_PE_LENGTH_HOST_READY); ++ cdesc.pe_length_word |= FIELD_PREP(EIP93_PE_LENGTH_LENGTH, ++ len); ++ ++ cdesc.user_id |= FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, EIP93_DESC_HASH); ++ ++ if (last) { ++ int crypto_async_idr; ++ ++ /* For last block, pass sa_record with CMD_HMAC enabled */ ++ if (IS_HMAC(ctx->flags)) ++ cdesc.sa_addr = rctx->sa_record_hmac_base; ++ ++ if (!rctx->no_finalize) ++ cdesc.pe_ctrl_stat_word |= EIP93_PE_CTRL_PE_HASH_FINAL; ++ ++ scoped_guard(spinlock_bh, &mtk->ring->idr_lock) ++ crypto_async_idr = idr_alloc(&mtk->ring->crypto_async_idr, async, 0, ++ EIP93_RING_NUM - 1, GFP_ATOMIC); ++ ++ cdesc.user_id |= FIELD_PREP(EIP93_PE_USER_ID_CRYPTO_IDR, (u16)crypto_async_idr) | ++ FIELD_PREP(EIP93_PE_USER_ID_DESC_FLAGS, EIP93_DESC_LAST); ++ } ++ ++again: ++ ret = eip93_put_descriptor(mtk, &cdesc); ++ if (ret) { ++ usleep_range(EIP93_RING_BUSY_DELAY, ++ EIP93_RING_BUSY_DELAY * 2); ++ goto again; ++ } ++ ++ /* Writing new descriptor count starts DMA action */ ++ writel(1, mtk->base + EIP93_REG_PE_CD_COUNT); ++} ++ ++static int eip93_hash_update(struct ahash_request *req) ++{ ++ struct crypto_async_request *async = &req->base; ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ unsigned int to_consume = req->nbytes; ++ struct eip93_device *mtk = rctx->mtk; ++ struct mkt_hash_block *block; ++ int read = 0; ++ int ret; ++ ++ /* If the request is 0 length, do nothing */ ++ if (!to_consume) ++ return 0; ++ ++ /* ++ * Check if we are at a second iteration. ++ * 1. Try to fill the first block to 64byte (if not already) ++ * 2. Send full block (if we have more data to consume) ++ */ ++ if (rctx->len > 0) { ++ int offset = SHA256_BLOCK_SIZE - rctx->left_last; ++ ++ block = list_first_entry(&rctx->blocks, ++ struct mkt_hash_block, list); ++ ++ /* Fill the first block */ ++ if (rctx->left_last) { ++ read += sg_pcopy_to_buffer(req->src, sg_nents(req->src), ++ block->data + offset, ++ min(to_consume, rctx->left_last), ++ 0); ++ to_consume -= read; ++ rctx->left_last -= read; ++ } ++ ++ /* Send descriptor if we have more data to consume */ ++ if (to_consume > 0) { ++ block->data_dma = dma_map_single(mtk->dev, block->data, ++ SHA256_BLOCK_SIZE, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, block->data_dma); ++ if (ret) ++ return ret; ++ ++ eip93_send_hash_req(async, block->data_dma, ++ SHA256_BLOCK_SIZE, false); ++ } ++ } ++ ++ /* ++ * Consume remaining data. ++ * 1. Loop until we consume all the data in block of 64bytes ++ * 2. Send full block of 64bytes ++ * 3. Skip sending last block for future update() or for final() to ++ * enable HASH_FINALIZE bit. ++ */ ++ while (to_consume > 0) { ++ int to_read = min(to_consume, SHA256_BLOCK_SIZE); ++ ++ block = kzalloc(sizeof(*block), GFP_KERNEL); ++ if (!block) ++ return -ENOMEM; ++ ++ read += sg_pcopy_to_buffer(req->src, sg_nents(req->src), ++ block->data, to_read, ++ read); ++ ++ list_add(&block->list, &rctx->blocks); ++ ++ to_consume -= to_read; ++ rctx->left_last = SHA256_BLOCK_SIZE - to_read; ++ ++ /* Send descriptor if we have more data to consume */ ++ if (to_consume > 0) { ++ block->data_dma = dma_map_single(mtk->dev, block->data, ++ SHA256_BLOCK_SIZE, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, block->data_dma); ++ if (ret) ++ return ret; ++ ++ eip93_send_hash_req(async, block->data_dma, ++ SHA256_BLOCK_SIZE, false); ++ } ++ } ++ ++ /* ++ * Update counter with processed bytes. ++ * This is also used to check if we are at the second iteration ++ * of an update(). ++ */ ++ rctx->len += req->nbytes; ++ ++ return 0; ++} ++ ++void eip93_hash_handle_result(struct crypto_async_request *async, int err) ++{ ++ struct ahash_request *req = ahash_request_cast(async); ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ int digestsize = crypto_ahash_digestsize(ahash); ++ struct sa_state *sa_state = rctx->sa_state; ++ int i; ++ ++ /* Unmap and sync sa_state for host */ ++ dma_unmap_single(rctx->mtk->dev, rctx->sa_state_base, ++ sizeof(*sa_state), DMA_FROM_DEVICE); ++ ++ /* ++ * With no_finalize assume SHA256_DIGEST_SIZE buffer is passed. ++ * This is to handle SHA224 that have a 32 byte intermediate digest. ++ */ ++ if (rctx->no_finalize) ++ digestsize = SHA256_DIGEST_SIZE; ++ ++ /* bytes needs to be swapped for req->result */ ++ if (!IS_HASH_MD5(ctx->flags)) { ++ for (i = 0; i < digestsize / sizeof(u32); i++) { ++ u32 *digest = (u32 *)sa_state->state_i_digest; ++ ++ digest[i] = be32_to_cpu(digest[i]); ++ } ++ } ++ ++ memcpy(req->result, sa_state->state_i_digest, digestsize); ++ ++ kfree(sa_state); ++ eip93_hash_free_data_blocks(req); ++ eip93_hash_free_sa_record(req); ++ ++ ahash_request_complete(req, err); ++} ++ ++static int eip93_hash_final(struct ahash_request *req) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); ++ struct eip93_hash_ctx *ctx = crypto_ahash_ctx(ahash); ++ struct crypto_async_request *async = &req->base; ++ struct eip93_device *mtk = rctx->mtk; ++ struct mkt_hash_block *block; ++ int ret; ++ ++ /* EIP93 can't handle zero bytes hash */ ++ if (!rctx->len && !IS_HMAC(ctx->flags)) { ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ memcpy(req->result, sha256_zero_message_hash, ++ SHA256_DIGEST_SIZE); ++ break; ++ case EIP93_HASH_SHA224: ++ memcpy(req->result, sha224_zero_message_hash, ++ SHA224_DIGEST_SIZE); ++ break; ++ case EIP93_HASH_SHA1: ++ memcpy(req->result, sha1_zero_message_hash, ++ SHA1_DIGEST_SIZE); ++ break; ++ case EIP93_HASH_MD5: ++ memcpy(req->result, md5_zero_message_hash, ++ MD5_DIGEST_SIZE); ++ break; ++ default: /* Impossible */ ++ return -EINVAL; ++ } ++ ++ eip93_hash_free_sa_state(req); ++ eip93_hash_free_sa_record(req); ++ ++ return 0; ++ } ++ ++ /* Send last block */ ++ block = list_first_entry(&rctx->blocks, struct mkt_hash_block, list); ++ ++ block->data_dma = dma_map_single(mtk->dev, block->data, ++ SHA256_BLOCK_SIZE, DMA_TO_DEVICE); ++ ret = dma_mapping_error(mtk->dev, block->data_dma); ++ if (ret) ++ return ret; ++ ++ eip93_send_hash_req(async, block->data_dma, ++ SHA256_BLOCK_SIZE - rctx->left_last, ++ true); ++ ++ return -EINPROGRESS; ++} ++ ++static int eip93_hash_finup(struct ahash_request *req) ++{ ++ int ret; ++ ++ ret = eip93_hash_update(req); ++ if (ret) ++ return ret; ++ ++ return eip93_hash_final(req); ++} ++ ++static int eip93_hash_hmac_setkey(struct crypto_ahash *ahash, const u8 *key, ++ u32 keylen) ++{ ++ unsigned int digestsize = crypto_ahash_digestsize(ahash); ++ struct crypto_tfm *tfm = crypto_ahash_tfm(ahash); ++ struct eip93_hash_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct crypto_ahash *ahash_tfm; ++ struct eip93_hash_reqctx *rctx; ++ struct scatterlist sg[1]; ++ struct ahash_request *req; ++ DECLARE_CRYPTO_WAIT(wait); ++ const char *alg_name; ++ int i, ret = 0; ++ u8 *opad; ++ ++ switch ((ctx->flags & EIP93_HASH_MASK)) { ++ case EIP93_HASH_SHA256: ++ alg_name = "sha256-eip93"; ++ break; ++ case EIP93_HASH_SHA224: ++ alg_name = "sha224-eip93"; ++ break; ++ case EIP93_HASH_SHA1: ++ alg_name = "sha1-eip93"; ++ break; ++ case EIP93_HASH_MD5: ++ alg_name = "md5-eip93"; ++ break; ++ default: /* Impossible */ ++ return -EINVAL; ++ } ++ ++ ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); ++ if (IS_ERR(ahash_tfm)) ++ return PTR_ERR(ahash_tfm); ++ ++ req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); ++ if (!req) { ++ ret = -ENOMEM; ++ goto err_ahash; ++ } ++ ++ opad = kzalloc(SHA256_BLOCK_SIZE, GFP_KERNEL); ++ if (!opad) { ++ ret = -ENOMEM; ++ goto err_req; ++ } ++ ++ rctx = ahash_request_ctx(req); ++ crypto_init_wait(&wait); ++ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ++ crypto_req_done, &wait); ++ ++ /* Hash the key if > SHA256_BLOCK_SIZE */ ++ if (keylen > SHA256_BLOCK_SIZE) { ++ sg_init_one(&sg[0], key, keylen); ++ ++ ahash_request_set_crypt(req, sg, ctx->ipad, keylen); ++ ret = crypto_wait_req(crypto_ahash_digest(req), &wait); ++ ++ keylen = digestsize; ++ } else { ++ memcpy(ctx->ipad, key, keylen); ++ } ++ ++ /* Copy to opad */ ++ memset(ctx->ipad + keylen, 0, SHA256_BLOCK_SIZE - keylen); ++ memcpy(opad, ctx->ipad, SHA256_BLOCK_SIZE); ++ ++ /* Pad with HMAC constants */ ++ for (i = 0; i < SHA256_BLOCK_SIZE; i++) { ++ ctx->ipad[i] ^= HMAC_IPAD_VALUE; ++ opad[i] ^= HMAC_OPAD_VALUE; ++ } ++ ++ sg_init_one(&sg[0], opad, SHA256_BLOCK_SIZE); ++ ++ /* Hash opad */ ++ ahash_request_set_crypt(req, sg, ctx->opad, SHA256_BLOCK_SIZE); ++ ret = crypto_ahash_init(req); ++ if (ret) ++ goto exit; ++ ++ /* Disable HASH_FINALIZE for opad hash */ ++ rctx->no_finalize = true; ++ ++ ret = crypto_wait_req(crypto_ahash_finup(req), &wait); ++ if (ret) ++ goto exit; ++ ++ if (!IS_HASH_MD5(ctx->flags)) { ++ u32 *opad_hash = (u32 *)ctx->opad; ++ ++ for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++) ++ opad_hash[i] = cpu_to_be32(opad_hash[i]); ++ } ++ ++exit: ++ kfree(opad); ++err_req: ++ ahash_request_free(req); ++err_ahash: ++ crypto_free_ahash(ahash_tfm); ++ ++ return ret; ++} ++ ++static int eip93_hash_cra_init(struct crypto_tfm *tfm) ++{ ++ struct eip93_hash_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct eip93_alg_template *tmpl = container_of(tfm->__crt_alg, ++ struct eip93_alg_template, alg.ahash.halg.base); ++ ++ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), ++ sizeof(struct eip93_hash_reqctx)); ++ ++ ctx->mtk = tmpl->mtk; ++ ctx->flags = tmpl->flags; ++ ++ return 0; ++} ++ ++static int eip93_hash_digest(struct ahash_request *req) ++{ ++ int ret; ++ ++ ret = eip93_hash_init(req); ++ if (ret) ++ return ret; ++ ++ return eip93_hash_finup(req); ++} ++ ++static int eip93_hash_import(struct ahash_request *req, const void *in) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ const struct eip93_hash_export_state *state = in; ++ int ret; ++ ++ ret = _eip93_hash_init(req, state->sa_state, state->sa_state_base); ++ if (ret) ++ goto err; ++ ++ rctx->len = state->len; ++ rctx->left_last = state->left_last; ++ memcpy(&rctx->blocks, &state->blocks, sizeof(rctx->blocks)); ++ ++ return 0; ++err: ++ eip93_hash_free_data_blocks(req); ++ eip93_hash_free_sa_state(req); ++ return ret; ++} ++ ++static int eip93_hash_export(struct ahash_request *req, void *out) ++{ ++ struct eip93_hash_reqctx *rctx = ahash_request_ctx(req); ++ struct eip93_hash_export_state *state = out; ++ ++ state->sa_state = rctx->sa_state; ++ state->sa_state_base = rctx->sa_state_base; ++ state->len = rctx->len; ++ state->left_last = rctx->left_last; ++ memcpy(&state->blocks, &rctx->blocks, sizeof(rctx->blocks)); ++ ++ return 0; ++} ++ ++struct eip93_alg_template eip93_alg_md5 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_MD5, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = MD5_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "md5", ++ .cra_driver_name = "md5-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = MD5_HMAC_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_sha1 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_SHA1, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA1_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "sha1", ++ .cra_driver_name = "sha1-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_sha224 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_SHA224, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA224_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "sha224", ++ .cra_driver_name = "sha224-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA224_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_sha256 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_SHA256, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA256_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "sha256", ++ .cra_driver_name = "sha256-eip93", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_md5 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_MD5, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = MD5_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(md5)", ++ .cra_driver_name = "hmac(md5-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = MD5_HMAC_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_sha1 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA1, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA1_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(sha1)", ++ .cra_driver_name = "hmac(sha1-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA1_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_sha224 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA224, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA224_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(sha224)", ++ .cra_driver_name = "hmac(sha224-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA224_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; ++ ++struct eip93_alg_template eip93_alg_hmac_sha256 = { ++ .type = EIP93_ALG_TYPE_HASH, ++ .flags = EIP93_HASH_HMAC | EIP93_HASH_SHA256, ++ .alg.ahash = { ++ .init = eip93_hash_init, ++ .update = eip93_hash_update, ++ .final = eip93_hash_final, ++ .finup = eip93_hash_finup, ++ .digest = eip93_hash_digest, ++ .setkey = eip93_hash_hmac_setkey, ++ .export = eip93_hash_export, ++ .import = eip93_hash_import, ++ .halg = { ++ .digestsize = SHA256_DIGEST_SIZE, ++ .statesize = sizeof(struct eip93_hash_export_state), ++ .base = { ++ .cra_name = "hmac(sha256)", ++ .cra_driver_name = "hmac(sha256-eip93)", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_KERN_DRIVER_ONLY | ++ CRYPTO_ALG_ALLOCATES_MEMORY, ++ .cra_blocksize = SHA256_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct eip93_hash_ctx), ++ .cra_init = eip93_hash_cra_init, ++ .cra_module = THIS_MODULE, ++ }, ++ }, ++ }, ++}; +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-hash.h +@@ -0,0 +1,72 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++ ++#include "eip93-main.h" ++ ++struct eip93_hash_ctx { ++ struct eip93_device *mtk; ++ u32 flags; ++ ++ u8 ipad[SHA256_BLOCK_SIZE] __aligned(sizeof(u32)); ++ u8 opad[SHA256_DIGEST_SIZE] __aligned(sizeof(u32)); ++}; ++ ++struct eip93_hash_reqctx { ++ struct eip93_device *mtk; ++ ++ struct sa_record *sa_record; ++ dma_addr_t sa_record_base; ++ ++ struct sa_record *sa_record_hmac; ++ dma_addr_t sa_record_hmac_base; ++ ++ struct sa_state *sa_state; ++ dma_addr_t sa_state_base; ++ ++ /* Don't enable HASH_FINALIZE when last block is sent */ ++ bool no_finalize; ++ ++ /* ++ * EIP93 requires data to be accumulated in block of 64 bytes ++ * for intermediate hash calculation. ++ */ ++ u64 len; ++ u32 left_last; ++ struct list_head blocks; ++}; ++ ++struct mkt_hash_block { ++ struct list_head list; ++ u8 data[SHA256_BLOCK_SIZE] __aligned(sizeof(u32)); ++ dma_addr_t data_dma; ++}; ++ ++struct eip93_hash_export_state { ++ u64 len; ++ u32 left_last; ++ struct sa_state *sa_state; ++ dma_addr_t sa_state_base; ++ struct list_head blocks; ++}; ++ ++void eip93_hash_handle_result(struct crypto_async_request *async, int err); ++ ++extern struct eip93_alg_template eip93_alg_md5; ++extern struct eip93_alg_template eip93_alg_sha1; ++extern struct eip93_alg_template eip93_alg_sha224; ++extern struct eip93_alg_template eip93_alg_sha256; ++extern struct eip93_alg_template eip93_alg_hmac_md5; ++extern struct eip93_alg_template eip93_alg_hmac_sha1; ++extern struct eip93_alg_template eip93_alg_hmac_sha224; ++extern struct eip93_alg_template eip93_alg_hmac_sha256; ++ ++#endif /* _EIP93_HASH_H_ */ +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-main.c +@@ -0,0 +1,502 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-main.h" ++#include "eip93-regs.h" ++#include "eip93-common.h" ++#include "eip93-cipher.h" ++#include "eip93-aes.h" ++#include "eip93-des.h" ++#include "eip93-aead.h" ++#include "eip93-hash.h" ++ ++static struct eip93_alg_template *eip93_algs[] = { ++ &eip93_alg_ecb_des, ++ &eip93_alg_cbc_des, ++ &eip93_alg_ecb_des3_ede, ++ &eip93_alg_cbc_des3_ede, ++ &eip93_alg_ecb_aes, ++ &eip93_alg_cbc_aes, ++ &eip93_alg_ctr_aes, ++ &eip93_alg_rfc3686_aes, ++ &eip93_alg_authenc_hmac_md5_cbc_des, ++ &eip93_alg_authenc_hmac_sha1_cbc_des, ++ &eip93_alg_authenc_hmac_sha224_cbc_des, ++ &eip93_alg_authenc_hmac_sha256_cbc_des, ++ &eip93_alg_authenc_hmac_md5_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_sha1_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_sha224_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_sha256_cbc_des3_ede, ++ &eip93_alg_authenc_hmac_md5_cbc_aes, ++ &eip93_alg_authenc_hmac_sha1_cbc_aes, ++ &eip93_alg_authenc_hmac_sha224_cbc_aes, ++ &eip93_alg_authenc_hmac_sha256_cbc_aes, ++ &eip93_alg_authenc_hmac_md5_rfc3686_aes, ++ &eip93_alg_authenc_hmac_sha1_rfc3686_aes, ++ &eip93_alg_authenc_hmac_sha224_rfc3686_aes, ++ &eip93_alg_authenc_hmac_sha256_rfc3686_aes, ++ &eip93_alg_md5, ++ &eip93_alg_sha1, ++ &eip93_alg_sha224, ++ &eip93_alg_sha256, ++ &eip93_alg_hmac_md5, ++ &eip93_alg_hmac_sha1, ++ &eip93_alg_hmac_sha224, ++ &eip93_alg_hmac_sha256, ++}; ++ ++inline void eip93_irq_disable(struct eip93_device *mtk, u32 mask) ++{ ++ __raw_writel(mask, mtk->base + EIP93_REG_MASK_DISABLE); ++} ++ ++inline void eip93_irq_enable(struct eip93_device *mtk, u32 mask) ++{ ++ __raw_writel(mask, mtk->base + EIP93_REG_MASK_ENABLE); ++} ++ ++inline void eip93_irq_clear(struct eip93_device *mtk, u32 mask) ++{ ++ __raw_writel(mask, mtk->base + EIP93_REG_INT_CLR); ++} ++ ++static void eip93_unregister_algs(unsigned int i) ++{ ++ unsigned int j; ++ ++ for (j = 0; j < i; j++) { ++ switch (eip93_algs[j]->type) { ++ case EIP93_ALG_TYPE_SKCIPHER: ++ crypto_unregister_skcipher(&eip93_algs[j]->alg.skcipher); ++ break; ++ case EIP93_ALG_TYPE_AEAD: ++ crypto_unregister_aead(&eip93_algs[j]->alg.aead); ++ break; ++ case EIP93_ALG_TYPE_HASH: ++ crypto_unregister_ahash(&eip93_algs[i]->alg.ahash); ++ break; ++ } ++ } ++} ++ ++static int eip93_register_algs(struct eip93_device *mtk, u32 supported_algo_flags) ++{ ++ unsigned int i; ++ int ret = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(eip93_algs); i++) { ++ u32 alg_flags = eip93_algs[i]->flags; ++ ++ eip93_algs[i]->mtk = mtk; ++ ++ if ((IS_DES(alg_flags) || IS_3DES(alg_flags)) && ++ !(supported_algo_flags & EIP93_PE_OPTION_TDES)) ++ continue; ++ ++ if (IS_AES(alg_flags)) { ++ if (!(supported_algo_flags & EIP93_PE_OPTION_AES)) ++ continue; ++ ++ if (!IS_HMAC(alg_flags)) { ++ if (supported_algo_flags & EIP93_PE_OPTION_AES_KEY128) ++ eip93_algs[i]->alg.skcipher.max_keysize = ++ AES_KEYSIZE_128; ++ ++ if (supported_algo_flags & EIP93_PE_OPTION_AES_KEY192) ++ eip93_algs[i]->alg.skcipher.max_keysize = ++ AES_KEYSIZE_192; ++ ++ if (supported_algo_flags & EIP93_PE_OPTION_AES_KEY256) ++ eip93_algs[i]->alg.skcipher.max_keysize = ++ AES_KEYSIZE_256; ++ ++ if (IS_RFC3686(alg_flags)) ++ eip93_algs[i]->alg.skcipher.max_keysize += ++ CTR_RFC3686_NONCE_SIZE; ++ } ++ } ++ ++ if (IS_HASH_MD5(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_MD5)) ++ continue; ++ ++ if (IS_HASH_SHA1(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_SHA_1)) ++ continue; ++ ++ if (IS_HASH_SHA224(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_SHA_224)) ++ continue; ++ ++ if (IS_HASH_SHA256(alg_flags) && ++ !(supported_algo_flags & EIP93_PE_OPTION_SHA_256)) ++ continue; ++ ++ switch (eip93_algs[i]->type) { ++ case EIP93_ALG_TYPE_SKCIPHER: ++ ret = crypto_register_skcipher(&eip93_algs[i]->alg.skcipher); ++ break; ++ case EIP93_ALG_TYPE_AEAD: ++ ret = crypto_register_aead(&eip93_algs[i]->alg.aead); ++ break; ++ case EIP93_ALG_TYPE_HASH: ++ ret = crypto_register_ahash(&eip93_algs[i]->alg.ahash); ++ break; ++ } ++ if (ret) ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ eip93_unregister_algs(i); ++ ++ return ret; ++} ++ ++static void eip93_handle_result_descriptor(struct eip93_device *mtk) ++{ ++ struct crypto_async_request *async; ++ struct eip93_descriptor *rdesc; ++ u16 desc_flags, crypto_idr; ++ bool last_entry; ++ int handled, left, err; ++ u32 pe_ctrl_stat; ++ u32 pe_length; ++ ++get_more: ++ handled = 0; ++ ++ left = readl(mtk->base + EIP93_REG_PE_RD_COUNT) & EIP93_PE_RD_COUNT; ++ ++ if (!left) { ++ eip93_irq_clear(mtk, EIP93_INT_RDR_THRESH); ++ eip93_irq_enable(mtk, EIP93_INT_RDR_THRESH); ++ return; ++ } ++ ++ last_entry = false; ++ ++ while (left) { ++ rdesc = eip93_get_descriptor(mtk); ++ if (IS_ERR(rdesc)) { ++ dev_err(mtk->dev, "Ndesc: %d nreq: %d\n", ++ handled, left); ++ err = -EIO; ++ break; ++ } ++ /* make sure DMA is finished writing */ ++ do { ++ pe_ctrl_stat = READ_ONCE(rdesc->pe_ctrl_stat_word); ++ pe_length = READ_ONCE(rdesc->pe_length_word); ++ } while (FIELD_GET(EIP93_PE_CTRL_PE_READY_DES_TRING_OWN, pe_ctrl_stat) != ++ EIP93_PE_CTRL_PE_READY || ++ FIELD_GET(EIP93_PE_LENGTH_HOST_PE_READY, pe_length) != ++ EIP93_PE_LENGTH_PE_READY); ++ ++ err = rdesc->pe_ctrl_stat_word & (EIP93_PE_CTRL_PE_EXT_ERR_CODE | ++ EIP93_PE_CTRL_PE_EXT_ERR | ++ EIP93_PE_CTRL_PE_SEQNUM_ERR | ++ EIP93_PE_CTRL_PE_PAD_ERR | ++ EIP93_PE_CTRL_PE_AUTH_ERR); ++ ++ desc_flags = FIELD_GET(EIP93_PE_USER_ID_DESC_FLAGS, rdesc->user_id); ++ crypto_idr = FIELD_GET(EIP93_PE_USER_ID_CRYPTO_IDR, rdesc->user_id); ++ ++ writel(1, mtk->base + EIP93_REG_PE_RD_COUNT); ++ eip93_irq_clear(mtk, EIP93_INT_RDR_THRESH); ++ ++ handled++; ++ left--; ++ ++ if (desc_flags & EIP93_DESC_LAST) { ++ last_entry = true; ++ break; ++ } ++ } ++ ++ if (!last_entry) ++ goto get_more; ++ ++ /* Get crypto async ref only for last descriptor */ ++ scoped_guard(spinlock_bh, &mtk->ring->idr_lock) { ++ async = idr_find(&mtk->ring->crypto_async_idr, crypto_idr); ++ idr_remove(&mtk->ring->crypto_async_idr, crypto_idr); ++ } ++ ++ /* Parse error in ctrl stat word */ ++ err = eip93_parse_ctrl_stat_err(mtk, err); ++ ++ if (desc_flags & EIP93_DESC_SKCIPHER) ++ eip93_skcipher_handle_result(async, err); ++ ++ if (desc_flags & EIP93_DESC_AEAD) ++ eip93_aead_handle_result(async, err); ++ ++ if (desc_flags & EIP93_DESC_HASH) ++ eip93_hash_handle_result(async, err); ++ ++ goto get_more; ++} ++ ++static void eip93_done_task(unsigned long data) ++{ ++ struct eip93_device *mtk = (struct eip93_device *)data; ++ ++ eip93_handle_result_descriptor(mtk); ++} ++ ++static irqreturn_t eip93_irq_handler(int irq, void *data) ++{ ++ struct eip93_device *mtk = data; ++ u32 irq_status; ++ ++ irq_status = readl(mtk->base + EIP93_REG_INT_MASK_STAT); ++ if (FIELD_GET(EIP93_INT_RDR_THRESH, irq_status)) { ++ eip93_irq_disable(mtk, EIP93_INT_RDR_THRESH); ++ tasklet_schedule(&mtk->ring->done_task); ++ return IRQ_HANDLED; ++ } ++ ++ /* Ignore errors in AUTO mode, handled by the RDR */ ++ eip93_irq_clear(mtk, irq_status); ++ if (irq_status) ++ eip93_irq_disable(mtk, irq_status); ++ ++ return IRQ_NONE; ++} ++ ++static void eip93_initialize(struct eip93_device *mtk, u32 supported_algo_flags) ++{ ++ u32 val; ++ ++ /* Reset PE and rings */ ++ val = EIP93_PE_CONFIG_RST_PE | EIP93_PE_CONFIG_RST_RING; ++ val |= EIP93_PE_TARGET_AUTO_RING_MODE; ++ /* For Auto more, update the CDR ring owner after processing */ ++ val |= EIP93_PE_CONFIG_EN_CDR_UPDATE; ++ writel(val, mtk->base + EIP93_REG_PE_CONFIG); ++ ++ /* Wait for PE and ring to reset */ ++ usleep_range(10, 20); ++ ++ /* Release PE and ring reset */ ++ val = readl(mtk->base + EIP93_REG_PE_CONFIG); ++ val &= ~(EIP93_PE_CONFIG_RST_PE | EIP93_PE_CONFIG_RST_RING); ++ writel(val, mtk->base + EIP93_REG_PE_CONFIG); ++ ++ /* Config Clocks */ ++ val = EIP93_PE_CLOCK_EN_PE_CLK; ++ if (supported_algo_flags & EIP93_PE_OPTION_TDES) ++ val |= EIP93_PE_CLOCK_EN_DES_CLK; ++ if (supported_algo_flags & EIP93_PE_OPTION_AES) ++ val |= EIP93_PE_CLOCK_EN_AES_CLK; ++ if (supported_algo_flags & ++ (EIP93_PE_OPTION_MD5 | EIP93_PE_OPTION_SHA_1 | EIP93_PE_OPTION_SHA_224 | ++ EIP93_PE_OPTION_SHA_256)) ++ val |= EIP93_PE_CLOCK_EN_HASH_CLK; ++ writel(val, mtk->base + EIP93_REG_PE_CLOCK_CTRL); ++ ++ /* Config DMA thresholds */ ++ val = FIELD_PREP(EIP93_PE_OUTBUF_THRESH, 128) | ++ FIELD_PREP(EIP93_PE_INBUF_THRESH, 128); ++ writel(val, mtk->base + EIP93_REG_PE_BUF_THRESH); ++ ++ /* Clear/ack all interrupts before disable all */ ++ eip93_irq_clear(mtk, EIP93_INT_ALL); ++ eip93_irq_disable(mtk, EIP93_INT_ALL); ++ ++ /* Setup CRD threshold to trigger interrupt */ ++ val = FIELD_PREP(EIPR93_PE_CDR_THRESH, EIP93_RING_NUM - EIP93_RING_BUSY); ++ /* ++ * Configure RDR interrupt to be triggered if RD counter is not 0 ++ * for more than 2^(N+10) system clocks. ++ */ ++ val |= FIELD_PREP(EIPR93_PE_RD_TIMEOUT, 5) | EIPR93_PE_TIMEROUT_EN; ++ writel(val, mtk->base + EIP93_REG_PE_RING_THRESH); ++} ++ ++static void eip93_desc_free(struct eip93_device *mtk) ++{ ++ writel(0, mtk->base + EIP93_REG_PE_RING_CONFIG); ++ writel(0, mtk->base + EIP93_REG_PE_CDR_BASE); ++ writel(0, mtk->base + EIP93_REG_PE_RDR_BASE); ++} ++ ++static int eip93_set_ring(struct eip93_device *mtk, struct eip93_desc_ring *ring) ++{ ++ ring->offset = sizeof(struct eip93_descriptor); ++ ring->base = dmam_alloc_coherent(mtk->dev, ++ sizeof(struct eip93_descriptor) * EIP93_RING_NUM, ++ &ring->base_dma, GFP_KERNEL); ++ if (!ring->base) ++ return -ENOMEM; ++ ++ ring->write = ring->base; ++ ring->base_end = ring->base + sizeof(struct eip93_descriptor) * (EIP93_RING_NUM - 1); ++ ring->read = ring->base; ++ ++ return 0; ++} ++ ++static int eip93_desc_init(struct eip93_device *mtk) ++{ ++ struct eip93_desc_ring *cdr = &mtk->ring->cdr; ++ struct eip93_desc_ring *rdr = &mtk->ring->rdr; ++ int ret; ++ u32 val; ++ ++ ret = eip93_set_ring(mtk, cdr); ++ if (ret) ++ return ret; ++ ++ ret = eip93_set_ring(mtk, rdr); ++ if (ret) ++ return ret; ++ ++ writel((u32 __force)cdr->base_dma, mtk->base + EIP93_REG_PE_CDR_BASE); ++ writel((u32 __force)rdr->base_dma, mtk->base + EIP93_REG_PE_RDR_BASE); ++ ++ val = FIELD_PREP(EIP93_PE_RING_SIZE, EIP93_RING_NUM - 1); ++ writel(val, mtk->base + EIP93_REG_PE_RING_CONFIG); ++ ++ atomic_set(&mtk->ring->free, EIP93_RING_NUM - 1); ++ ++ return 0; ++} ++ ++static void eip93_cleanup(struct eip93_device *mtk) ++{ ++ tasklet_kill(&mtk->ring->done_task); ++ ++ /* Clear/ack all interrupts before disable all */ ++ eip93_irq_clear(mtk, EIP93_INT_ALL); ++ eip93_irq_disable(mtk, EIP93_INT_ALL); ++ ++ writel(0, mtk->base + EIP93_REG_PE_CLOCK_CTRL); ++ ++ eip93_desc_free(mtk); ++ ++ idr_destroy(&mtk->ring->crypto_async_idr); ++} ++ ++static int eip93_crypto_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct eip93_device *mtk; ++ u32 ver, algo_flags; ++ int ret; ++ ++ mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL); ++ if (!mtk) ++ return -ENOMEM; ++ ++ mtk->dev = dev; ++ platform_set_drvdata(pdev, mtk); ++ ++ mtk->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mtk->base)) ++ return PTR_ERR(mtk->base); ++ ++ mtk->irq = platform_get_irq(pdev, 0); ++ if (mtk->irq < 0) ++ return mtk->irq; ++ ++ ret = devm_request_threaded_irq(mtk->dev, mtk->irq, eip93_irq_handler, ++ NULL, IRQF_ONESHOT, ++ dev_name(mtk->dev), mtk); ++ ++ mtk->ring = devm_kcalloc(mtk->dev, 1, sizeof(*mtk->ring), GFP_KERNEL); ++ if (!mtk->ring) ++ return -ENOMEM; ++ ++ ret = eip93_desc_init(mtk); ++ ++ if (ret) ++ return ret; ++ ++ tasklet_init(&mtk->ring->done_task, eip93_done_task, (unsigned long)mtk); ++ ++ spin_lock_init(&mtk->ring->read_lock); ++ spin_lock_init(&mtk->ring->write_lock); ++ ++ spin_lock_init(&mtk->ring->idr_lock); ++ idr_init(&mtk->ring->crypto_async_idr); ++ ++ algo_flags = readl(mtk->base + EIP93_REG_PE_OPTION_1); ++ ++ eip93_initialize(mtk, algo_flags); ++ ++ /* Init finished, enable RDR interrupt */ ++ eip93_irq_enable(mtk, EIP93_INT_RDR_THRESH); ++ ++ ret = eip93_register_algs(mtk, algo_flags); ++ if (ret) { ++ eip93_cleanup(mtk); ++ return ret; ++ } ++ ++ ver = readl(mtk->base + EIP93_REG_PE_REVISION); ++ /* EIP_EIP_NO:MAJOR_HW_REV:MINOR_HW_REV:HW_PATCH,PE(ALGO_FLAGS) */ ++ dev_info(mtk->dev, "EIP%lu:%lx:%lx:%lx,PE(0x%x:0x%x)\n", ++ FIELD_GET(EIP93_PE_REVISION_EIP_NO, ver), ++ FIELD_GET(EIP93_PE_REVISION_MAJ_HW_REV, ver), ++ FIELD_GET(EIP93_PE_REVISION_MIN_HW_REV, ver), ++ FIELD_GET(EIP93_PE_REVISION_HW_PATCH, ver), ++ algo_flags, ++ readl(mtk->base + EIP93_REG_PE_OPTION_0)); ++ ++ return 0; ++} ++ ++static void eip93_crypto_remove(struct platform_device *pdev) ++{ ++ struct eip93_device *mtk = platform_get_drvdata(pdev); ++ ++ eip93_unregister_algs(ARRAY_SIZE(eip93_algs)); ++ eip93_cleanup(mtk); ++} ++ ++static const struct of_device_id eip93_crypto_of_match[] = { ++ { .compatible = "inside-secure,safexcel-eip93i", }, ++ { .compatible = "inside-secure,safexcel-eip93ie", }, ++ { .compatible = "inside-secure,safexcel-eip93is", }, ++ { .compatible = "inside-secure,safexcel-eip93ies", }, ++ /* IW not supported currently, missing AES-XCB-MAC/AES-CCM */ ++ /* { .compatible = "inside-secure,safexcel-eip93iw", }, */ ++ {} ++}; ++MODULE_DEVICE_TABLE(of, eip93_crypto_of_match); ++ ++static struct platform_driver eip93_crypto_driver = { ++ .probe = eip93_crypto_probe, ++ .remove_new = eip93_crypto_remove, ++ .driver = { ++ .name = "mtk-eip93", ++ .of_match_table = eip93_crypto_of_match, ++ }, ++}; ++module_platform_driver(eip93_crypto_driver); ++ ++MODULE_AUTHOR("Richard van Schagen "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Mediatek EIP-93 crypto engine driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-main.h +@@ -0,0 +1,155 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi ++#include ++#include ++#include ++#include ++#include ++ ++#include "eip93-regs.h" ++ ++#define EIP93_RING_BUSY_DELAY 500 ++ ++#define EIP93_RING_NUM 512 ++#define EIP93_RING_BUSY 32 ++#define EIP93_CRA_PRIORITY 1500 ++ ++#define EIP93_RING_SA_STATE_ADDR(base, idx) ((base) + (idx)) ++#define EIP93_RING_SA_STATE_DMA(dma_base, idx) ((u32 __force)(dma_base) + \ ++ ((idx) * sizeof(struct sa_state))) ++ ++/* cipher algorithms */ ++#define EIP93_ALG_DES BIT(0) ++#define EIP93_ALG_3DES BIT(1) ++#define EIP93_ALG_AES BIT(2) ++#define EIP93_ALG_MASK GENMASK(2, 0) ++/* hash and hmac algorithms */ ++#define EIP93_HASH_MD5 BIT(3) ++#define EIP93_HASH_SHA1 BIT(4) ++#define EIP93_HASH_SHA224 BIT(5) ++#define EIP93_HASH_SHA256 BIT(6) ++#define EIP93_HASH_HMAC BIT(7) ++#define EIP93_HASH_MASK GENMASK(6, 3) ++/* cipher modes */ ++#define EIP93_MODE_CBC BIT(8) ++#define EIP93_MODE_ECB BIT(9) ++#define EIP93_MODE_CTR BIT(10) ++#define EIP93_MODE_RFC3686 BIT(11) ++#define EIP93_MODE_MASK GENMASK(10, 8) ++ ++/* cipher encryption/decryption operations */ ++#define EIP93_ENCRYPT BIT(12) ++#define EIP93_DECRYPT BIT(13) ++ ++#define EIP93_BUSY BIT(14) ++ ++/* descriptor flags */ ++#define EIP93_DESC_DMA_IV BIT(0) ++#define EIP93_DESC_IPSEC BIT(1) ++#define EIP93_DESC_FINISH BIT(2) ++#define EIP93_DESC_LAST BIT(3) ++#define EIP93_DESC_FAKE_HMAC BIT(4) ++#define EIP93_DESC_PRNG BIT(5) ++#define EIP93_DESC_HASH BIT(6) ++#define EIP93_DESC_AEAD BIT(7) ++#define EIP93_DESC_SKCIPHER BIT(8) ++#define EIP93_DESC_ASYNC BIT(9) ++ ++#define IS_DMA_IV(desc_flags) ((desc_flags) & EIP93_DESC_DMA_IV) ++ ++#define IS_DES(flags) ((flags) & EIP93_ALG_DES) ++#define IS_3DES(flags) ((flags) & EIP93_ALG_3DES) ++#define IS_AES(flags) ((flags) & EIP93_ALG_AES) ++ ++#define IS_HASH_MD5(flags) ((flags) & EIP93_HASH_MD5) ++#define IS_HASH_SHA1(flags) ((flags) & EIP93_HASH_SHA1) ++#define IS_HASH_SHA224(flags) ((flags) & EIP93_HASH_SHA224) ++#define IS_HASH_SHA256(flags) ((flags) & EIP93_HASH_SHA256) ++#define IS_HMAC(flags) ((flags) & EIP93_HASH_HMAC) ++ ++#define IS_CBC(mode) ((mode) & EIP93_MODE_CBC) ++#define IS_ECB(mode) ((mode) & EIP93_MODE_ECB) ++#define IS_CTR(mode) ((mode) & EIP93_MODE_CTR) ++#define IS_RFC3686(mode) ((mode) & EIP93_MODE_RFC3686) ++ ++#define IS_BUSY(flags) ((flags) & EIP93_BUSY) ++ ++#define IS_ENCRYPT(dir) ((dir) & EIP93_ENCRYPT) ++#define IS_DECRYPT(dir) ((dir) & EIP93_DECRYPT) ++ ++#define IS_CIPHER(flags) ((flags) & (EIP93_ALG_DES | \ ++ EIP93_ALG_3DES | \ ++ EIP93_ALG_AES)) ++ ++#define IS_HASH(flags) ((flags) & (EIP93_HASH_MD5 | \ ++ EIP93_HASH_SHA1 | \ ++ EIP93_HASH_SHA224 | \ ++ EIP93_HASH_SHA256)) ++ ++/** ++ * struct eip93_device - crypto engine device structure ++ */ ++struct eip93_device { ++ void __iomem *base; ++ struct device *dev; ++ struct clk *clk; ++ int irq; ++ struct eip93_ring *ring; ++}; ++ ++struct eip93_desc_ring { ++ void *base; ++ void *base_end; ++ dma_addr_t base_dma; ++ /* write and read pointers */ ++ void *read; ++ void *write; ++ /* descriptor element offset */ ++ u32 offset; ++}; ++ ++struct eip93_state_pool { ++ void *base; ++ dma_addr_t base_dma; ++}; ++ ++struct eip93_ring { ++ struct tasklet_struct done_task; ++ /* command/result rings */ ++ struct eip93_desc_ring cdr; ++ struct eip93_desc_ring rdr; ++ spinlock_t write_lock; ++ spinlock_t read_lock; ++ atomic_t free; ++ /* aync idr */ ++ spinlock_t idr_lock; ++ struct idr crypto_async_idr; ++}; ++ ++enum eip93_alg_type { ++ EIP93_ALG_TYPE_AEAD, ++ EIP93_ALG_TYPE_SKCIPHER, ++ EIP93_ALG_TYPE_HASH, ++}; ++ ++struct eip93_alg_template { ++ struct eip93_device *mtk; ++ enum eip93_alg_type type; ++ u32 flags; ++ union { ++ struct aead_alg aead; ++ struct skcipher_alg skcipher; ++ struct ahash_alg ahash; ++ } alg; ++}; ++ ++#endif /* _EIP93_MAIN_H_ */ +--- /dev/null ++++ b/drivers/crypto/inside-secure/eip93/eip93-regs.h +@@ -0,0 +1,335 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2019 - 2021 ++ * ++ * Richard van Schagen ++ * Christian Marangi +Date: Tue, 10 Mar 2026 16:16:03 +0100 +Subject: [PATCH] pinctrl: airoha: permit GPIO43-46 for PHY LED0 + +On some board it was found that also the GPIO pin range from 43 to 46 can be +used for PHY LED0. Add these additional GPIO pins to the function groups. + +Signed-off-by: Christian Marangi +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 48 +++++++++++++++++++++-- + 1 file changed, 44 insertions(+), 4 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -904,13 +904,21 @@ static const char *const pwm_groups[] = + "gpio44", "gpio45", + "gpio46", "gpio47" }; + static const char *const phy1_led0_groups[] = { "gpio33", "gpio34", +- "gpio35", "gpio42" }; ++ "gpio35", "gpio42", ++ "gpio43", "gpio44", ++ "gpio45", "gpio46" }; + static const char *const phy2_led0_groups[] = { "gpio33", "gpio34", +- "gpio35", "gpio42" }; ++ "gpio35", "gpio42", ++ "gpio43", "gpio44", ++ "gpio45", "gpio46" }; + static const char *const phy3_led0_groups[] = { "gpio33", "gpio34", +- "gpio35", "gpio42" }; ++ "gpio35", "gpio42", ++ "gpio43", "gpio44", ++ "gpio45", "gpio46" }; + static const char *const phy4_led0_groups[] = { "gpio33", "gpio34", +- "gpio35", "gpio42" }; ++ "gpio35", "gpio42", ++ "gpio43", "gpio44", ++ "gpio45", "gpio46" }; + static const char *const phy1_led1_groups[] = { "gpio43", "gpio44", + "gpio45", "gpio46" }; + static const char *const phy2_led1_groups[] = { "gpio43", "gpio44", +@@ -1548,6 +1556,14 @@ static const struct airoha_pinctrl_func_ + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), + AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio43", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio44", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio45", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(0)), ++ AIROHA_PINCTRL_PHY_LED0("gpio46", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(0)), + }; + + static const struct airoha_pinctrl_func_group phy2_led0_func_group[] = { +@@ -1559,6 +1575,14 @@ static const struct airoha_pinctrl_func_ + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(1)), + AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio43", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio44", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio45", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(1)), ++ AIROHA_PINCTRL_PHY_LED0("gpio46", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(1)), + }; + + static const struct airoha_pinctrl_func_group phy3_led0_func_group[] = { +@@ -1570,6 +1594,14 @@ static const struct airoha_pinctrl_func_ + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), + AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio43", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio44", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio45", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(2)), ++ AIROHA_PINCTRL_PHY_LED0("gpio46", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(2)), + }; + + static const struct airoha_pinctrl_func_group phy4_led0_func_group[] = { +@@ -1581,6 +1613,14 @@ static const struct airoha_pinctrl_func_ + LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), + AIROHA_PINCTRL_PHY_LED0("gpio42", GPIO_LAN3_LED0_MODE_MASK, + LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio43", GPIO_LAN0_LED0_MODE_MASK, ++ LAN0_LED_MAPPING_MASK, LAN0_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio44", GPIO_LAN1_LED0_MODE_MASK, ++ LAN1_LED_MAPPING_MASK, LAN1_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio45", GPIO_LAN2_LED0_MODE_MASK, ++ LAN2_LED_MAPPING_MASK, LAN2_PHY_LED_MAP(3)), ++ AIROHA_PINCTRL_PHY_LED0("gpio46", GPIO_LAN3_LED0_MODE_MASK, ++ LAN3_LED_MAPPING_MASK, LAN3_PHY_LED_MAP(3)), + }; + + static const struct airoha_pinctrl_func_group phy1_led1_func_group[] = { diff --git a/target/linux/airoha/patches-6.12/220-04-dt-bindings-soc-Add-bindings-for-Airoha-SCU-Serdes-l.patch b/target/linux/airoha/patches-6.12/220-04-dt-bindings-soc-Add-bindings-for-Airoha-SCU-Serdes-l.patch new file mode 100644 index 00000000000..3fa8d6d5909 --- /dev/null +++ b/target/linux/airoha/patches-6.12/220-04-dt-bindings-soc-Add-bindings-for-Airoha-SCU-Serdes-l.patch @@ -0,0 +1,35 @@ +From 39537b6b334dfac001aba395229adb9318627463 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 28 Oct 2025 12:09:57 +0100 +Subject: [PATCH 04/10] dt-bindings: soc: Add bindings for Airoha SCU Serdes + lines + +The Airoha AN7581 SoC have can configure the SCU serdes lines for +multiple purpose. For example the Serdes for the USB1 port can be both +used for USB 3.0 operation or for Ethernet. Or the USB2 serdes can both +used for USB 3.0 operation or for PCIe. + +Add bindings to permit correct reference of the different ports in DT, +mostly to differenciate the different supported modes internally to the +drivers. + +Signed-off-by: Christian Marangi +--- + include/dt-bindings/soc/airoha,scu-ssr.h | 11 +++++++++++ + 1 file changed, 11 insertions(+) + create mode 100644 include/dt-bindings/soc/airoha,scu-ssr.h + +--- /dev/null ++++ b/include/dt-bindings/soc/airoha,scu-ssr.h +@@ -0,0 +1,11 @@ ++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ ++ ++#ifndef __DT_BINDINGS_AIROHA_SCU_SSR_H ++#define __DT_BINDINGS_AIROHA_SCU_SSR_H ++ ++#define AIROHA_SCU_SERDES_WIFI1 0 ++#define AIROHA_SCU_SERDES_WIFI2 1 ++#define AIROHA_SCU_SERDES_USB1 2 ++#define AIROHA_SCU_SERDES_USB2 3 ++ ++#endif /* __DT_BINDINGS_AIROHA_SCU_SSR_H */ diff --git a/target/linux/airoha/patches-6.12/220-05-dt-bindings-phy-Add-documentation-for-Airoha-AN7581-.patch b/target/linux/airoha/patches-6.12/220-05-dt-bindings-phy-Add-documentation-for-Airoha-AN7581-.patch new file mode 100644 index 00000000000..1937a692eed --- /dev/null +++ b/target/linux/airoha/patches-6.12/220-05-dt-bindings-phy-Add-documentation-for-Airoha-AN7581-.patch @@ -0,0 +1,141 @@ +From e0095e21dd9179250c304d6df2643e9a50d48edb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Feb 2025 13:25:28 +0100 +Subject: [PATCH 05/10] dt-bindings: phy: Add documentation for Airoha AN7581 + USB PHY + +Add documentation for Airoha AN7581 USB PHY that describe the USB PHY +for the USB controller. + +Airoha AN7581 SoC support a maximum of 2 USB port. The USB 2.0 mode is +always supported. The USB 3.0 mode is optional and depends on the Serdes +mode currently configured on the system for the USB port. + +If the airoha,serdes-port property is not declared, it's assumed USB 3.0 +mode is not supported, as the Serdes mode can't be validated. + +Signed-off-by: Christian Marangi +--- + .../bindings/phy/airoha,an7581-usb-phy.yaml | 83 +++++++++++++++++++ + MAINTAINERS | 7 ++ + .../dt-bindings/phy/airoha,an7581-usb-phy.h | 11 +++ + 3 files changed, 101 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/airoha,an7581-usb-phy.yaml + create mode 100644 include/dt-bindings/phy/airoha,an7581-usb-phy.h + +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/airoha,an7581-usb-phy.yaml +@@ -0,0 +1,83 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/airoha,an7581-usb-phy.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Airoha AN7581 SoC USB PHY ++ ++maintainers: ++ - Christian Marangi ++ ++description: > ++ The Airoha AN7581 SoC USB PHY describes the USB PHY for the USB controller. ++ ++ Airoha AN7581 SoC support a maximum of 2 USB port. The USB 2.0 mode is ++ always supported. The USB 3.0 mode is optional and depends on the Serdes ++ mode currently configured on the system for the USB port. ++ ++ If the airoha,serdes-port property is not declared, it's assumed USB 3.0 ++ mode is not supported, as the Serdes mode can't be validated. ++ ++properties: ++ compatible: ++ const: airoha,an7581-usb-phy ++ ++ reg: ++ maxItems: 1 ++ ++ ++ airoha,usb2-monitor-clk-sel: ++ description: Describe what oscillator across the available 4 ++ should be selected for USB 2.0 Slew Rate calibration. ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ enum: [0, 1, 2, 3] ++ ++ airoha,serdes-port: ++ description: Describe what Serdes Port is attached to the USB 3.0 port. ++ $ref: /schemas/types.yaml#/definitions/uint32 ++ enum: [0, 1, 2, 3] ++ ++ airoha,scu: ++ description: Phandle to the SCU node for USB 3.0 Serdes mode validation. ++ $ref: /schemas/types.yaml#/definitions/phandle ++ ++ '#phy-cells': ++ const: 1 ++ ++required: ++ - compatible ++ - reg ++ - airoha,usb2-monitor-clk-sel ++ - '#phy-cells' ++ ++dependentRequired: ++ airoha,serdes-port: [ 'airoha,scu' ] ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #include ++ ++ phy@1fac0000 { ++ compatible = "airoha,an7581-usb-phy"; ++ reg = <0x1fac0000 0x10000>; ++ ++ airoha,usb2-monitor-clk-sel = ; ++ airoha,scu = <&scu>; ++ airoha,serdes-port = ; ++ ++ #phy-cells = <1>; ++ }; ++ ++ phy@1fae0000 { ++ compatible = "airoha,an7581-usb-phy"; ++ reg = <0x1fae0000 0x10000>; ++ ++ airoha,usb2-monitor-clk-sel = ; ++ ++ #phy-cells = <1>; ++ }; ++ +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -751,6 +751,13 @@ S: Maintained + F: Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml + F: drivers/spi/spi-airoha-snfi.c + ++AIROHA USB PHY DRIVER ++M: Christian Marangi ++L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) ++S: Maintained ++F: Documentation/devicetree/bindings/phy/airoha,an7581-usb-phy.yaml ++F: include/dt-bindings/phy/airoha,an7581-usb-phy.h ++ + AIRSPY MEDIA DRIVER + L: linux-media@vger.kernel.org + S: Orphan +--- /dev/null ++++ b/include/dt-bindings/phy/airoha,an7581-usb-phy.h +@@ -0,0 +1,11 @@ ++/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ ++ ++#ifndef _DT_BINDINGS_AIROHA_AN7581_USB_PHY_H_ ++#define _DT_BINDINGS_AIROHA_AN7581_USB_PHY_H_ ++ ++#define AIROHA_USB2_MONCLK_SEL0 0 ++#define AIROHA_USB2_MONCLK_SEL1 1 ++#define AIROHA_USB2_MONCLK_SEL2 2 ++#define AIROHA_USB2_MONCLK_SEL3 3 ++ ++#endif diff --git a/target/linux/airoha/patches-6.12/220-06-phy-move-Airoha-PCIe-PHY-driver-to-dedicated-directo.patch b/target/linux/airoha/patches-6.12/220-06-phy-move-Airoha-PCIe-PHY-driver-to-dedicated-directo.patch new file mode 100644 index 00000000000..e61de0441e3 --- /dev/null +++ b/target/linux/airoha/patches-6.12/220-06-phy-move-Airoha-PCIe-PHY-driver-to-dedicated-directo.patch @@ -0,0 +1,1885 @@ +From 1bfe1cc581ffba2462580496507497840aa018aa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 19 Mar 2025 15:23:50 +0100 +Subject: [PATCH 06/10] phy: move Airoha PCIe PHY driver to dedicated directory + +To keep the generic PHY directory tidy, move the PCIe PHY driver to a +dedicated directory. + +This is also in preparation for support of the Airoha USB PHY driver. + +Signed-off-by: Christian Marangi +--- + MAINTAINERS | 4 +- + drivers/phy/Kconfig | 11 +- + drivers/phy/Makefile | 5 +- + drivers/phy/airoha/Kconfig | 13 + + drivers/phy/airoha/Makefile | 3 + + drivers/phy/airoha/phy-airoha-pcie-regs.h | 494 ++++++++ + drivers/phy/airoha/phy-airoha-pcie.c | 1290 +++++++++++++++++++++ + 7 files changed, 1806 insertions(+), 14 deletions(-) + create mode 100644 drivers/phy/airoha/Kconfig + create mode 100644 drivers/phy/airoha/Makefile + create mode 100644 drivers/phy/airoha/phy-airoha-pcie-regs.h + create mode 100644 drivers/phy/airoha/phy-airoha-pcie.c + +# diff --git a/MAINTAINERS b/MAINTAINERS +# index 2468f4fea5b7..3f930a613658 100644 +# --- a/MAINTAINERS +# +++ b/MAINTAINERS +# @@ -733,8 +733,8 @@ M: Lorenzo Bianconi +# L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +# S: Maintained +# F: Documentation/devicetree/bindings/phy/airoha,en7581-pcie-phy.yaml +# -F: drivers/phy/phy-airoha-pcie-regs.h +# -F: drivers/phy/phy-airoha-pcie.c +# +F: drivers/phy/airoha/phy-airoha-pcie-regs.h +# +F: drivers/phy/airoha/phy-airoha-pcie.c + +# AIROHA SPI SNFI DRIVER +# M: Lorenzo Bianconi +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -72,16 +72,7 @@ config PHY_CAN_TRANSCEIVER + functional modes using gpios and sets the attribute max link + rate, for CAN drivers. + +-config PHY_AIROHA_PCIE +- tristate "Airoha PCIe-PHY Driver" +- depends on ARCH_AIROHA || COMPILE_TEST +- depends on OF +- select GENERIC_PHY +- help +- Say Y here to add support for Airoha PCIe PHY driver. +- This driver create the basic PHY instance and provides initialize +- callback for PCIe GEN3 port. +- ++source "drivers/phy/airoha/Kconfig" + source "drivers/phy/allwinner/Kconfig" + source "drivers/phy/amlogic/Kconfig" + source "drivers/phy/broadcom/Kconfig" +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -10,8 +10,8 @@ obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy + obj-$(CONFIG_PHY_XGENE) += phy-xgene.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o + obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o +-obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o +-obj-y += allwinner/ \ ++obj-y += airoha/ \ ++ allwinner/ \ + amlogic/ \ + broadcom/ \ + cadence/ \ +--- /dev/null ++++ b/drivers/phy/airoha/Kconfig +@@ -0,0 +1,13 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# Phy drivers for Airoha devices ++# ++config PHY_AIROHA_PCIE ++ tristate "Airoha PCIe-PHY Driver" ++ depends on ARCH_AIROHA || COMPILE_TEST ++ depends on OF ++ select GENERIC_PHY ++ help ++ Say Y here to add support for Airoha PCIe PHY driver. ++ This driver create the basic PHY instance and provides initialize ++ callback for PCIe GEN3 port. +--- /dev/null ++++ b/drivers/phy/airoha/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o +--- /dev/null ++++ b/drivers/phy/airoha/phy-airoha-pcie-regs.h +@@ -0,0 +1,494 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#ifndef _PHY_AIROHA_PCIE_H ++#define _PHY_AIROHA_PCIE_H ++ ++/* CSR_2L */ ++#define REG_CSR_2L_CMN 0x0000 ++#define CSR_2L_PXP_CMN_LANE_EN BIT(0) ++#define CSR_2L_PXP_CMN_TRIM_MASK GENMASK(28, 24) ++ ++#define REG_CSR_2L_JCPLL_IB_EXT 0x0004 ++#define REG_CSR_2L_JCPLL_LPF_SHCK_EN BIT(8) ++#define CSR_2L_PXP_JCPLL_CHP_IBIAS GENMASK(21, 16) ++#define CSR_2L_PXP_JCPLL_CHP_IOFST GENMASK(29, 24) ++ ++#define REG_CSR_2L_JCPLL_LPF_BR 0x0008 ++#define CSR_2L_PXP_JCPLL_LPF_BR GENMASK(4, 0) ++#define CSR_2L_PXP_JCPLL_LPF_BC GENMASK(12, 8) ++#define CSR_2L_PXP_JCPLL_LPF_BP GENMASK(20, 16) ++#define CSR_2L_PXP_JCPLL_LPF_BWR GENMASK(28, 24) ++ ++#define REG_CSR_2L_JCPLL_LPF_BWC 0x000c ++#define CSR_2L_PXP_JCPLL_LPF_BWC GENMASK(4, 0) ++#define CSR_2L_PXP_JCPLL_KBAND_CODE GENMASK(23, 16) ++#define CSR_2L_PXP_JCPLL_KBAND_DIV GENMASK(26, 24) ++ ++#define REG_CSR_2L_JCPLL_KBAND_KFC 0x0010 ++#define CSR_2L_PXP_JCPLL_KBAND_KFC GENMASK(1, 0) ++#define CSR_2L_PXP_JCPLL_KBAND_KF GENMASK(9, 8) ++#define CSR_2L_PXP_JCPLL_KBAND_KS GENMASK(17, 16) ++#define CSR_2L_PXP_JCPLL_POSTDIV_EN BIT(24) ++ ++#define REG_CSR_2L_JCPLL_MMD_PREDIV_MODE 0x0014 ++#define CSR_2L_PXP_JCPLL_MMD_PREDIV_MODE GENMASK(1, 0) ++#define CSR_2L_PXP_JCPLL_POSTDIV_D2 BIT(16) ++#define CSR_2L_PXP_JCPLL_POSTDIV_D5 BIT(24) ++ ++#define CSR_2L_PXP_JCPLL_MONCK 0x0018 ++#define CSR_2L_PXP_JCPLL_REFIN_DIV GENMASK(25, 24) ++ ++#define REG_CSR_2L_JCPLL_RST_DLY 0x001c ++#define CSR_2L_PXP_JCPLL_RST_DLY GENMASK(2, 0) ++#define CSR_2L_PXP_JCPLL_RST BIT(8) ++#define CSR_2L_PXP_JCPLL_SDM_DI_EN BIT(16) ++#define CSR_2L_PXP_JCPLL_SDM_DI_LS GENMASK(25, 24) ++ ++#define REG_CSR_2L_JCPLL_SDM_IFM 0x0020 ++#define CSR_2L_PXP_JCPLL_SDM_IFM BIT(0) ++ ++#define REG_CSR_2L_JCPLL_SDM_HREN 0x0024 ++#define CSR_2L_PXP_JCPLL_SDM_HREN BIT(0) ++#define CSR_2L_PXP_JCPLL_TCL_AMP_EN BIT(8) ++#define CSR_2L_PXP_JCPLL_TCL_AMP_GAIN GENMASK(18, 16) ++#define CSR_2L_PXP_JCPLL_TCL_AMP_VREF GENMASK(28, 24) ++ ++#define REG_CSR_2L_JCPLL_TCL_CMP 0x0028 ++#define CSR_2L_PXP_JCPLL_TCL_LPF_EN BIT(16) ++#define CSR_2L_PXP_JCPLL_TCL_LPF_BW GENMASK(26, 24) ++ ++#define REG_CSR_2L_JCPLL_VCODIV 0x002c ++#define CSR_2L_PXP_JCPLL_VCO_CFIX GENMASK(9, 8) ++#define CSR_2L_PXP_JCPLL_VCO_HALFLSB_EN BIT(16) ++#define CSR_2L_PXP_JCPLL_VCO_SCAPWR GENMASK(26, 24) ++ ++#define REG_CSR_2L_JCPLL_VCO_TCLVAR 0x0030 ++#define CSR_2L_PXP_JCPLL_VCO_TCLVAR GENMASK(2, 0) ++ ++#define REG_CSR_2L_JCPLL_SSC 0x0038 ++#define CSR_2L_PXP_JCPLL_SSC_EN BIT(0) ++#define CSR_2L_PXP_JCPLL_SSC_PHASE_INI BIT(8) ++#define CSR_2L_PXP_JCPLL_SSC_TRI_EN BIT(16) ++ ++#define REG_CSR_2L_JCPLL_SSC_DELTA1 0x003c ++#define CSR_2L_PXP_JCPLL_SSC_DELTA1 GENMASK(15, 0) ++#define CSR_2L_PXP_JCPLL_SSC_DELTA GENMASK(31, 16) ++ ++#define REG_CSR_2L_JCPLL_SSC_PERIOD 0x0040 ++#define CSR_2L_PXP_JCPLL_SSC_PERIOD GENMASK(15, 0) ++ ++#define REG_CSR_2L_JCPLL_TCL_VTP_EN 0x004c ++#define CSR_2L_PXP_JCPLL_SPARE_LOW GENMASK(31, 24) ++ ++#define REG_CSR_2L_JCPLL_TCL_KBAND_VREF 0x0050 ++#define CSR_2L_PXP_JCPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define CSR_2L_PXP_JCPLL_VCO_KBAND_MEAS_EN BIT(24) ++ ++#define REG_CSR_2L_750M_SYS_CK 0x0054 ++#define CSR_2L_PXP_TXPLL_LPF_SHCK_EN BIT(16) ++#define CSR_2L_PXP_TXPLL_CHP_IBIAS GENMASK(29, 24) ++ ++#define REG_CSR_2L_TXPLL_CHP_IOFST 0x0058 ++#define CSR_2L_PXP_TXPLL_CHP_IOFST GENMASK(5, 0) ++#define CSR_2L_PXP_TXPLL_LPF_BR GENMASK(12, 8) ++#define CSR_2L_PXP_TXPLL_LPF_BC GENMASK(20, 16) ++#define CSR_2L_PXP_TXPLL_LPF_BP GENMASK(28, 24) ++ ++#define REG_CSR_2L_TXPLL_LPF_BWR 0x005c ++#define CSR_2L_PXP_TXPLL_LPF_BWR GENMASK(4, 0) ++#define CSR_2L_PXP_TXPLL_LPF_BWC GENMASK(12, 8) ++#define CSR_2L_PXP_TXPLL_KBAND_CODE GENMASK(31, 24) ++ ++#define REG_CSR_2L_TXPLL_KBAND_DIV 0x0060 ++#define CSR_2L_PXP_TXPLL_KBAND_DIV GENMASK(2, 0) ++#define CSR_2L_PXP_TXPLL_KBAND_KFC GENMASK(9, 8) ++#define CSR_2L_PXP_TXPLL_KBAND_KF GENMASK(17, 16) ++#define CSR_2L_PXP_txpll_KBAND_KS GENMASK(25, 24) ++ ++#define REG_CSR_2L_TXPLL_POSTDIV 0x0064 ++#define CSR_2L_PXP_TXPLL_POSTDIV_EN BIT(0) ++#define CSR_2L_PXP_TXPLL_MMD_PREDIV_MODE GENMASK(9, 8) ++#define CSR_2L_PXP_TXPLL_PHY_CK1_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_PHY_CK2 0x0068 ++#define CSR_2L_PXP_TXPLL_REFIN_INTERNAL BIT(24) ++ ++#define REG_CSR_2L_TXPLL_REFIN_DIV 0x006c ++#define CSR_2L_PXP_TXPLL_REFIN_DIV GENMASK(1, 0) ++#define CSR_2L_PXP_TXPLL_RST_DLY GENMASK(10, 8) ++#define CSR_2L_PXP_TXPLL_PLL_RSTB BIT(16) ++ ++#define REG_CSR_2L_TXPLL_SDM_DI_LS 0x0070 ++#define CSR_2L_PXP_TXPLL_SDM_DI_LS GENMASK(1, 0) ++#define CSR_2L_PXP_TXPLL_SDM_IFM BIT(8) ++#define CSR_2L_PXP_TXPLL_SDM_ORD GENMASK(25, 24) ++ ++#define REG_CSR_2L_TXPLL_SDM_OUT 0x0074 ++#define CSR_2L_PXP_TXPLL_TCL_AMP_EN BIT(16) ++#define CSR_2L_PXP_TXPLL_TCL_AMP_GAIN GENMASK(26, 24) ++ ++#define REG_CSR_2L_TXPLL_TCL_AMP_VREF 0x0078 ++#define CSR_2L_PXP_TXPLL_TCL_AMP_VREF GENMASK(4, 0) ++#define CSR_2L_PXP_TXPLL_TCL_LPF_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_TCL_LPF_BW 0x007c ++#define CSR_2L_PXP_TXPLL_TCL_LPF_BW GENMASK(2, 0) ++#define CSR_2L_PXP_TXPLL_VCO_CFIX GENMASK(17, 16) ++#define CSR_2L_PXP_TXPLL_VCO_HALFLSB_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_VCO_SCAPWR 0x0080 ++#define CSR_2L_PXP_TXPLL_VCO_SCAPWR GENMASK(2, 0) ++ ++#define REG_CSR_2L_TXPLL_SSC 0x0084 ++#define CSR_2L_PXP_TXPLL_SSC_EN BIT(0) ++#define CSR_2L_PXP_TXPLL_SSC_PHASE_INI BIT(8) ++ ++#define REG_CSR_2L_TXPLL_SSC_DELTA1 0x0088 ++#define CSR_2L_PXP_TXPLL_SSC_DELTA1 GENMASK(15, 0) ++#define CSR_2L_PXP_TXPLL_SSC_DELTA GENMASK(31, 16) ++ ++#define REG_CSR_2L_TXPLL_SSC_PERIOD 0x008c ++#define CSR_2L_PXP_txpll_SSC_PERIOD GENMASK(15, 0) ++ ++#define REG_CSR_2L_TXPLL_VTP 0x0090 ++#define CSR_2L_PXP_TXPLL_VTP_EN BIT(0) ++ ++#define REG_CSR_2L_TXPLL_TCL_VTP 0x0098 ++#define CSR_2L_PXP_TXPLL_SPARE_L GENMASK(31, 24) ++ ++#define REG_CSR_2L_TXPLL_TCL_KBAND_VREF 0x009c ++#define CSR_2L_PXP_TXPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define CSR_2L_PXP_TXPLL_VCO_KBAND_MEAS_EN BIT(24) ++ ++#define REG_CSR_2L_TXPLL_POSTDIV_D256 0x00a0 ++#define CSR_2L_PXP_CLKTX0_AMP GENMASK(10, 8) ++#define CSR_2L_PXP_CLKTX0_OFFSET GENMASK(17, 16) ++#define CSR_2L_PXP_CLKTX0_SR GENMASK(25, 24) ++ ++#define REG_CSR_2L_CLKTX0_FORCE_OUT1 0x00a4 ++#define CSR_2L_PXP_CLKTX0_HZ BIT(8) ++#define CSR_2L_PXP_CLKTX0_IMP_SEL GENMASK(20, 16) ++#define CSR_2L_PXP_CLKTX1_AMP GENMASK(26, 24) ++ ++#define REG_CSR_2L_CLKTX1_OFFSET 0x00a8 ++#define CSR_2L_PXP_CLKTX1_OFFSET GENMASK(1, 0) ++#define CSR_2L_PXP_CLKTX1_SR GENMASK(9, 8) ++#define CSR_2L_PXP_CLKTX1_HZ BIT(24) ++ ++#define REG_CSR_2L_CLKTX1_IMP_SEL 0x00ac ++#define CSR_2L_PXP_CLKTX1_IMP_SEL GENMASK(4, 0) ++ ++#define REG_CSR_2L_PLL_CMN_RESERVE0 0x00b0 ++#define CSR_2L_PXP_PLL_RESERVE_MASK GENMASK(15, 0) ++ ++#define REG_CSR_2L_TX0_CKLDO 0x00cc ++#define CSR_2L_PXP_TX0_CKLDO_EN BIT(0) ++#define CSR_2L_PXP_TX0_DMEDGEGEN_EN BIT(24) ++ ++#define REG_CSR_2L_TX1_CKLDO 0x00e8 ++#define CSR_2L_PXP_TX1_CKLDO_EN BIT(0) ++#define CSR_2L_PXP_TX1_DMEDGEGEN_EN BIT(24) ++ ++#define REG_CSR_2L_TX1_MULTLANE 0x00ec ++#define CSR_2L_PXP_TX1_MULTLANE_EN BIT(0) ++ ++#define REG_CSR_2L_RX0_REV0 0x00fc ++#define CSR_2L_PXP_VOS_PNINV GENMASK(19, 18) ++#define CSR_2L_PXP_FE_GAIN_NORMAL_MODE GENMASK(22, 20) ++#define CSR_2L_PXP_FE_GAIN_TRAIN_MODE GENMASK(26, 24) ++ ++#define REG_CSR_2L_RX0_PHYCK_DIV 0x0100 ++#define CSR_2L_PXP_RX0_PHYCK_SEL GENMASK(9, 8) ++#define CSR_2L_PXP_RX0_PHYCK_RSTB BIT(16) ++#define CSR_2L_PXP_RX0_TDC_CK_SEL BIT(24) ++ ++#define REG_CSR_2L_CDR0_PD_PICAL_CKD8_INV 0x0104 ++#define CSR_2L_PXP_CDR0_PD_EDGE_DISABLE BIT(8) ++ ++#define REG_CSR_2L_CDR0_LPF_RATIO 0x0110 ++#define CSR_2L_PXP_CDR0_LPF_TOP_LIM GENMASK(26, 8) ++ ++#define REG_CSR_2L_CDR0_PR_INJ_MODE 0x011c ++#define CSR_2L_PXP_CDR0_INJ_FORCE_OFF BIT(24) ++ ++#define REG_CSR_2L_CDR0_PR_BETA_DAC 0x0120 ++#define CSR_2L_PXP_CDR0_PR_BETA_SEL GENMASK(19, 16) ++#define CSR_2L_PXP_CDR0_PR_KBAND_DIV GENMASK(26, 24) ++ ++#define REG_CSR_2L_CDR0_PR_VREG_IBAND 0x0124 ++#define CSR_2L_PXP_CDR0_PR_VREG_IBAND GENMASK(2, 0) ++#define CSR_2L_PXP_CDR0_PR_VREG_CKBUF GENMASK(10, 8) ++ ++#define REG_CSR_2L_CDR0_PR_CKREF_DIV 0x0128 ++#define CSR_2L_PXP_CDR0_PR_CKREF_DIV GENMASK(1, 0) ++ ++#define REG_CSR_2L_CDR0_PR_MONCK 0x012c ++#define CSR_2L_PXP_CDR0_PR_MONCK_ENABLE BIT(0) ++#define CSR_2L_PXP_CDR0_PR_RESERVE0 GENMASK(19, 16) ++ ++#define REG_CSR_2L_CDR0_PR_COR_HBW 0x0130 ++#define CSR_2L_PXP_CDR0_PR_LDO_FORCE_ON BIT(8) ++#define CSR_2L_PXP_CDR0_PR_CKREF_DIV1 GENMASK(17, 16) ++ ++#define REG_CSR_2L_CDR0_PR_MONPI 0x0134 ++#define CSR_2L_PXP_CDR0_PR_XFICK_EN BIT(8) ++ ++#define REG_CSR_2L_RX0_SIGDET_DCTEST 0x0140 ++#define CSR_2L_PXP_RX0_SIGDET_LPF_CTRL GENMASK(9, 8) ++#define CSR_2L_PXP_RX0_SIGDET_PEAK GENMASK(25, 24) ++ ++#define REG_CSR_2L_RX0_SIGDET_VTH_SEL 0x0144 ++#define CSR_2L_PXP_RX0_SIGDET_VTH_SEL GENMASK(4, 0) ++#define CSR_2L_PXP_RX0_FE_VB_EQ1_EN BIT(24) ++ ++#define REG_CSR_2L_PXP_RX0_FE_VB_EQ2 0x0148 ++#define CSR_2L_PXP_RX0_FE_VB_EQ2_EN BIT(0) ++#define CSR_2L_PXP_RX0_FE_VB_EQ3_EN BIT(8) ++#define CSR_2L_PXP_RX0_FE_VCM_GEN_PWDB BIT(16) ++ ++#define REG_CSR_2L_PXP_RX0_OSCAL_CTLE1IOS 0x0158 ++#define CSR_2L_PXP_RX0_PR_OSCAL_VGA1IOS GENMASK(29, 24) ++ ++#define REG_CSR_2L_PXP_RX0_OSCA_VGA1VOS 0x015c ++#define CSR_2L_PXP_RX0_PR_OSCAL_VGA1VOS GENMASK(5, 0) ++#define CSR_2L_PXP_RX0_PR_OSCAL_VGA2IOS GENMASK(13, 8) ++ ++#define REG_CSR_2L_RX1_REV0 0x01b4 ++ ++#define REG_CSR_2L_RX1_PHYCK_DIV 0x01b8 ++#define CSR_2L_PXP_RX1_PHYCK_SEL GENMASK(9, 8) ++#define CSR_2L_PXP_RX1_PHYCK_RSTB BIT(16) ++#define CSR_2L_PXP_RX1_TDC_CK_SEL BIT(24) ++ ++#define REG_CSR_2L_CDR1_PD_PICAL_CKD8_INV 0x01bc ++#define CSR_2L_PXP_CDR1_PD_EDGE_DISABLE BIT(8) ++ ++#define REG_CSR_2L_CDR1_PR_BETA_DAC 0x01d8 ++#define CSR_2L_PXP_CDR1_PR_BETA_SEL GENMASK(19, 16) ++#define CSR_2L_PXP_CDR1_PR_KBAND_DIV GENMASK(26, 24) ++ ++#define REG_CSR_2L_CDR1_PR_MONCK 0x01e4 ++#define CSR_2L_PXP_CDR1_PR_MONCK_ENABLE BIT(0) ++#define CSR_2L_PXP_CDR1_PR_RESERVE0 GENMASK(19, 16) ++ ++#define REG_CSR_2L_CDR1_LPF_RATIO 0x01c8 ++#define CSR_2L_PXP_CDR1_LPF_TOP_LIM GENMASK(26, 8) ++ ++#define REG_CSR_2L_CDR1_PR_INJ_MODE 0x01d4 ++#define CSR_2L_PXP_CDR1_INJ_FORCE_OFF BIT(24) ++ ++#define REG_CSR_2L_CDR1_PR_VREG_IBAND_VAL 0x01dc ++#define CSR_2L_PXP_CDR1_PR_VREG_IBAND GENMASK(2, 0) ++#define CSR_2L_PXP_CDR1_PR_VREG_CKBUF GENMASK(10, 8) ++ ++#define REG_CSR_2L_CDR1_PR_CKREF_DIV 0x01e0 ++#define CSR_2L_PXP_CDR1_PR_CKREF_DIV GENMASK(1, 0) ++ ++#define REG_CSR_2L_CDR1_PR_COR_HBW 0x01e8 ++#define CSR_2L_PXP_CDR1_PR_LDO_FORCE_ON BIT(8) ++#define CSR_2L_PXP_CDR1_PR_CKREF_DIV1 GENMASK(17, 16) ++ ++#define REG_CSR_2L_CDR1_PR_MONPI 0x01ec ++#define CSR_2L_PXP_CDR1_PR_XFICK_EN BIT(8) ++ ++#define REG_CSR_2L_RX1_DAC_RANGE_EYE 0x01f4 ++#define CSR_2L_PXP_RX1_SIGDET_LPF_CTRL GENMASK(25, 24) ++ ++#define REG_CSR_2L_RX1_SIGDET_NOVTH 0x01f8 ++#define CSR_2L_PXP_RX1_SIGDET_PEAK GENMASK(9, 8) ++#define CSR_2L_PXP_RX1_SIGDET_VTH_SEL GENMASK(20, 16) ++ ++#define REG_CSR_2L_RX1_FE_VB_EQ1 0x0200 ++#define CSR_2L_PXP_RX1_FE_VB_EQ1_EN BIT(0) ++#define CSR_2L_PXP_RX1_FE_VB_EQ2_EN BIT(8) ++#define CSR_2L_PXP_RX1_FE_VB_EQ3_EN BIT(16) ++#define CSR_2L_PXP_RX1_FE_VCM_GEN_PWDB BIT(24) ++ ++#define REG_CSR_2L_RX1_OSCAL_VGA1IOS 0x0214 ++#define CSR_2L_PXP_RX1_PR_OSCAL_VGA1IOS GENMASK(5, 0) ++#define CSR_2L_PXP_RX1_PR_OSCAL_VGA1VOS GENMASK(13, 8) ++#define CSR_2L_PXP_RX1_PR_OSCAL_VGA2IOS GENMASK(21, 16) ++ ++/* PMA */ ++#define REG_PCIE_PMA_SS_LCPLL_PWCTL_SETTING_1 0x0004 ++#define PCIE_LCPLL_MAN_PWDB BIT(0) ++ ++#define REG_PCIE_PMA_SEQUENCE_DISB_CTRL1 0x010c ++#define PCIE_DISB_RX_SDCAL_EN BIT(0) ++ ++#define REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1 0x0114 ++#define PCIE_FORCE_RX_SDCAL_EN BIT(0) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET1 0x014c ++#define PCIE_PLL_FT_LOCK_CYCLECNT GENMASK(15, 0) ++#define PCIE_PLL_FT_UNLOCK_CYCLECNT GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET2 0x0150 ++#define PCIE_LOCK_TARGET_BEG GENMASK(15, 0) ++#define PCIE_LOCK_TARGET_END GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET3 0x0154 ++#define PCIE_UNLOCK_TARGET_BEG GENMASK(15, 0) ++#define PCIE_UNLOCK_TARGET_END GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_SS_RX_FREQ_DET4 0x0158 ++#define PCIE_FREQLOCK_DET_EN GENMASK(2, 0) ++#define PCIE_LOCK_LOCKTH GENMASK(11, 8) ++#define PCIE_UNLOCK_LOCKTH GENMASK(15, 12) ++ ++#define REG_PCIE_PMA_SS_RX_CAL1 0x0160 ++#define REG_PCIE_PMA_SS_RX_CAL2 0x0164 ++#define PCIE_CAL_OUT_OS GENMASK(11, 8) ++ ++#define REG_PCIE_PMA_SS_RX_SIGDET0 0x0168 ++#define PCIE_SIGDET_WIN_NONVLD_TIMES GENMASK(28, 24) ++ ++#define REG_PCIE_PMA_TX_RESET 0x0260 ++#define PCIE_TX_TOP_RST BIT(0) ++#define PCIE_TX_CAL_RST BIT(8) ++ ++#define REG_PCIE_PMA_RX_FORCE_MODE0 0x0294 ++#define PCIE_FORCE_DA_XPON_RX_FE_GAIN_CTRL GENMASK(1, 0) ++ ++#define REG_PCIE_PMA_SS_DA_XPON_PWDB0 0x034c ++#define PCIE_DA_XPON_CDR_PR_PWDB BIT(8) ++ ++#define REG_PCIE_PMA_SW_RESET 0x0460 ++#define PCIE_SW_RX_FIFO_RST BIT(0) ++#define PCIE_SW_RX_RST BIT(1) ++#define PCIE_SW_TX_RST BIT(2) ++#define PCIE_SW_PMA_RST BIT(3) ++#define PCIE_SW_ALLPCS_RST BIT(4) ++#define PCIE_SW_REF_RST BIT(5) ++#define PCIE_SW_TX_FIFO_RST BIT(6) ++#define PCIE_SW_XFI_TXPCS_RST BIT(7) ++#define PCIE_SW_XFI_RXPCS_RST BIT(8) ++#define PCIE_SW_XFI_RXPCS_BIST_RST BIT(9) ++#define PCIE_SW_HSG_TXPCS_RST BIT(10) ++#define PCIE_SW_HSG_RXPCS_RST BIT(11) ++#define PCIE_PMA_SW_RST (PCIE_SW_RX_FIFO_RST | \ ++ PCIE_SW_RX_RST | \ ++ PCIE_SW_TX_RST | \ ++ PCIE_SW_PMA_RST | \ ++ PCIE_SW_ALLPCS_RST | \ ++ PCIE_SW_REF_RST | \ ++ PCIE_SW_TX_FIFO_RST | \ ++ PCIE_SW_XFI_TXPCS_RST | \ ++ PCIE_SW_XFI_RXPCS_RST | \ ++ PCIE_SW_XFI_RXPCS_BIST_RST | \ ++ PCIE_SW_HSG_TXPCS_RST | \ ++ PCIE_SW_HSG_RXPCS_RST) ++ ++#define REG_PCIE_PMA_RO_RX_FREQDET 0x0530 ++#define PCIE_RO_FBCK_LOCK BIT(0) ++#define PCIE_RO_FL_OUT GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC 0x0794 ++#define PCIE_FORCE_DA_PXP_CDR_PR_IDAC GENMASK(10, 0) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_SDM_PCW 0x0798 ++#define PCIE_FORCE_DA_PXP_TXPLL_SDM_PCW GENMASK(30, 0) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_VOS 0x079c ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_SDM_PCW BIT(16) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_JCPLL_SDM_PCW 0x0800 ++#define PCIE_FORCE_DA_PXP_JCPLL_SDM_PCW GENMASK(30, 0) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PD_PWDB 0x081c ++#define PCIE_FORCE_DA_PXP_CDR_PD_PWDB BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PD_PWDB BIT(8) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C 0x0820 ++#define PCIE_FORCE_DA_PXP_CDR_PR_LPF_C_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN BIT(8) ++#define PCIE_FORCE_DA_PXP_CDR_PR_LPF_R_EN BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB 0x0824 ++#define PCIE_FORCE_DA_PXP_CDR_PR_PWDB BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT 0x0828 ++#define PCIE_FORCE_DA_PXP_JCPLL_CKOUT_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_CKOUT_EN BIT(8) ++#define PCIE_FORCE_DA_PXP_JCPLL_EN BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_EN BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_SCAN_RST 0x0084c ++#define PCIE_FORCE_DA_PXP_RX_SIGDET_PWDB BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_RX_SIGDET_PWDB BIT(24) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT 0x0854 ++#define PCIE_FORCE_DA_PXP_TXPLL_CKOUT_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_TXPLL_CKOUT_EN BIT(8) ++#define PCIE_FORCE_DA_PXP_TXPLL_EN BIT(16) ++#define PCIE_FORCE_SEL_DA_PXP_TXPLL_EN BIT(24) ++ ++#define REG_PCIE_PMA_SCAN_MODE 0x0884 ++#define PCIE_FORCE_DA_PXP_JCPLL_KBAND_LOAD_EN BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_JCPLL_KBAND_LOAD_EN BIT(8) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_13 0x08bc ++#define PCIE_FLL_IDAC_PCIEG1 GENMASK(10, 0) ++#define PCIE_FLL_IDAC_PCIEG2 GENMASK(26, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_14 0x08c0 ++#define PCIE_FLL_IDAC_PCIEG3 GENMASK(10, 0) ++#define PCIE_FLL_LOAD_EN BIT(16) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL 0x088c ++#define PCIE_FORCE_DA_PXP_RX_FE_GAIN_CTRL GENMASK(1, 0) ++#define PCIE_FORCE_SEL_DA_PXP_RX_FE_GAIN_CTRL BIT(8) ++ ++#define REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_PWDB 0x0894 ++#define PCIE_FORCE_DA_PXP_RX_FE_PWDB BIT(0) ++#define PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB BIT(8) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_12 0x08b8 ++#define PCIE_FORCE_PMA_RX_SPEED GENMASK(7, 4) ++#define PCIE_FORCE_SEL_PMA_RX_SPEED BIT(7) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_17 0x08e0 ++ ++#define REG_PCIE_PMA_DIG_RESERVE_18 0x08e4 ++#define PCIE_PXP_RX_VTH_SEL_PCIE_G1 GENMASK(4, 0) ++#define PCIE_PXP_RX_VTH_SEL_PCIE_G2 GENMASK(12, 8) ++#define PCIE_PXP_RX_VTH_SEL_PCIE_G3 GENMASK(20, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_19 0x08e8 ++#define PCIE_PCP_RX_REV0_PCIE_GEN1 GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_20 0x08ec ++#define PCIE_PCP_RX_REV0_PCIE_GEN2 GENMASK(15, 0) ++#define PCIE_PCP_RX_REV0_PCIE_GEN3 GENMASK(31, 16) ++ ++#define REG_PCIE_PMA_DIG_RESERVE_21 0x08f0 ++#define REG_PCIE_PMA_DIG_RESERVE_22 0x08f4 ++#define REG_PCIE_PMA_DIG_RESERVE_27 0x0908 ++#define REG_PCIE_PMA_DIG_RESERVE_30 0x0914 ++ ++/* DTIME */ ++#define REG_PCIE_PEXTP_DIG_GLB44 0x00 ++#define PCIE_XTP_RXDET_VCM_OFF_STB_T_SEL GENMASK(7, 0) ++#define PCIE_XTP_RXDET_EN_STB_T_SEL GENMASK(15, 8) ++#define PCIE_XTP_RXDET_FINISH_STB_T_SEL GENMASK(23, 16) ++#define PCIE_XTP_TXPD_TX_DATA_EN_DLY GENMASK(27, 24) ++#define PCIE_XTP_TXPD_RXDET_DONE_CDT BIT(28) ++#define PCIE_XTP_RXDET_LATCH_STB_T_SEL GENMASK(31, 29) ++ ++/* RX AEQ */ ++#define REG_PCIE_PEXTP_DIG_LN_RX30_P0 0x0000 ++#define PCIE_XTP_LN_RX_PDOWN_L1P2_EXIT_WAIT GENMASK(7, 0) ++#define PCIE_XTP_LN_RX_PDOWN_T2RLB_DIG_EN BIT(8) ++#define PCIE_XTP_LN_RX_PDOWN_E0_AEQEN_WAIT GENMASK(31, 16) ++ ++#define REG_PCIE_PEXTP_DIG_LN_RX30_P1 0x0100 ++ ++#endif /* _PHY_AIROHA_PCIE_H */ +--- /dev/null ++++ b/drivers/phy/airoha/phy-airoha-pcie.c +@@ -0,0 +1,1290 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "phy-airoha-pcie-regs.h" ++ ++#define LEQ_LEN_CTRL_MAX_VAL 7 ++#define FREQ_LOCK_MAX_ATTEMPT 10 ++ ++/* PCIe-PHY initialization time in ms needed by the hw to complete */ ++#define PHY_HW_INIT_TIME_MS 30 ++ ++enum airoha_pcie_port_gen { ++ PCIE_PORT_GEN1 = 1, ++ PCIE_PORT_GEN2, ++ PCIE_PORT_GEN3, ++}; ++ ++/** ++ * struct airoha_pcie_phy - PCIe phy driver main structure ++ * @dev: pointer to device ++ * @phy: pointer to generic phy ++ * @csr_2l: Analogic lane IO mapped register base address ++ * @pma0: IO mapped register base address of PMA0-PCIe ++ * @pma1: IO mapped register base address of PMA1-PCIe ++ * @p0_xr_dtime: IO mapped register base address of port0 Tx-Rx detection time ++ * @p1_xr_dtime: IO mapped register base address of port1 Tx-Rx detection time ++ * @rx_aeq: IO mapped register base address of Rx AEQ training ++ */ ++struct airoha_pcie_phy { ++ struct device *dev; ++ struct phy *phy; ++ void __iomem *csr_2l; ++ void __iomem *pma0; ++ void __iomem *pma1; ++ void __iomem *p0_xr_dtime; ++ void __iomem *p1_xr_dtime; ++ void __iomem *rx_aeq; ++}; ++ ++static void airoha_phy_clear_bits(void __iomem *reg, u32 mask) ++{ ++ u32 val = readl(reg) & ~mask; ++ ++ writel(val, reg); ++} ++ ++static void airoha_phy_set_bits(void __iomem *reg, u32 mask) ++{ ++ u32 val = readl(reg) | mask; ++ ++ writel(val, reg); ++} ++ ++static void airoha_phy_update_bits(void __iomem *reg, u32 mask, u32 val) ++{ ++ u32 tmp = readl(reg); ++ ++ tmp &= ~mask; ++ tmp |= val & mask; ++ writel(tmp, reg); ++} ++ ++#define airoha_phy_update_field(reg, mask, val) \ ++ do { \ ++ BUILD_BUG_ON_MSG(!__builtin_constant_p((mask)), \ ++ "mask is not constant"); \ ++ airoha_phy_update_bits((reg), (mask), \ ++ FIELD_PREP((mask), (val))); \ ++ } while (0) ++ ++#define airoha_phy_csr_2l_clear_bits(pcie_phy, reg, mask) \ ++ airoha_phy_clear_bits((pcie_phy)->csr_2l + (reg), (mask)) ++#define airoha_phy_csr_2l_set_bits(pcie_phy, reg, mask) \ ++ airoha_phy_set_bits((pcie_phy)->csr_2l + (reg), (mask)) ++#define airoha_phy_csr_2l_update_field(pcie_phy, reg, mask, val) \ ++ airoha_phy_update_field((pcie_phy)->csr_2l + (reg), (mask), (val)) ++#define airoha_phy_pma0_clear_bits(pcie_phy, reg, mask) \ ++ airoha_phy_clear_bits((pcie_phy)->pma0 + (reg), (mask)) ++#define airoha_phy_pma1_clear_bits(pcie_phy, reg, mask) \ ++ airoha_phy_clear_bits((pcie_phy)->pma1 + (reg), (mask)) ++#define airoha_phy_pma0_set_bits(pcie_phy, reg, mask) \ ++ airoha_phy_set_bits((pcie_phy)->pma0 + (reg), (mask)) ++#define airoha_phy_pma1_set_bits(pcie_phy, reg, mask) \ ++ airoha_phy_set_bits((pcie_phy)->pma1 + (reg), (mask)) ++#define airoha_phy_pma0_update_field(pcie_phy, reg, mask, val) \ ++ airoha_phy_update_field((pcie_phy)->pma0 + (reg), (mask), (val)) ++#define airoha_phy_pma1_update_field(pcie_phy, reg, mask, val) \ ++ airoha_phy_update_field((pcie_phy)->pma1 + (reg), (mask), (val)) ++ ++static void ++airoha_phy_init_lane0_rx_fw_pre_calib(struct airoha_pcie_phy *pcie_phy, ++ enum airoha_pcie_port_gen gen) ++{ ++ u32 fl_out_target = gen == PCIE_PORT_GEN3 ? 41600 : 41941; ++ u32 lock_cyclecnt = gen == PCIE_PORT_GEN3 ? 26000 : 32767; ++ u32 pr_idac, val, cdr_pr_idac_tmp = 0; ++ int i; ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_SS_LCPLL_PWCTL_SETTING_1, ++ PCIE_LCPLL_MAN_PWDB); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_LOCK_CYCLECNT, lock_cyclecnt); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_LOCK_LOCKTH, 0x3); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_UNLOCK_CYCLECNT, ++ lock_cyclecnt); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_UNLOCK_LOCKTH, 0x3); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR0_PR_INJ_MODE, ++ CSR_2L_PXP_CDR0_INJ_FORCE_OFF); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ ++ for (i = 0; i < LEQ_LEN_CTRL_MAX_VAL; i++) { ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, i << 8); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma0 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val > fl_out_target) ++ cdr_pr_idac_tmp = i << 8; ++ } ++ ++ for (i = LEQ_LEN_CTRL_MAX_VAL; i >= 0; i--) { ++ pr_idac = cdr_pr_idac_tmp | (0x1 << i); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, pr_idac); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma0 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val < fl_out_target) ++ pr_idac &= ~(0x1 << i); ++ ++ cdr_pr_idac_tmp = pr_idac; ++ } ++ ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, ++ cdr_pr_idac_tmp); ++ ++ for (i = 0; i < FREQ_LOCK_MAX_ATTEMPT; i++) { ++ u32 val; ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = readl(pcie_phy->pma0 + REG_PCIE_PMA_RO_RX_FREQDET); ++ if (val & PCIE_RO_FBCK_LOCK) ++ break; ++ } ++ ++ /* turn off force mode and update band values */ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_INJ_MODE, ++ CSR_2L_PXP_CDR0_INJ_FORCE_OFF); ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ if (gen == PCIE_PORT_GEN3) { ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_IDAC_PCIEG3, ++ cdr_pr_idac_tmp); ++ } else { ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG1, ++ cdr_pr_idac_tmp); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG2, ++ cdr_pr_idac_tmp); ++ } ++} ++ ++static void ++airoha_phy_init_lane1_rx_fw_pre_calib(struct airoha_pcie_phy *pcie_phy, ++ enum airoha_pcie_port_gen gen) ++{ ++ u32 fl_out_target = gen == PCIE_PORT_GEN3 ? 41600 : 41941; ++ u32 lock_cyclecnt = gen == PCIE_PORT_GEN3 ? 26000 : 32767; ++ u32 pr_idac, val, cdr_pr_idac_tmp = 0; ++ int i; ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_SS_LCPLL_PWCTL_SETTING_1, ++ PCIE_LCPLL_MAN_PWDB); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET2, ++ PCIE_LOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_LOCK_CYCLECNT, lock_cyclecnt); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_LOCK_LOCKTH, 0x3); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_BEG, ++ fl_out_target - 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET3, ++ PCIE_UNLOCK_TARGET_END, ++ fl_out_target + 100); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET1, ++ PCIE_PLL_FT_UNLOCK_CYCLECNT, ++ lock_cyclecnt); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_UNLOCK_LOCKTH, 0x3); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR1_PR_INJ_MODE, ++ CSR_2L_PXP_CDR1_INJ_FORCE_OFF); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PR_PWDB); ++ ++ for (i = 0; i < LEQ_LEN_CTRL_MAX_VAL; i++) { ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, i << 8); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma1 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val > fl_out_target) ++ cdr_pr_idac_tmp = i << 8; ++ } ++ ++ for (i = LEQ_LEN_CTRL_MAX_VAL; i >= 0; i--) { ++ pr_idac = cdr_pr_idac_tmp | (0x1 << i); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, pr_idac); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = FIELD_GET(PCIE_RO_FL_OUT, ++ readl(pcie_phy->pma1 + ++ REG_PCIE_PMA_RO_RX_FREQDET)); ++ if (val < fl_out_target) ++ pr_idac &= ~(0x1 << i); ++ ++ cdr_pr_idac_tmp = pr_idac; ++ } ++ ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_DA_PXP_CDR_PR_IDAC, ++ cdr_pr_idac_tmp); ++ ++ for (i = 0; i < FREQ_LOCK_MAX_ATTEMPT; i++) { ++ u32 val; ++ ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_SS_RX_FREQ_DET4, ++ PCIE_FREQLOCK_DET_EN, 0x3); ++ ++ usleep_range(10000, 15000); ++ ++ val = readl(pcie_phy->pma1 + REG_PCIE_PMA_RO_RX_FREQDET); ++ if (val & PCIE_RO_FBCK_LOCK) ++ break; ++ } ++ ++ /* turn off force mode and update band values */ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_INJ_MODE, ++ CSR_2L_PXP_CDR1_INJ_FORCE_OFF); ++ ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_R_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_LPF_C, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_LPF_C_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_PIEYE_PWDB, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_PWDB); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_CDR_PR_IDAC); ++ if (gen == PCIE_PORT_GEN3) { ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_IDAC_PCIEG3, ++ cdr_pr_idac_tmp); ++ } else { ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG1, ++ cdr_pr_idac_tmp); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_DIG_RESERVE_13, ++ PCIE_FLL_IDAC_PCIEG2, ++ cdr_pr_idac_tmp); ++ } ++} ++ ++static void airoha_pcie_phy_init_default(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CMN, ++ CSR_2L_PXP_CMN_TRIM_MASK, 0x10); ++ writel(0xcccbcccb, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_21); ++ writel(0xcccb, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_22); ++ writel(0xcccbcccb, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_21); ++ writel(0xcccb, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_22); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CMN, ++ CSR_2L_PXP_CMN_LANE_EN); ++} ++ ++static void airoha_pcie_phy_init_clk_out(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_POSTDIV_D256, ++ CSR_2L_PXP_CLKTX0_AMP, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CLKTX0_FORCE_OUT1, ++ CSR_2L_PXP_CLKTX1_AMP, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_POSTDIV_D256, ++ CSR_2L_PXP_CLKTX0_OFFSET, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, ++ CSR_2L_PXP_CLKTX1_OFFSET, 0x2); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX0_FORCE_OUT1, ++ CSR_2L_PXP_CLKTX0_HZ); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, ++ CSR_2L_PXP_CLKTX1_HZ); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CLKTX0_FORCE_OUT1, ++ CSR_2L_PXP_CLKTX0_IMP_SEL, 0x12); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CLKTX1_IMP_SEL, ++ CSR_2L_PXP_CLKTX1_IMP_SEL, 0x12); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV_D256, ++ CSR_2L_PXP_CLKTX0_SR); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CLKTX1_OFFSET, ++ CSR_2L_PXP_CLKTX1_SR); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_PLL_CMN_RESERVE0, ++ CSR_2L_PXP_PLL_RESERVE_MASK, 0xd0d); ++} ++ ++static void airoha_pcie_phy_init_csr_2l(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_XFI_RXPCS_RST | PCIE_SW_REF_RST | ++ PCIE_SW_RX_RST); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_XFI_RXPCS_RST | PCIE_SW_REF_RST | ++ PCIE_SW_RX_RST); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, ++ PCIE_TX_TOP_RST | PCIE_TX_CAL_RST); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_TX_RESET, ++ PCIE_TX_TOP_RST | PCIE_TX_CAL_RST); ++} ++ ++static void airoha_pcie_phy_init_rx(struct airoha_pcie_phy *pcie_phy) ++{ ++ writel(0x2a00090b, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_17); ++ writel(0x2a00090b, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_17); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR0_PR_MONPI, ++ CSR_2L_PXP_CDR0_PR_XFICK_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR1_PR_MONPI, ++ CSR_2L_PXP_CDR1_PR_XFICK_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, ++ REG_CSR_2L_CDR0_PD_PICAL_CKD8_INV, ++ CSR_2L_PXP_CDR0_PD_EDGE_DISABLE); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, ++ REG_CSR_2L_CDR1_PD_PICAL_CKD8_INV, ++ CSR_2L_PXP_CDR1_PD_EDGE_DISABLE); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_PHYCK_DIV, ++ CSR_2L_PXP_RX0_PHYCK_SEL, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_PHYCK_DIV, ++ CSR_2L_PXP_RX1_PHYCK_SEL, 0x1); ++} ++ ++static void airoha_pcie_phy_init_jcpll(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_TCL_VTP_EN, ++ CSR_2L_PXP_JCPLL_SPARE_LOW, 0x20); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_RST); ++ writel(0x0, pcie_phy->csr_2l + REG_CSR_2L_JCPLL_SSC_DELTA1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC_PERIOD, ++ CSR_2L_PXP_JCPLL_SSC_PERIOD); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_TRI_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BR, 0xa); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BP, 0xc); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BC, 0x1f); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BWC, ++ CSR_2L_PXP_JCPLL_LPF_BWC, 0x1e); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BR, ++ CSR_2L_PXP_JCPLL_LPF_BWR, 0xa); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_MMD_PREDIV_MODE, ++ 0x1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, CSR_2L_PXP_JCPLL_MONCK, ++ CSR_2L_PXP_JCPLL_REFIN_DIV); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_VOS, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_SDM_PCW); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_VOS, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_SDM_PCW); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ 0x50000000); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_JCPLL_SDM_PCW, ++ 0x50000000); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_POSTDIV_D5); ++ airoha_phy_csr_2l_set_bits(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_POSTDIV_D2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_RST_DLY, 0x4); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_SDM_DI_LS); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_JCPLL_VCO_KBAND_MEAS_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_IB_EXT, ++ CSR_2L_PXP_JCPLL_CHP_IOFST); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_IB_EXT, ++ CSR_2L_PXP_JCPLL_CHP_IBIAS, 0xc); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_JCPLL_MMD_PREDIV_MODE, ++ CSR_2L_PXP_JCPLL_MMD_PREDIV_MODE, ++ 0x1); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_VCODIV, ++ CSR_2L_PXP_JCPLL_VCO_HALFLSB_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_VCODIV, ++ CSR_2L_PXP_JCPLL_VCO_CFIX, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_VCODIV, ++ CSR_2L_PXP_JCPLL_VCO_SCAPWR, 0x4); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_IB_EXT, ++ REG_CSR_2L_JCPLL_LPF_SHCK_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_POSTDIV_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_KBAND_KFC); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_KBAND_KF, 0x3); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_KBAND_KFC, ++ CSR_2L_PXP_JCPLL_KBAND_KS); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BWC, ++ CSR_2L_PXP_JCPLL_KBAND_DIV, 0x1); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SCAN_MODE, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_KBAND_LOAD_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SCAN_MODE, ++ PCIE_FORCE_DA_PXP_JCPLL_KBAND_LOAD_EN); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_LPF_BWC, ++ CSR_2L_PXP_JCPLL_KBAND_CODE, 0xe4); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_TCL_AMP_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_TCL_CMP, ++ CSR_2L_PXP_JCPLL_TCL_LPF_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_JCPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_JCPLL_TCL_KBAND_VREF, 0xf); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_TCL_AMP_GAIN, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_TCL_AMP_VREF, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_TCL_CMP, ++ CSR_2L_PXP_JCPLL_TCL_LPF_BW, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_VCO_TCLVAR, ++ CSR_2L_PXP_JCPLL_VCO_TCLVAR, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_JCPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_PXP_JCPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_JCPLL_EN); ++} ++ ++static void airoha_pcie_phy_txpll(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_PLL_RSTB); ++ writel(0x0, pcie_phy->csr_2l + REG_CSR_2L_TXPLL_SSC_DELTA1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC_PERIOD, ++ CSR_2L_PXP_txpll_SSC_PERIOD); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_CHP_IOFST, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_750M_SYS_CK, ++ CSR_2L_PXP_TXPLL_CHP_IBIAS, 0x2d); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_REFIN_DIV); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_VCO_CFIX, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ 0xc800000); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ PCIE_FORCE_DA_PXP_TXPLL_SDM_PCW, ++ 0xc800000); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SDM_DI_LS, ++ CSR_2L_PXP_TXPLL_SDM_IFM); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC, ++ CSR_2L_PXP_TXPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_RST_DLY, 0x4); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SDM_DI_LS, ++ CSR_2L_PXP_TXPLL_SDM_DI_LS); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_SDM_DI_LS, ++ CSR_2L_PXP_TXPLL_SDM_ORD, 0x3); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_TXPLL_VCO_KBAND_MEAS_EN); ++ writel(0x0, pcie_phy->csr_2l + REG_CSR_2L_TXPLL_SSC_DELTA1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_LPF_BP, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_LPF_BC, 0x18); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_LPF_BR, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_CHP_IOFST, ++ CSR_2L_PXP_TXPLL_CHP_IOFST, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_750M_SYS_CK, ++ CSR_2L_PXP_TXPLL_CHP_IBIAS, 0x2d); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_VTP, ++ CSR_2L_PXP_TXPLL_SPARE_L, 0x1); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_LPF_BWR, ++ CSR_2L_PXP_TXPLL_LPF_BWC); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV, ++ CSR_2L_PXP_TXPLL_MMD_PREDIV_MODE); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_REFIN_DIV, ++ CSR_2L_PXP_TXPLL_REFIN_DIV); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_VCO_HALFLSB_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_VCO_SCAPWR, ++ CSR_2L_PXP_TXPLL_VCO_SCAPWR, 0x7); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_VCO_CFIX, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PR_IDAC, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_SDM_PCW); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC, ++ CSR_2L_PXP_TXPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_LPF_BWR, ++ CSR_2L_PXP_TXPLL_LPF_BWR); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_PHY_CK2, ++ CSR_2L_PXP_TXPLL_REFIN_INTERNAL); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_TXPLL_VCO_KBAND_MEAS_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_VTP, ++ CSR_2L_PXP_TXPLL_VTP_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV, ++ CSR_2L_PXP_TXPLL_PHY_CK1_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_PHY_CK2, ++ CSR_2L_PXP_TXPLL_REFIN_INTERNAL); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_SSC, ++ CSR_2L_PXP_TXPLL_SSC_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_750M_SYS_CK, ++ CSR_2L_PXP_TXPLL_LPF_SHCK_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_POSTDIV, ++ CSR_2L_PXP_TXPLL_POSTDIV_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_TXPLL_KBAND_KFC); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_TXPLL_KBAND_KF, 0x3); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_txpll_KBAND_KS, 0x1); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_KBAND_DIV, ++ CSR_2L_PXP_TXPLL_KBAND_DIV, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_LPF_BWR, ++ CSR_2L_PXP_TXPLL_KBAND_CODE, 0xe4); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_SDM_OUT, ++ CSR_2L_PXP_TXPLL_TCL_AMP_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TXPLL_TCL_AMP_VREF, ++ CSR_2L_PXP_TXPLL_TCL_LPF_EN); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_TCL_KBAND_VREF, ++ CSR_2L_PXP_TXPLL_TCL_KBAND_VREF, 0xf); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_SDM_OUT, ++ CSR_2L_PXP_TXPLL_TCL_AMP_GAIN, 0x3); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_TXPLL_TCL_AMP_VREF, ++ CSR_2L_PXP_TXPLL_TCL_AMP_VREF, 0xb); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_TXPLL_TCL_LPF_BW, ++ CSR_2L_PXP_TXPLL_TCL_LPF_BW, 0x3); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_CKOUT_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_SEL_DA_PXP_TXPLL_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_TXPLL_CKOUT, ++ PCIE_FORCE_DA_PXP_TXPLL_EN); ++} ++ ++static void airoha_pcie_phy_init_ssc_jcpll(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SSC_DELTA1, ++ CSR_2L_PXP_JCPLL_SSC_DELTA1, 0x106); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SSC_DELTA1, ++ CSR_2L_PXP_JCPLL_SSC_DELTA, 0x106); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_JCPLL_SSC_PERIOD, ++ CSR_2L_PXP_JCPLL_SSC_PERIOD, 0x31b); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_PHASE_INI); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_IFM, ++ CSR_2L_PXP_JCPLL_SDM_IFM); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SDM_HREN, ++ CSR_2L_PXP_JCPLL_SDM_HREN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_RST_DLY, ++ CSR_2L_PXP_JCPLL_SDM_DI_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_TRI_EN); ++} ++ ++static void ++airoha_pcie_phy_set_rxlan0_signal_detect(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR0_PR_COR_HBW, ++ CSR_2L_PXP_CDR0_PR_LDO_FORCE_ON); ++ ++ usleep_range(100, 200); ++ ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_19, ++ PCIE_PCP_RX_REV0_PCIE_GEN1, 0x18b0); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN2, 0x18b0); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN3, 0x1030); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_SIGDET_DCTEST, ++ CSR_2L_PXP_RX0_SIGDET_PEAK, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_SIGDET_VTH_SEL, ++ CSR_2L_PXP_RX0_SIGDET_VTH_SEL, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_REV0, ++ CSR_2L_PXP_VOS_PNINV, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_SIGDET_DCTEST, ++ CSR_2L_PXP_RX0_SIGDET_LPF_CTRL, 0x1); ++ ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_CAL2, ++ PCIE_CAL_OUT_OS, 0x0); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_PXP_RX0_FE_VB_EQ2, ++ CSR_2L_PXP_RX0_FE_VCM_GEN_PWDB); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ airoha_phy_pma0_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_DA_PXP_RX_FE_GAIN_CTRL, 0x3); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_RX_FORCE_MODE0, ++ PCIE_FORCE_DA_XPON_RX_FE_GAIN_CTRL, 0x1); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_SIGDET0, ++ PCIE_SIGDET_WIN_NONVLD_TIMES, 0x3); ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SEQUENCE_DISB_CTRL1, ++ PCIE_DISB_RX_SDCAL_EN); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++ usleep_range(150, 200); ++ airoha_phy_pma0_clear_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++} ++ ++static void ++airoha_pcie_phy_set_rxlan1_signal_detect(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_CDR1_PR_COR_HBW, ++ CSR_2L_PXP_CDR1_PR_LDO_FORCE_ON); ++ ++ usleep_range(100, 200); ++ ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_19, ++ PCIE_PCP_RX_REV0_PCIE_GEN1, 0x18b0); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN2, 0x18b0); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_20, ++ PCIE_PCP_RX_REV0_PCIE_GEN3, 0x1030); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_SIGDET_NOVTH, ++ CSR_2L_PXP_RX1_SIGDET_PEAK, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_SIGDET_NOVTH, ++ CSR_2L_PXP_RX1_SIGDET_VTH_SEL, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_REV0, ++ CSR_2L_PXP_VOS_PNINV, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_DAC_RANGE_EYE, ++ CSR_2L_PXP_RX1_SIGDET_LPF_CTRL, 0x1); ++ ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_CAL2, ++ PCIE_CAL_OUT_OS, 0x0); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX1_FE_VB_EQ1, ++ CSR_2L_PXP_RX1_FE_VCM_GEN_PWDB); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ airoha_phy_pma1_update_field(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_GAIN_CTRL, ++ PCIE_FORCE_DA_PXP_RX_FE_GAIN_CTRL, 0x3); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_RX_FORCE_MODE0, ++ PCIE_FORCE_DA_XPON_RX_FE_GAIN_CTRL, 0x1); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_SS_RX_SIGDET0, ++ PCIE_SIGDET_WIN_NONVLD_TIMES, 0x3); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_SEQUENCE_DISB_CTRL1, ++ PCIE_DISB_RX_SDCAL_EN); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++ usleep_range(150, 200); ++ airoha_phy_pma1_clear_bits(pcie_phy, ++ REG_PCIE_PMA_CTRL_SEQUENCE_FORCE_CTRL1, ++ PCIE_FORCE_RX_SDCAL_EN); ++} ++ ++static void airoha_pcie_phy_set_rxflow(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_SCAN_RST, ++ PCIE_FORCE_DA_PXP_RX_SIGDET_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_SIGDET_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_SCAN_RST, ++ PCIE_FORCE_DA_PXP_RX_SIGDET_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_SIGDET_PWDB); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PD_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PD_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_CDR_PD_PWDB); ++ airoha_phy_pma0_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_PWDB, ++ PCIE_FORCE_DA_PXP_RX_FE_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_CDR_PD_PWDB, ++ PCIE_FORCE_DA_PXP_CDR_PD_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_CDR_PD_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, ++ REG_PCIE_PMA_FORCE_DA_PXP_RX_FE_PWDB, ++ PCIE_FORCE_DA_PXP_RX_FE_PWDB | ++ PCIE_FORCE_SEL_DA_PXP_RX_FE_PWDB); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX0_PHYCK_DIV, ++ CSR_2L_PXP_RX0_PHYCK_RSTB | ++ CSR_2L_PXP_RX0_TDC_CK_SEL); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX1_PHYCK_DIV, ++ CSR_2L_PXP_RX1_PHYCK_RSTB | ++ CSR_2L_PXP_RX1_TDC_CK_SEL); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_RX_FIFO_RST | PCIE_SW_TX_RST | ++ PCIE_SW_PMA_RST | PCIE_SW_ALLPCS_RST | ++ PCIE_SW_TX_FIFO_RST); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_SW_RX_FIFO_RST | PCIE_SW_TX_RST | ++ PCIE_SW_PMA_RST | PCIE_SW_ALLPCS_RST | ++ PCIE_SW_TX_FIFO_RST); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_PXP_RX0_FE_VB_EQ2, ++ CSR_2L_PXP_RX0_FE_VB_EQ2_EN | ++ CSR_2L_PXP_RX0_FE_VB_EQ3_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX0_SIGDET_VTH_SEL, ++ CSR_2L_PXP_RX0_FE_VB_EQ1_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_RX1_FE_VB_EQ1, ++ CSR_2L_PXP_RX1_FE_VB_EQ1_EN | ++ CSR_2L_PXP_RX1_FE_VB_EQ2_EN | ++ CSR_2L_PXP_RX1_FE_VB_EQ3_EN); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_REV0, ++ CSR_2L_PXP_FE_GAIN_NORMAL_MODE, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX0_REV0, ++ CSR_2L_PXP_FE_GAIN_TRAIN_MODE, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_REV0, ++ CSR_2L_PXP_FE_GAIN_NORMAL_MODE, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_REV0, ++ CSR_2L_PXP_FE_GAIN_TRAIN_MODE, 0x4); ++} ++ ++static void airoha_pcie_phy_set_pr(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_VREG_IBAND, ++ CSR_2L_PXP_CDR0_PR_VREG_IBAND, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_VREG_IBAND, ++ CSR_2L_PXP_CDR0_PR_VREG_CKBUF, 0x5); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_CKREF_DIV, ++ CSR_2L_PXP_CDR0_PR_CKREF_DIV); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_COR_HBW, ++ CSR_2L_PXP_CDR0_PR_CKREF_DIV1); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CDR1_PR_VREG_IBAND_VAL, ++ CSR_2L_PXP_CDR1_PR_VREG_IBAND, 0x5); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_CDR1_PR_VREG_IBAND_VAL, ++ CSR_2L_PXP_CDR1_PR_VREG_CKBUF, 0x5); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_CKREF_DIV, ++ CSR_2L_PXP_CDR1_PR_CKREF_DIV); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_COR_HBW, ++ CSR_2L_PXP_CDR1_PR_CKREF_DIV1); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_LPF_RATIO, ++ CSR_2L_PXP_CDR0_LPF_TOP_LIM, 0x20000); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_LPF_RATIO, ++ CSR_2L_PXP_CDR1_LPF_TOP_LIM, 0x20000); ++ ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_BETA_DAC, ++ CSR_2L_PXP_CDR0_PR_BETA_SEL, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_PR_BETA_DAC, ++ CSR_2L_PXP_CDR1_PR_BETA_SEL, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_BETA_DAC, ++ CSR_2L_PXP_CDR0_PR_KBAND_DIV, 0x4); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_PR_BETA_DAC, ++ CSR_2L_PXP_CDR1_PR_KBAND_DIV, 0x4); ++} ++ ++static void airoha_pcie_phy_set_txflow(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX0_CKLDO, ++ CSR_2L_PXP_TX0_CKLDO_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX1_CKLDO, ++ CSR_2L_PXP_TX1_CKLDO_EN); ++ ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX0_CKLDO, ++ CSR_2L_PXP_TX0_DMEDGEGEN_EN); ++ airoha_phy_csr_2l_set_bits(pcie_phy, REG_CSR_2L_TX1_CKLDO, ++ CSR_2L_PXP_TX1_DMEDGEGEN_EN); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_TX1_MULTLANE, ++ CSR_2L_PXP_TX1_MULTLANE_EN); ++} ++ ++static void airoha_pcie_phy_set_rx_mode(struct airoha_pcie_phy *pcie_phy) ++{ ++ writel(0x804000, pcie_phy->pma0 + REG_PCIE_PMA_DIG_RESERVE_27); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G1, 0x5); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G2, 0x5); ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G3, 0x5); ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_30, ++ 0x77700); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR0_PR_MONCK, ++ CSR_2L_PXP_CDR0_PR_MONCK_ENABLE); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR0_PR_MONCK, ++ CSR_2L_PXP_CDR0_PR_RESERVE0, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_PXP_RX0_OSCAL_CTLE1IOS, ++ CSR_2L_PXP_RX0_PR_OSCAL_VGA1IOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_PXP_RX0_OSCA_VGA1VOS, ++ CSR_2L_PXP_RX0_PR_OSCAL_VGA1VOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, ++ REG_CSR_2L_PXP_RX0_OSCA_VGA1VOS, ++ CSR_2L_PXP_RX0_PR_OSCAL_VGA2IOS, 0x14); ++ ++ writel(0x804000, pcie_phy->pma1 + REG_PCIE_PMA_DIG_RESERVE_27); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G1, 0x5); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G2, 0x5); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_18, ++ PCIE_PXP_RX_VTH_SEL_PCIE_G3, 0x5); ++ ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_30, ++ 0x77700); ++ ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_CDR1_PR_MONCK, ++ CSR_2L_PXP_CDR1_PR_MONCK_ENABLE); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_CDR1_PR_MONCK, ++ CSR_2L_PXP_CDR1_PR_RESERVE0, 0x2); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_OSCAL_VGA1IOS, ++ CSR_2L_PXP_RX1_PR_OSCAL_VGA1IOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_OSCAL_VGA1IOS, ++ CSR_2L_PXP_RX1_PR_OSCAL_VGA1VOS, 0x19); ++ airoha_phy_csr_2l_update_field(pcie_phy, REG_CSR_2L_RX1_OSCAL_VGA1IOS, ++ CSR_2L_PXP_RX1_PR_OSCAL_VGA2IOS, 0x14); ++} ++ ++static void airoha_pcie_phy_load_kflow(struct airoha_pcie_phy *pcie_phy) ++{ ++ airoha_phy_pma0_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED, 0xa); ++ airoha_phy_pma1_update_field(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED, 0xa); ++ airoha_phy_init_lane0_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN3); ++ airoha_phy_init_lane1_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN3); ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_12, ++ PCIE_FORCE_PMA_RX_SPEED); ++ usleep_range(100, 200); ++ ++ airoha_phy_init_lane0_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN2); ++ airoha_phy_init_lane1_rx_fw_pre_calib(pcie_phy, PCIE_PORT_GEN2); ++} ++ ++/** ++ * airoha_pcie_phy_init() - Initialize the phy ++ * @phy: the phy to be initialized ++ * ++ * Initialize the phy registers. ++ * The hardware settings will be reset during suspend, it should be ++ * reinitialized when the consumer calls phy_init() again on resume. ++ */ ++static int airoha_pcie_phy_init(struct phy *phy) ++{ ++ struct airoha_pcie_phy *pcie_phy = phy_get_drvdata(phy); ++ u32 val; ++ ++ /* Setup Tx-Rx detection time */ ++ val = FIELD_PREP(PCIE_XTP_RXDET_VCM_OFF_STB_T_SEL, 0x33) | ++ FIELD_PREP(PCIE_XTP_RXDET_EN_STB_T_SEL, 0x1) | ++ FIELD_PREP(PCIE_XTP_RXDET_FINISH_STB_T_SEL, 0x2) | ++ FIELD_PREP(PCIE_XTP_TXPD_TX_DATA_EN_DLY, 0x3) | ++ FIELD_PREP(PCIE_XTP_RXDET_LATCH_STB_T_SEL, 0x1); ++ writel(val, pcie_phy->p0_xr_dtime + REG_PCIE_PEXTP_DIG_GLB44); ++ writel(val, pcie_phy->p1_xr_dtime + REG_PCIE_PEXTP_DIG_GLB44); ++ /* Setup Rx AEQ training time */ ++ val = FIELD_PREP(PCIE_XTP_LN_RX_PDOWN_L1P2_EXIT_WAIT, 0x32) | ++ FIELD_PREP(PCIE_XTP_LN_RX_PDOWN_E0_AEQEN_WAIT, 0x5050); ++ writel(val, pcie_phy->rx_aeq + REG_PCIE_PEXTP_DIG_LN_RX30_P0); ++ writel(val, pcie_phy->rx_aeq + REG_PCIE_PEXTP_DIG_LN_RX30_P1); ++ ++ /* enable load FLL-K flow */ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_LOAD_EN); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_DIG_RESERVE_14, ++ PCIE_FLL_LOAD_EN); ++ ++ airoha_pcie_phy_init_default(pcie_phy); ++ airoha_pcie_phy_init_clk_out(pcie_phy); ++ airoha_pcie_phy_init_csr_2l(pcie_phy); ++ ++ usleep_range(100, 200); ++ ++ airoha_pcie_phy_init_rx(pcie_phy); ++ /* phase 1, no ssc for K TXPLL */ ++ airoha_pcie_phy_init_jcpll(pcie_phy); ++ ++ usleep_range(500, 600); ++ ++ /* TX PLL settings */ ++ airoha_pcie_phy_txpll(pcie_phy); ++ ++ usleep_range(200, 300); ++ ++ /* SSC JCPLL setting */ ++ airoha_pcie_phy_init_ssc_jcpll(pcie_phy); ++ ++ usleep_range(100, 200); ++ ++ /* Rx lan0 signal detect */ ++ airoha_pcie_phy_set_rxlan0_signal_detect(pcie_phy); ++ /* Rx lan1 signal detect */ ++ airoha_pcie_phy_set_rxlan1_signal_detect(pcie_phy); ++ /* RX FLOW */ ++ airoha_pcie_phy_set_rxflow(pcie_phy); ++ ++ usleep_range(100, 200); ++ ++ airoha_pcie_phy_set_pr(pcie_phy); ++ /* TX FLOW */ ++ airoha_pcie_phy_set_txflow(pcie_phy); ++ ++ usleep_range(100, 200); ++ /* RX mode setting */ ++ airoha_pcie_phy_set_rx_mode(pcie_phy); ++ /* Load K-Flow */ ++ airoha_pcie_phy_load_kflow(pcie_phy); ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ ++ usleep_range(100, 200); ++ ++ airoha_phy_pma0_set_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ airoha_phy_pma1_set_bits(pcie_phy, REG_PCIE_PMA_SS_DA_XPON_PWDB0, ++ PCIE_DA_XPON_CDR_PR_PWDB); ++ ++ /* Wait for the PCIe PHY to complete initialization before returning */ ++ msleep(PHY_HW_INIT_TIME_MS); ++ ++ return 0; ++} ++ ++static int airoha_pcie_phy_exit(struct phy *phy) ++{ ++ struct airoha_pcie_phy *pcie_phy = phy_get_drvdata(phy); ++ ++ airoha_phy_pma0_clear_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_PMA_SW_RST); ++ airoha_phy_pma1_clear_bits(pcie_phy, REG_PCIE_PMA_SW_RESET, ++ PCIE_PMA_SW_RST); ++ airoha_phy_csr_2l_clear_bits(pcie_phy, REG_CSR_2L_JCPLL_SSC, ++ CSR_2L_PXP_JCPLL_SSC_PHASE_INI | ++ CSR_2L_PXP_JCPLL_SSC_TRI_EN | ++ CSR_2L_PXP_JCPLL_SSC_EN); ++ ++ return 0; ++} ++ ++static const struct phy_ops airoha_pcie_phy_ops = { ++ .init = airoha_pcie_phy_init, ++ .exit = airoha_pcie_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int airoha_pcie_phy_probe(struct platform_device *pdev) ++{ ++ struct airoha_pcie_phy *pcie_phy; ++ struct device *dev = &pdev->dev; ++ struct phy_provider *provider; ++ ++ pcie_phy = devm_kzalloc(dev, sizeof(*pcie_phy), GFP_KERNEL); ++ if (!pcie_phy) ++ return -ENOMEM; ++ ++ pcie_phy->csr_2l = devm_platform_ioremap_resource_byname(pdev, "csr-2l"); ++ if (IS_ERR(pcie_phy->csr_2l)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->csr_2l), ++ "Failed to map phy-csr-2l base\n"); ++ ++ pcie_phy->pma0 = devm_platform_ioremap_resource_byname(pdev, "pma0"); ++ if (IS_ERR(pcie_phy->pma0)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->pma0), ++ "Failed to map phy-pma0 base\n"); ++ ++ pcie_phy->pma1 = devm_platform_ioremap_resource_byname(pdev, "pma1"); ++ if (IS_ERR(pcie_phy->pma1)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->pma1), ++ "Failed to map phy-pma1 base\n"); ++ ++ pcie_phy->phy = devm_phy_create(dev, dev->of_node, &airoha_pcie_phy_ops); ++ if (IS_ERR(pcie_phy->phy)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->phy), ++ "Failed to create PCIe phy\n"); ++ ++ pcie_phy->p0_xr_dtime = ++ devm_platform_ioremap_resource_byname(pdev, "p0-xr-dtime"); ++ if (IS_ERR(pcie_phy->p0_xr_dtime)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->p0_xr_dtime), ++ "Failed to map P0 Tx-Rx dtime base\n"); ++ ++ pcie_phy->p1_xr_dtime = ++ devm_platform_ioremap_resource_byname(pdev, "p1-xr-dtime"); ++ if (IS_ERR(pcie_phy->p1_xr_dtime)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->p1_xr_dtime), ++ "Failed to map P1 Tx-Rx dtime base\n"); ++ ++ pcie_phy->rx_aeq = devm_platform_ioremap_resource_byname(pdev, "rx-aeq"); ++ if (IS_ERR(pcie_phy->rx_aeq)) ++ return dev_err_probe(dev, PTR_ERR(pcie_phy->rx_aeq), ++ "Failed to map Rx AEQ base\n"); ++ ++ pcie_phy->dev = dev; ++ phy_set_drvdata(pcie_phy->phy, pcie_phy); ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(provider)) ++ return dev_err_probe(dev, PTR_ERR(provider), ++ "PCIe phy probe failed\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id airoha_pcie_phy_of_match[] = { ++ { .compatible = "airoha,en7581-pcie-phy" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, airoha_pcie_phy_of_match); ++ ++static struct platform_driver airoha_pcie_phy_driver = { ++ .probe = airoha_pcie_phy_probe, ++ .driver = { ++ .name = "airoha-pcie-phy", ++ .of_match_table = airoha_pcie_phy_of_match, ++ }, ++}; ++module_platform_driver(airoha_pcie_phy_driver); ++ ++MODULE_DESCRIPTION("Airoha PCIe PHY driver"); ++MODULE_AUTHOR("Lorenzo Bianconi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/airoha/patches-6.12/220-07-phy-airoha-Add-support-for-Airoha-AN7581-USB-PHY.patch b/target/linux/airoha/patches-6.12/220-07-phy-airoha-Add-support-for-Airoha-AN7581-USB-PHY.patch new file mode 100644 index 00000000000..8994927b077 --- /dev/null +++ b/target/linux/airoha/patches-6.12/220-07-phy-airoha-Add-support-for-Airoha-AN7581-USB-PHY.patch @@ -0,0 +1,667 @@ +From fadd22890b239e5a251dbe47367cfbeb1ea105f7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Feb 2025 13:28:40 +0100 +Subject: [PATCH 07/10] phy: airoha: Add support for Airoha AN7581 USB PHY + +Add support for Airoha AN7581 USB PHY driver. AN7581 supports up to 2 +USB port with USB 2.0 mode always supported and USB 3.0 mode available +only if the Serdes port is correctly configured for USB 3.0. + +The second USB port on the SoC can be both used for USB 3.0 operation or +PCIe. (toggled by the SCU SSR register and configured by the USB PHY +driver) + +If the USB 3.0 mode is not configured, the modes needs to be also +disabled in the xHCI node or the driver will report unsable clock and +fail probe. + +Also USB 3.0 PHY instance are provided only if the airoha,serdes-port +and airoha,scu property is defined in DT, if it's not then USB 3.0 PHY +is assumed not supported. + +For USB 2.0 Slew Rate calibration, airoha,usb2-monitor-clk-sel is +mandatory and is used to select the monitor clock for calibration. + +Normally it's 1 for USB port 1 and 2 for USB port 2. + +Signed-off-by: Christian Marangi +--- + MAINTAINERS | 1 + + drivers/phy/airoha/Kconfig | 10 + + drivers/phy/airoha/Makefile | 1 + + drivers/phy/airoha/phy-airoha-usb.c | 597 ++++++++++++++++++++++++++++ + 4 files changed, 609 insertions(+) + create mode 100644 drivers/phy/airoha/phy-airoha-usb.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -756,6 +756,7 @@ M: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SCU */ ++#define AIROHA_SCU_SSTR 0x9c ++#define AIROHA_SCU_SSTR_USB_PCIE_SEL BIT(3) ++#define AIROHA_SCU_SSTR_USB_PCIE_SEL_PCIE FIELD_PREP_CONST(AIROHA_SCU_SSTR_USB_PCIE_SEL, 0x0) ++#define AIROHA_SCU_SSTR_USB_PCIE_SEL_USB FIELD_PREP_CONST(AIROHA_SCU_SSTR_USB_PCIE_SEL, 0x1) ++ ++/* U2PHY */ ++#define AIROHA_USB_PHY_FMCR0 0x100 ++#define AIROHA_USB_PHY_MONCLK_SEL GENMASK(27, 26) ++#define AIROHA_USB_PHY_MONCLK_SEL0 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x0) ++#define AIROHA_USB_PHY_MONCLK_SEL1 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x1) ++#define AIROHA_USB_PHY_MONCLK_SEL2 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x2) ++#define AIROHA_USB_PHY_MONCLK_SEL3 FIELD_PREP_CONST(AIROHA_USB_PHY_MONCLK_SEL, 0x3) ++#define AIROHA_USB_PHY_FREQDET_EN BIT(24) ++#define AIROHA_USB_PHY_CYCLECNT GENMASK(23, 0) ++#define AIROHA_USB_PHY_FMMONR0 0x10c ++#define AIROHA_USB_PHY_USB_FM_OUT GENMASK(31, 0) ++#define AIROHA_USB_PHY_FMMONR1 0x110 ++#define AIROHA_USB_PHY_FRCK_EN BIT(8) ++ ++#define AIROHA_USB_PHY_USBPHYACR4 0x310 ++#define AIROHA_USB_PHY_USB20_FS_CR GENMASK(10, 8) ++#define AIROHA_USB_PHY_USB20_FS_CR_MAX FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x0) ++#define AIROHA_USB_PHY_USB20_FS_CR_NORMAL FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x2) ++#define AIROHA_USB_PHY_USB20_FS_CR_SMALLER FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x4) ++#define AIROHA_USB_PHY_USB20_FS_CR_MIN FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_CR, 0x6) ++#define AIROHA_USB_PHY_USB20_FS_SR GENMASK(2, 0) ++#define AIROHA_USB_PHY_USB20_FS_SR_MAX FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x0) ++#define AIROHA_USB_PHY_USB20_FS_SR_NORMAL FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x2) ++#define AIROHA_USB_PHY_USB20_FS_SR_SMALLER FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x4) ++#define AIROHA_USB_PHY_USB20_FS_SR_MIN FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_FS_SR, 0x6) ++#define AIROHA_USB_PHY_USBPHYACR5 0x314 ++#define AIROHA_USB_PHY_USB20_HSTX_SRCAL_EN BIT(15) ++#define AIROHA_USB_PHY_USB20_HSTX_SRCTRL GENMASK(14, 12) ++#define AIROHA_USB_PHY_USBPHYACR6 0x318 ++#define AIROHA_USB_PHY_USB20_BC11_SW_EN BIT(23) ++#define AIROHA_USB_PHY_USB20_DISCTH GENMASK(7, 4) ++#define AIROHA_USB_PHY_USB20_DISCTH_400 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x0) ++#define AIROHA_USB_PHY_USB20_DISCTH_420 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x1) ++#define AIROHA_USB_PHY_USB20_DISCTH_440 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x2) ++#define AIROHA_USB_PHY_USB20_DISCTH_460 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x3) ++#define AIROHA_USB_PHY_USB20_DISCTH_480 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x4) ++#define AIROHA_USB_PHY_USB20_DISCTH_500 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x5) ++#define AIROHA_USB_PHY_USB20_DISCTH_520 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x6) ++#define AIROHA_USB_PHY_USB20_DISCTH_540 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x7) ++#define AIROHA_USB_PHY_USB20_DISCTH_560 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x8) ++#define AIROHA_USB_PHY_USB20_DISCTH_580 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0x9) ++#define AIROHA_USB_PHY_USB20_DISCTH_600 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xa) ++#define AIROHA_USB_PHY_USB20_DISCTH_620 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xb) ++#define AIROHA_USB_PHY_USB20_DISCTH_640 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xc) ++#define AIROHA_USB_PHY_USB20_DISCTH_660 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xd) ++#define AIROHA_USB_PHY_USB20_DISCTH_680 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xe) ++#define AIROHA_USB_PHY_USB20_DISCTH_700 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_DISCTH, 0xf) ++#define AIROHA_USB_PHY_USB20_SQTH GENMASK(3, 0) ++#define AIROHA_USB_PHY_USB20_SQTH_85 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x0) ++#define AIROHA_USB_PHY_USB20_SQTH_90 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x1) ++#define AIROHA_USB_PHY_USB20_SQTH_95 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x2) ++#define AIROHA_USB_PHY_USB20_SQTH_100 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x3) ++#define AIROHA_USB_PHY_USB20_SQTH_105 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x4) ++#define AIROHA_USB_PHY_USB20_SQTH_110 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x5) ++#define AIROHA_USB_PHY_USB20_SQTH_115 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x6) ++#define AIROHA_USB_PHY_USB20_SQTH_120 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x7) ++#define AIROHA_USB_PHY_USB20_SQTH_125 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x8) ++#define AIROHA_USB_PHY_USB20_SQTH_130 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0x9) ++#define AIROHA_USB_PHY_USB20_SQTH_135 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xa) ++#define AIROHA_USB_PHY_USB20_SQTH_140 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xb) ++#define AIROHA_USB_PHY_USB20_SQTH_145 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xc) ++#define AIROHA_USB_PHY_USB20_SQTH_150 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xd) ++#define AIROHA_USB_PHY_USB20_SQTH_155 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xe) ++#define AIROHA_USB_PHY_USB20_SQTH_160 FIELD_PREP_CONST(AIROHA_USB_PHY_USB20_SQTH, 0xf) ++ ++#define AIROHA_USB_PHY_U2PHYDTM1 0x36c ++#define AIROHA_USB_PHY_FORCE_IDDIG BIT(9) ++#define AIROHA_USB_PHY_IDDIG BIT(1) ++ ++#define AIROHA_USB_PHY_GPIO_CTLD 0x80c ++#define AIROHA_USB_PHY_C60802_GPIO_CTLD GENMASK(31, 0) ++#define AIROHA_USB_PHY_SSUSB_IP_SW_RST BIT(31) ++#define AIROHA_USB_PHY_MCU_BUS_CK_GATE_EN BIT(30) ++#define AIROHA_USB_PHY_FORCE_SSUSB_IP_SW_RST BIT(29) ++#define AIROHA_USB_PHY_SSUSB_SW_RST BIT(28) ++ ++#define AIROHA_USB_PHY_U3_PHYA_REG0 0xb00 ++#define AIROHA_USB_PHY_SSUSB_BG_DIV GENMASK(29, 28) ++#define AIROHA_USB_PHY_SSUSB_BG_DIV_2 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x0) ++#define AIROHA_USB_PHY_SSUSB_BG_DIV_4 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x1) ++#define AIROHA_USB_PHY_SSUSB_BG_DIV_8 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x2) ++#define AIROHA_USB_PHY_SSUSB_BG_DIV_16 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_BG_DIV, 0x3) ++#define AIROHA_USB_PHY_U3_PHYA_REG1 0xb04 ++#define AIROHA_USB_PHY_SSUSB_XTAL_TOP_RESERVE GENMASK(25, 10) ++#define AIROHA_USB_PHY_U3_PHYA_REG6 0xb18 ++#define AIROHA_USB_PHY_SSUSB_CDR_RESERVE GENMASK(31, 24) ++#define AIROHA_USB_PHY_U3_PHYA_REG8 0xb20 ++#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY GENMASK(7, 6) ++#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_32 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x0) ++#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_64 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x1) ++#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_128 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x2) ++#define AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_216 FIELD_PREP_CONST(AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, 0x3) ++ ++#define AIROHA_USB_PHY_U3_PHYA_DA_REG19 0xc38 ++#define AIROHA_USB_PHY_SSUSB_PLL_SSC_DELTA1_U3 GENMASK(15, 0) ++ ++#define AIROHA_USB_PHY_U2_FM_DET_CYCLE_CNT 1024 ++#define AIROHA_USB_PHY_REF_CK 20 ++#define AIROHA_USB_PHY_U2_SR_COEF 28 ++#define AIROHA_USB_PHY_U2_SR_COEF_DIVISOR 1000 ++ ++#define AIROHA_USB_PHY_DEFAULT_SR_CALIBRATION 0x5 ++#define AIROHA_USB_PHY_FREQDET_SLEEP 1000 /* 1ms */ ++#define AIROHA_USB_PHY_FREQDET_TIMEOUT (AIROHA_USB_PHY_FREQDET_SLEEP * 10) ++ ++struct airoha_usb_phy_instance { ++ struct phy *phy; ++ u32 type; ++}; ++ ++enum airoha_usb_phy_instance_type { ++ AIROHA_PHY_USB2, ++ AIROHA_PHY_USB3, ++ ++ AIROHA_PHY_USB_MAX, ++}; ++ ++struct airoha_usb_phy_priv { ++ struct device *dev; ++ struct regmap *regmap; ++ struct regmap *scu; ++ ++ unsigned int monclk_sel; ++ unsigned int serdes_port; ++ ++ struct airoha_usb_phy_instance *phys[AIROHA_PHY_USB_MAX]; ++}; ++ ++static void airoha_usb_phy_u2_slew_rate_calibration(struct airoha_usb_phy_priv *priv) ++{ ++ u32 fm_out; ++ u32 srctrl; ++ ++ /* Enable HS TX SR calibration */ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR5, ++ AIROHA_USB_PHY_USB20_HSTX_SRCAL_EN); ++ ++ usleep_range(1000, 1500); ++ ++ /* Enable Free run clock */ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_FMMONR1, ++ AIROHA_USB_PHY_FRCK_EN); ++ ++ /* Select Monitor Clock */ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, ++ AIROHA_USB_PHY_MONCLK_SEL, ++ FIELD_PREP(AIROHA_USB_PHY_MONCLK_SEL, ++ priv->monclk_sel)); ++ ++ /* Set cyclecnt */ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, ++ AIROHA_USB_PHY_CYCLECNT, ++ FIELD_PREP(AIROHA_USB_PHY_CYCLECNT, ++ AIROHA_USB_PHY_U2_FM_DET_CYCLE_CNT)); ++ ++ /* Enable Frequency meter */ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, ++ AIROHA_USB_PHY_FREQDET_EN); ++ ++ /* Timeout can happen and we will apply workaround at the end */ ++ regmap_read_poll_timeout(priv->regmap, AIROHA_USB_PHY_FMMONR0, fm_out, ++ fm_out, AIROHA_USB_PHY_FREQDET_SLEEP, ++ AIROHA_USB_PHY_FREQDET_TIMEOUT); ++ ++ /* Disable Frequency meter */ ++ regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_FMCR0, ++ AIROHA_USB_PHY_FREQDET_EN); ++ ++ /* Disable Free run clock */ ++ regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_FMMONR1, ++ AIROHA_USB_PHY_FRCK_EN); ++ ++ /* Disable HS TX SR calibration */ ++ regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR5, ++ AIROHA_USB_PHY_USB20_HSTX_SRCAL_EN); ++ ++ usleep_range(1000, 1500); ++ ++ /* Frequency was not detected, use default SR calibration value */ ++ if (!fm_out) { ++ srctrl = AIROHA_USB_PHY_DEFAULT_SR_CALIBRATION; ++ dev_err(priv->dev, "Frequency not detected, using default SR calibration.\n"); ++ } else { ++ /* (1024 / FM_OUT) * REF_CK * U2_SR_COEF (round to the nearest digits) */ ++ srctrl = AIROHA_USB_PHY_REF_CK * AIROHA_USB_PHY_U2_SR_COEF; ++ srctrl = (srctrl * AIROHA_USB_PHY_U2_FM_DET_CYCLE_CNT) / fm_out; ++ srctrl = DIV_ROUND_CLOSEST(srctrl, AIROHA_USB_PHY_U2_SR_COEF_DIVISOR); ++ dev_dbg(priv->dev, "SR calibration applied: %x\n", srctrl); ++ } ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR5, ++ AIROHA_USB_PHY_USB20_HSTX_SRCTRL, ++ FIELD_PREP(AIROHA_USB_PHY_USB20_HSTX_SRCTRL, srctrl)); ++} ++ ++static void airoha_usb_phy_u2_init(struct airoha_usb_phy_priv *priv) ++{ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR4, ++ AIROHA_USB_PHY_USB20_FS_CR, ++ AIROHA_USB_PHY_USB20_FS_CR_MIN); ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR4, ++ AIROHA_USB_PHY_USB20_FS_SR, ++ AIROHA_USB_PHY_USB20_FS_SR_NORMAL); ++ ++ /* FIXME: evaluate if needed */ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, ++ AIROHA_USB_PHY_USB20_SQTH, ++ AIROHA_USB_PHY_USB20_SQTH_130); ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, ++ AIROHA_USB_PHY_USB20_DISCTH, ++ AIROHA_USB_PHY_USB20_DISCTH_600); ++ ++ /* Enable the USB port and then disable after calibration */ ++ regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, ++ AIROHA_USB_PHY_USB20_BC11_SW_EN); ++ ++ airoha_usb_phy_u2_slew_rate_calibration(priv); ++ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, ++ AIROHA_USB_PHY_USB20_BC11_SW_EN); ++ ++ usleep_range(1000, 1500); ++} ++ ++/* ++ * USB 3.0 mode can only work if USB serdes is correctly set. ++ * This is validated in xLate function. ++ */ ++static void airoha_usb_phy_u3_init(struct airoha_usb_phy_priv *priv) ++{ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG8, ++ AIROHA_USB_PHY_SSUSB_CDR_RST_DLY, ++ AIROHA_USB_PHY_SSUSB_CDR_RST_DLY_32); ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG6, ++ AIROHA_USB_PHY_SSUSB_CDR_RESERVE, ++ FIELD_PREP(AIROHA_USB_PHY_SSUSB_CDR_RESERVE, 0xe)); ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG0, ++ AIROHA_USB_PHY_SSUSB_BG_DIV, ++ AIROHA_USB_PHY_SSUSB_BG_DIV_4); ++ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_REG1, ++ FIELD_PREP(AIROHA_USB_PHY_SSUSB_XTAL_TOP_RESERVE, 0x600)); ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U3_PHYA_DA_REG19, ++ AIROHA_USB_PHY_SSUSB_PLL_SSC_DELTA1_U3, ++ FIELD_PREP(AIROHA_USB_PHY_SSUSB_PLL_SSC_DELTA1_U3, 0x43)); ++} ++ ++static int airoha_usb_phy_init(struct phy *phy) ++{ ++ struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); ++ struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ airoha_usb_phy_u2_init(priv); ++ break; ++ case PHY_TYPE_USB3: ++ if (phy_get_mode(phy) == PHY_MODE_PCIE) ++ return 0; ++ ++ airoha_usb_phy_u3_init(priv); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_u2_power_on(struct airoha_usb_phy_priv *priv) ++{ ++ regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, ++ AIROHA_USB_PHY_USB20_BC11_SW_EN); ++ ++ usleep_range(1000, 1500); ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_u3_power_on(struct airoha_usb_phy_priv *priv) ++{ ++ regmap_clear_bits(priv->regmap, AIROHA_USB_PHY_GPIO_CTLD, ++ AIROHA_USB_PHY_SSUSB_IP_SW_RST | ++ AIROHA_USB_PHY_MCU_BUS_CK_GATE_EN | ++ AIROHA_USB_PHY_FORCE_SSUSB_IP_SW_RST | ++ AIROHA_USB_PHY_SSUSB_SW_RST); ++ ++ usleep_range(1000, 1500); ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_power_on(struct phy *phy) ++{ ++ struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); ++ struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ airoha_usb_phy_u2_power_on(priv); ++ break; ++ case PHY_TYPE_USB3: ++ if (phy_get_mode(phy) == PHY_MODE_PCIE) ++ return 0; ++ ++ airoha_usb_phy_u3_power_on(priv); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_u2_power_off(struct airoha_usb_phy_priv *priv) ++{ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_USBPHYACR6, ++ AIROHA_USB_PHY_USB20_BC11_SW_EN); ++ ++ usleep_range(1000, 1500); ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_u3_power_off(struct airoha_usb_phy_priv *priv) ++{ ++ regmap_set_bits(priv->regmap, AIROHA_USB_PHY_GPIO_CTLD, ++ AIROHA_USB_PHY_SSUSB_IP_SW_RST | ++ AIROHA_USB_PHY_FORCE_SSUSB_IP_SW_RST); ++ ++ usleep_range(1000, 1500); ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_power_off(struct phy *phy) ++{ ++ struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); ++ struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ airoha_usb_phy_u2_power_off(priv); ++ break; ++ case PHY_TYPE_USB3: ++ if (phy_get_mode(phy) == PHY_MODE_PCIE) ++ return 0; ++ ++ airoha_usb_phy_u3_power_off(priv); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_u2_set_mode(struct airoha_usb_phy_priv *priv, ++ enum phy_mode mode) ++{ ++ u32 val; ++ ++ /* ++ * For Device and Host mode, enable force IDDIG. ++ * For Device set IDDIG, for Host clear IDDIG. ++ * For OTG disable force and clear IDDIG bit while at it. ++ */ ++ switch (mode) { ++ case PHY_MODE_USB_DEVICE: ++ val = AIROHA_USB_PHY_IDDIG; ++ break; ++ case PHY_MODE_USB_HOST: ++ val = AIROHA_USB_PHY_FORCE_IDDIG | ++ AIROHA_USB_PHY_FORCE_IDDIG; ++ break; ++ case PHY_MODE_USB_OTG: ++ val = 0; ++ break; ++ default: ++ return 0; ++ } ++ ++ regmap_update_bits(priv->regmap, AIROHA_USB_PHY_U2PHYDTM1, ++ AIROHA_USB_PHY_FORCE_IDDIG | ++ AIROHA_USB_PHY_IDDIG, val); ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_u3_set_mode(struct airoha_usb_phy_priv *priv, ++ enum phy_mode mode) ++{ ++ u32 sel; ++ ++ /* Only USB2 supports PCIe mode */ ++ if (mode == PHY_MODE_PCIE && ++ priv->serdes_port != AIROHA_SCU_SERDES_USB2) ++ return -EINVAL; ++ ++ if (mode == PHY_MODE_PCIE) ++ sel = AIROHA_SCU_SSTR_USB_PCIE_SEL_PCIE; ++ else ++ sel = AIROHA_SCU_SSTR_USB_PCIE_SEL_USB; ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_SSTR, ++ AIROHA_SCU_SSTR_USB_PCIE_SEL, sel); ++ ++ return 0; ++} ++ ++static int airoha_usb_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) ++{ ++ struct airoha_usb_phy_instance *instance = phy_get_drvdata(phy); ++ struct airoha_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent); ++ ++ switch (instance->type) { ++ case PHY_TYPE_USB2: ++ return airoha_usb_phy_u2_set_mode(priv, mode); ++ case PHY_TYPE_USB3: ++ return airoha_usb_phy_u3_set_mode(priv, mode); ++ default: ++ return 0; ++ } ++} ++ ++static struct phy *airoha_usb_phy_xlate(struct device *dev, ++ const struct of_phandle_args *args) ++{ ++ struct airoha_usb_phy_priv *priv = dev_get_drvdata(dev); ++ struct airoha_usb_phy_instance *instance = NULL; ++ unsigned int index, phy_type; ++ ++ if (args->args_count != 1) { ++ dev_err(dev, "invalid number of cells in 'phy' property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ phy_type = args->args[0]; ++ if (!(phy_type == PHY_TYPE_USB2 || phy_type == PHY_TYPE_USB3)) { ++ dev_err(dev, "unsupported device type: %d\n", phy_type); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ for (index = 0; index < AIROHA_PHY_USB_MAX; index++) ++ if (priv->phys[index] && ++ phy_type == priv->phys[index]->type) { ++ instance = priv->phys[index]; ++ break; ++ } ++ ++ if (!instance) { ++ dev_err(dev, "failed to find appropriate phy\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return instance->phy; ++} ++ ++static const struct phy_ops airoha_phy = { ++ .init = airoha_usb_phy_init, ++ .power_on = airoha_usb_phy_power_on, ++ .power_off = airoha_usb_phy_power_off, ++ .set_mode = airoha_usb_phy_set_mode, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regmap_config airoha_usb_phy_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++}; ++ ++static int airoha_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct airoha_usb_phy_priv *priv; ++ struct device *dev = &pdev->dev; ++ unsigned int index; ++ void *base; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dev = dev; ++ ++ ret = of_property_read_u32(dev->of_node, "airoha,usb2-monitor-clk-sel", ++ &priv->monclk_sel); ++ if (ret) ++ return dev_err_probe(dev, ret, "Monitor clock selection is mandatory for USB PHY calibration.\n"); ++ ++ if (priv->monclk_sel > 3) ++ return dev_err_probe(dev, -EINVAL, "only 4 Monitor clock are selectable on the SoC.\n"); ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ priv->regmap = devm_regmap_init_mmio(dev, base, &airoha_usb_phy_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ platform_set_drvdata(pdev, priv); ++ ++ for (index = 0; index < AIROHA_PHY_USB_MAX; index++) { ++ enum airoha_usb_phy_instance_type phy_type; ++ struct airoha_usb_phy_instance *instance; ++ ++ switch (index) { ++ case AIROHA_PHY_USB2: ++ phy_type = PHY_TYPE_USB2; ++ break; ++ case AIROHA_PHY_USB3: ++ phy_type = PHY_TYPE_USB3; ++ break; ++ } ++ ++ /* Skip registering USB3 instance if not supported */ ++ if (phy_type == PHY_TYPE_USB3) { ++ ret = of_property_read_u32(dev->of_node, "airoha,serdes-port", ++ &priv->serdes_port); ++ if (ret) ++ continue; ++ ++ /* With Serdes Port property, SCU is required */ ++ priv->scu = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "airoha,scu"); ++ if (IS_ERR(priv->scu)) ++ return dev_err_probe(dev, PTR_ERR(priv->scu), "failed to get SCU syscon.\n"); ++ } ++ ++ instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); ++ if (!instance) ++ return -ENOMEM; ++ ++ instance->type = phy_type; ++ priv->phys[index] = instance; ++ ++ instance->phy = devm_phy_create(dev, NULL, &airoha_phy); ++ if (IS_ERR(instance->phy)) ++ return dev_err_probe(dev, PTR_ERR(instance->phy), "failed to create phy\n"); ++ ++ phy_set_drvdata(instance->phy, instance); ++ } ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, airoha_usb_phy_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id airoha_phy_id_table[] = { ++ { .compatible = "airoha,an7581-usb-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, airoha_phy_id_table); ++ ++static struct platform_driver airoha_usb_driver = { ++ .probe = airoha_usb_phy_probe, ++ .driver = { ++ .name = "airoha-usb-phy", ++ .of_match_table = airoha_phy_id_table, ++ }, ++}; ++ ++module_platform_driver(airoha_usb_driver); ++ ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Airoha USB PHY driver"); diff --git a/target/linux/airoha/patches-6.12/220-08-usb-host-add-ARCH_AIROHA-in-XHCI-MTK-dependency.patch b/target/linux/airoha/patches-6.12/220-08-usb-host-add-ARCH_AIROHA-in-XHCI-MTK-dependency.patch new file mode 100644 index 00000000000..3756f7643ae --- /dev/null +++ b/target/linux/airoha/patches-6.12/220-08-usb-host-add-ARCH_AIROHA-in-XHCI-MTK-dependency.patch @@ -0,0 +1,25 @@ +From 3d3a406dea89b789dfb550bd05d0eba5ae926755 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Feb 2025 14:17:06 +0100 +Subject: [PATCH 08/10] usb: host: add ARCH_AIROHA in XHCI MTK dependency + +Airoha SoC use the same register map a logic of the Mediatek xHCI +driver, hence add it to the dependency list to permit compilation also +on this ARCH. + +Signed-off-by: Christian Marangi +--- + drivers/usb/host/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -71,7 +71,7 @@ config USB_XHCI_HISTB + config USB_XHCI_MTK + tristate "xHCI support for MediaTek SoCs" + select MFD_SYSCON +- depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST ++ depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || ARCH_AIROHA || COMPILE_TEST + help + Say 'Y' to enable the support for the xHCI host controller + found in MediaTek SoCs. diff --git a/target/linux/airoha/patches-6.12/220-10-PCI-mediatek-gen3-set-PHY-mode-for-Airoha-EN7581.patch b/target/linux/airoha/patches-6.12/220-10-PCI-mediatek-gen3-set-PHY-mode-for-Airoha-EN7581.patch new file mode 100644 index 00000000000..19d168db645 --- /dev/null +++ b/target/linux/airoha/patches-6.12/220-10-PCI-mediatek-gen3-set-PHY-mode-for-Airoha-EN7581.patch @@ -0,0 +1,34 @@ +From 112c6ea7ac356dab16e11084f2183e653a289e91 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 28 Oct 2025 12:35:41 +0100 +Subject: [PATCH 10/10] PCI: mediatek-gen3: set PHY mode for Airoha EN7581 + +For the Airoha EN7581 SoC, the 3rd PCIe line is attached to a special +PHY that can be both used for USB 3.0 operation or PCIe. + +Configure the PHY for PCIe operation before init it to correctly +configure the SCU Serdes register. + +This permits correct functionality and enumeration of PCIe devices on +the 3rd PCIe line present on the SoC. + +Signed-off-by: Christian Marangi +--- + drivers/pci/controller/pcie-mediatek-gen3.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -925,6 +925,12 @@ static int mtk_pcie_en7581_power_up(stru + size = lower_32_bits(resource_size(entry->res)); + regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size))); + ++ err = phy_set_mode(pcie->phy, PHY_MODE_PCIE); ++ if (err) { ++ dev_err(dev, "failed to set PHY mode\n"); ++ return err; ++ } ++ + /* + * Unlike the other MediaTek Gen3 controllers, the Airoha EN7581 + * requires PHY initialization and power-on before PHY reset deassert. diff --git a/target/linux/airoha/patches-6.12/310-02-net-airoha-deassert-XSI-line-on-hw-init.patch b/target/linux/airoha/patches-6.12/310-02-net-airoha-deassert-XSI-line-on-hw-init.patch new file mode 100644 index 00000000000..a75e06f944a --- /dev/null +++ b/target/linux/airoha/patches-6.12/310-02-net-airoha-deassert-XSI-line-on-hw-init.patch @@ -0,0 +1,26 @@ +From 5cb5f11469dfcbd7568fbca8b79c0f20a21cfbf5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 17 Jan 2025 10:09:15 +0100 +Subject: [PATCH 2/9] net: airoha: deassert XSI line on hw init + +In preparation for phylink support, deassert XSI line as we will naw +make actual use of them for external PHY/SFP cage support. + +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1487,6 +1487,10 @@ static int airoha_hw_init(struct platfor + if (err) + return err; + ++ err = reset_control_bulk_deassert(eth->soc->num_xsi_rsts, eth->xsi_rsts); ++ if (err) ++ return err; ++ + msleep(20); + err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts); + if (err) diff --git a/target/linux/airoha/patches-6.12/310-03-net-airoha-add-reference-for-SPORT-GDM4-in-qdma_get_.patch b/target/linux/airoha/patches-6.12/310-03-net-airoha-add-reference-for-SPORT-GDM4-in-qdma_get_.patch new file mode 100644 index 00000000000..d5a2ba9d1ae --- /dev/null +++ b/target/linux/airoha/patches-6.12/310-03-net-airoha-add-reference-for-SPORT-GDM4-in-qdma_get_.patch @@ -0,0 +1,31 @@ +From ad29054f9b0e96e30a5d0bb6967d1204b8ea8bd1 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 17 Jan 2025 10:12:02 +0100 +Subject: [PATCH 3/9] net: airoha: add reference for SPORT GDM4 in + qdma_get_gdm_port + +Add SPORT reference in get gdm port as the on receive the SPORT 0x18 is +assigned for the GDM4 port. + +While at it also add comments to better identify GDM1 ports. + +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -574,8 +574,11 @@ static int airoha_qdma_get_gdm_port(stru + + sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); + switch (sport) { ++ case 0x18: ++ port = 3; /* GDM4 */ ++ break; + case 0x10 ... 0x14: +- port = 0; ++ port = 0; /* GDM1 */ + break; + case 0x2 ... 0x4: + port = sport - 1; diff --git a/target/linux/airoha/patches-6.12/310-06-net-airoha-add-initial-fixup-for-GDM3-4-port-support.patch b/target/linux/airoha/patches-6.12/310-06-net-airoha-add-initial-fixup-for-GDM3-4-port-support.patch new file mode 100644 index 00000000000..cd774e11eac --- /dev/null +++ b/target/linux/airoha/patches-6.12/310-06-net-airoha-add-initial-fixup-for-GDM3-4-port-support.patch @@ -0,0 +1,40 @@ +From a3cd6eb3259282a68b608fc923121460c0d3d2f7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 17 Jan 2025 10:35:41 +0100 +Subject: [PATCH 6/9] net: airoha: add initial fixup for GDM3/4 port support + +GDM3 and GDM4 require different configuration for max long frame +definition, needs the QDMA to strip CRC on RX and require the SPORT to +be enabled to correctly be identified. + +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 12 ++++++++++-- + drivers/net/ethernet/airoha/airoha_regs.h | 1 + + 2 files changed, 11 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -498,8 +498,10 @@ static int airoha_fe_init(struct airoha_ + FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) | + FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22)); + +- airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), GDM_PAD_EN_MASK); +- airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), GDM_PAD_EN_MASK); ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), ++ GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK); ++ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), ++ GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK); + + airoha_fe_crsn_qsel_init(eth); + +@@ -1719,7 +1721,8 @@ static int airoha_dev_open(struct net_de + if (err) + return err; + +- if (netdev_uses_dsa(dev)) ++ /* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */ ++ if (netdev_uses_dsa(dev) || port->id > 2) + airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + else diff --git a/target/linux/airoha/patches-6.12/310-07-airoha-ethernet-drop-xsi-mac-reset.patch b/target/linux/airoha/patches-6.12/310-07-airoha-ethernet-drop-xsi-mac-reset.patch new file mode 100644 index 00000000000..0ecba241d80 --- /dev/null +++ b/target/linux/airoha/patches-6.12/310-07-airoha-ethernet-drop-xsi-mac-reset.patch @@ -0,0 +1,33 @@ +From fecb65813ddf52abf310bc2227a0ac869dc897d1 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 29 Jan 2025 14:47:41 +0100 +Subject: [PATCH 1/3] airoha: ethernet: drop xsi-mac reset + +In preparation for support for Ethernet and PON PCS, drop the xsi-mac +reset from airoha_eth. This reset is related to the Ethernet PCS and +should be handled in the dedicated driver. + +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 9 ++++----- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 2 files changed, 5 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -3196,7 +3196,6 @@ static void airoha_remove(struct platfor + } + + static const char * const en7581_xsi_rsts_names[] = { +- "xsi-mac", + "hsi0-mac", + "hsi1-mac", + "hsi-mac", +@@ -3250,7 +3249,6 @@ static u32 airoha_en7581_get_vip_port(st + } + + static const char * const an7583_xsi_rsts_names[] = { +- "xsi-mac", + "hsi0-mac", + "hsi1-mac", + "xfp-mac", diff --git a/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-AN7581-SoC.patch b/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-AN7581-SoC.patch new file mode 100644 index 00000000000..1f3137f70b0 --- /dev/null +++ b/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-AN7581-SoC.patch @@ -0,0 +1,4793 @@ +From 5d11640584dd24a1ba23e767c6929314c8ac50cf Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 17 Jan 2025 12:40:32 +0100 +Subject: [PATCH 1/2] net: pcs: airoha: add PCS driver for Airoha AN7581 SoC + +Add PCS driver for Airoha AN7581 SoC for Ethernet/PON/PCIe/USB SERDES +and permit usage of external PHY or connected SFP cage. Supported modes +are USXGMII, 10G-BASER, 2500BASE-X, 1000BASE-X and SGMII. + +The driver probe and register the various needed registers and register as +a PCS provider for fwnode usage. + +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/Kconfig | 2 + + drivers/net/pcs/Makefile | 2 + + drivers/net/pcs/airoha/Kconfig | 12 + + drivers/net/pcs/airoha/Makefile | 7 + + drivers/net/pcs/airoha/pcs-airoha-common.c | 1313 ++++++++++++ + drivers/net/pcs/airoha/pcs-airoha.h | 1309 ++++++++++++ + drivers/net/pcs/airoha/pcs-an7581.c | 2093 ++++++++++++++++++++ + 7 files changed, 4738 insertions(+) + create mode 100644 drivers/net/pcs/airoha/Kconfig + create mode 100644 drivers/net/pcs/airoha/Makefile + create mode 100644 drivers/net/pcs/airoha/pcs-airoha-common.c + create mode 100644 drivers/net/pcs/airoha/pcs-airoha.h + create mode 100644 drivers/net/pcs/airoha/pcs-an7581.c + +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -51,4 +51,6 @@ config PCS_RZN1_MIIC + on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in + pass-through mode for MII. + ++source "drivers/net/pcs/airoha/Kconfig" ++ + endmenu +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -10,3 +10,5 @@ obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o + obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o + obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o + obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o ++ ++obj-$(CONFIG_PCS_AIROHA) += airoha/ +--- /dev/null ++++ b/drivers/net/pcs/airoha/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++config PCS_AIROHA ++ tristate ++ select FWNODE_PCS ++ ++config PCS_AIROHA_AN7581 ++ tristate "Airoha AN7581 PCS driver" ++ select PCS_AIROHA ++ help ++ This module provides helper to phylink for managing the Airoha ++ AN7581 PCS for SoC Ethernet and PON SERDES. +--- /dev/null ++++ b/drivers/net/pcs/airoha/Makefile +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++obj-y := pcs-airoha.o ++pcs-airoha-objs := pcs-airoha-common.o ++ifdef CONFIG_PCS_AIROHA_AN7581 ++pcs-airoha-objs += pcs-an7581.o ++endif +--- /dev/null ++++ b/drivers/net/pcs/airoha/pcs-airoha-common.c +@@ -0,0 +1,1312 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pcs-airoha.h" ++ ++static void airoha_pcs_setup_scu_eth(struct airoha_pcs_priv *priv, ++ phy_interface_t interface) ++{ ++ u32 xsi_sel; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ xsi_sel = AIROHA_SCU_ETH_XSI_HSGMII; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ default: ++ xsi_sel = AIROHA_SCU_ETH_XSI_USXGMII; ++ } ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_SSR3, ++ AIROHA_SCU_ETH_XSI_SEL, ++ xsi_sel); ++} ++ ++static void airoha_pcs_setup_scu_pon(struct airoha_pcs_priv *priv, ++ phy_interface_t interface) ++{ ++ u32 xsi_sel, wan_sel; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ wan_sel = AIROHA_SCU_WAN_SEL_SGMII; ++ xsi_sel = AIROHA_SCU_PON_XSI_HSGMII; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ wan_sel = AIROHA_SCU_WAN_SEL_HSGMII; ++ xsi_sel = AIROHA_SCU_PON_XSI_HSGMII; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ default: ++ wan_sel = AIROHA_SCU_WAN_SEL_USXGMII; ++ xsi_sel = AIROHA_SCU_PON_XSI_USXGMII; ++ } ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_SSTR, ++ AIROHA_SCU_PON_XSI_SEL, ++ xsi_sel); ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_WAN_CONF, ++ AIROHA_SCU_WAN_SEL, ++ wan_sel); ++} ++ ++static void airoha_pcs_setup_scu_pcie(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ u32 xsi_sel; ++ ++ if (index == 0) { ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ xsi_sel = AIROHA_SCU_PCIE_XSI0_HSGMII; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ default: ++ xsi_sel = AIROHA_SCU_PCIE_XSI0_USXGMII; ++ } ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_SSTR, ++ AIROHA_SCU_PCIE_XSI0_SEL, ++ xsi_sel); ++ } else { ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ xsi_sel = AIROHA_SCU_PCIE_XSI1_HSGMII; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ default: ++ xsi_sel = AIROHA_SCU_PCIE_XSI1_USXGMII; ++ } ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_SSTR, ++ AIROHA_SCU_PCIE_XSI1_SEL, ++ xsi_sel); ++ } ++} ++ ++static int airoha_pcs_setup_scu(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ int ret; ++ ++ switch (data->port_type) { ++ case AIROHA_PCS_ETH: ++ airoha_pcs_setup_scu_eth(priv, interface); ++ break; ++ case AIROHA_PCS_PON: ++ airoha_pcs_setup_scu_pon(priv, interface); ++ break; ++ case AIROHA_PCS_PCIE: ++ airoha_pcs_setup_scu_pcie(priv, index, interface); ++ break; ++ case AIROHA_PCS_USB: ++ break; ++ } ++ ++ /* TODO better handle reset from MAC */ ++ ret = reset_control_bulk_assert(ARRAY_SIZE(priv->rsts), ++ priv->rsts); ++ if (ret) ++ return ret; ++ ++ ret = reset_control_bulk_deassert(ARRAY_SIZE(priv->rsts), ++ priv->rsts); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void airoha_pcs_init_usxgmii(struct airoha_pcs_priv *priv, int index) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ ++ regmap_set_bits(maps->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0, ++ AIROHA_PCS_HSGMII_XFI_SEL); ++ ++ /* Disable Hibernation */ ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTROL_1, ++ AIROHA_PCS_USXGMII_SPEED_SEL_H); ++ ++ /* FIXME: wait Airoha */ ++ /* Avoid PCS sending garbage to MAC in some HW revision (E0) */ ++ regmap_write(maps->usxgmii_pcs, AIROHA_PCS_USGMII_VENDOR_DEFINE_116, 0); ++} ++ ++static void airoha_pcs_init_hsgmii(struct airoha_pcs_priv *priv, int index) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ ++ regmap_clear_bits(maps->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0, ++ AIROHA_PCS_HSGMII_XFI_SEL); ++ ++ regmap_update_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_1, ++ AIROHA_PCS_TBI_10B_MODE, ++ priv->phy ? 0 : AIROHA_PCS_TBI_10B_MODE); ++} ++ ++static void airoha_pcs_init_sgmii(struct airoha_pcs_priv *priv, int index) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ ++ regmap_clear_bits(maps->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0, ++ AIROHA_PCS_HSGMII_XFI_SEL); ++ ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_1, ++ AIROHA_PCS_TBI_10B_MODE); ++ ++ regmap_update_bits(maps->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_6, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_L, ++ FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_L, 0x07070707)); ++ ++ regmap_update_bits(maps->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_8, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_C, ++ FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_C, 0xff)); ++} ++ ++static void airoha_pcs_init(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ airoha_pcs_init_sgmii(priv, index); ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ airoha_pcs_init_hsgmii(priv, index); ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ airoha_pcs_init_usxgmii(priv, index); ++ break; ++ default: ++ return; ++ } ++} ++ ++static void airoha_pcs_interrupt_init_sgmii(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ ++ /* Disable every interrupt */ ++ regmap_clear_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT, ++ AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT | ++ AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT | ++ AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT | ++ AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT | ++ AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT); ++ ++ /* Clear interrupt */ ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT, ++ AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT_CLEAR); ++ ++ regmap_clear_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT, ++ AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT_CLEAR | ++ AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT_CLEAR); ++} ++ ++static void airoha_pcs_interrupt_init_usxgmii(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ ++ /* Disable every Interrupt */ ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_0, ++ AIROHA_PCS_USXGMII_T_TYPE_T_INT_EN | ++ AIROHA_PCS_USXGMII_T_TYPE_D_INT_EN | ++ AIROHA_PCS_USXGMII_T_TYPE_C_INT_EN | ++ AIROHA_PCS_USXGMII_T_TYPE_S_INT_EN); ++ ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_1, ++ AIROHA_PCS_USXGMII_R_TYPE_C_INT_EN | ++ AIROHA_PCS_USXGMII_R_TYPE_S_INT_EN | ++ AIROHA_PCS_USXGMII_TXPCS_FSM_ENC_ERR_INT_EN | ++ AIROHA_PCS_USXGMII_T_TYPE_E_INT_EN); ++ ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_2, ++ AIROHA_PCS_USXGMII_RPCS_FSM_DEC_ERR_INT_EN | ++ AIROHA_PCS_USXGMII_R_TYPE_E_INT_EN | ++ AIROHA_PCS_USXGMII_R_TYPE_T_INT_EN | ++ AIROHA_PCS_USXGMII_R_TYPE_D_INT_EN); ++ ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_3, ++ AIROHA_PCS_USXGMII_FAIL_SYNC_XOR_ST_INT_EN | ++ AIROHA_PCS_USXGMII_RX_BLOCK_LOCK_ST_INT_EN | ++ AIROHA_PCS_USXGMII_LINK_UP_ST_INT_EN | ++ AIROHA_PCS_USXGMII_HI_BER_ST_INT_EN); ++ ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTRL_4, ++ AIROHA_PCS_USXGMII_LINK_DOWN_ST_INT_EN); ++ ++ /* Clear any pending interrupt */ ++ regmap_set_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_INT_STA_2, ++ AIROHA_PCS_USXGMII_RPCS_FSM_DEC_ERR_INT | ++ AIROHA_PCS_USXGMII_R_TYPE_E_INT | ++ AIROHA_PCS_USXGMII_R_TYPE_T_INT | ++ AIROHA_PCS_USXGMII_R_TYPE_D_INT); ++ ++ regmap_set_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_INT_STA_3, ++ AIROHA_PCS_USXGMII_FAIL_SYNC_XOR_ST_INT | ++ AIROHA_PCS_USXGMII_RX_BLOCK_LOCK_ST_INT | ++ AIROHA_PCS_USXGMII_LINK_UP_ST_INT | ++ AIROHA_PCS_USXGMII_HI_BER_ST_INT); ++ ++ regmap_set_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_INT_STA_4, ++ AIROHA_PCS_USXGMII_LINK_DOWN_ST_INT); ++ ++ /* Interrupt saddly seems to be not weel supported for Link Down. ++ * PCS Poll is a must to correctly read and react on Cable Deatch ++ * as only cable attach interrupt are fired and Link Down interrupt ++ * are fired only in special case like AN restart. ++ */ ++} ++ ++static void airoha_pcs_interrupt_init(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return airoha_pcs_interrupt_init_sgmii(priv, index); ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ return airoha_pcs_interrupt_init_usxgmii(priv, index); ++ default: ++ return; ++ } ++} ++ ++static void airoha_pcs_get_state_sgmii(struct airoha_pcs_priv *priv, ++ int index, struct phylink_link_state *state) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ u32 bmsr, lpa; ++ ++ regmap_read(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_1, ++ &bmsr); ++ regmap_read(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_5, ++ &lpa); ++ ++ bmsr = (AIROHA_PCS_HSGMII_AN_SGMII_AN_COMPLETE | ++ AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT | ++ AIROHA_PCS_HSGMII_AN_SGMII_AN_ABILITY | ++ AIROHA_PCS_HSGMII_AN_SGMII_LINK_STATUS) & bmsr; ++ lpa = AIROHA_PCS_HSGMII_AN_SGMII_PARTNER_ABILITY & lpa; ++ ++ phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); ++} ++ ++static void airoha_pcs_get_state_hsgmii(struct airoha_pcs_priv *priv, int index, ++ struct phylink_link_state *state) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ u32 bmsr; ++ ++ regmap_read(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_1, ++ &bmsr); ++ ++ bmsr = (AIROHA_PCS_HSGMII_AN_SGMII_AN_COMPLETE | ++ AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT | ++ AIROHA_PCS_HSGMII_AN_SGMII_AN_ABILITY | ++ AIROHA_PCS_HSGMII_AN_SGMII_LINK_STATUS) & bmsr; ++ ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; ++} ++ ++static void airoha_pcs_get_state_usxgmii(struct airoha_pcs_priv *priv, int index, ++ struct phylink_link_state *state) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ u32 an_done, lpa; ++ ++ /* Trigger HW workaround if needed. If an error is reported, ++ * consider link down and test again later. ++ */ ++ if (data->rxlock_workaround && data->rxlock_workaround(priv, index)) { ++ state->link = false; ++ return; ++ } ++ ++ /* Toggle AN Status */ ++ regmap_set_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_6, ++ AIROHA_PCS_USXGMII_TOG_PCS_AUTONEG_STS); ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_6, ++ AIROHA_PCS_USXGMII_TOG_PCS_AUTONEG_STS); ++ ++ regmap_read(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_STATS_0, &lpa); ++ regmap_read(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_STATS_2, &an_done); ++ ++ state->link = !!(lpa & MDIO_USXGMII_LINK); ++ state->an_complete = !!(an_done & AIROHA_PCS_USXGMII_PCS_AN_COMPLETE); ++ ++ phylink_decode_usxgmii_word(state, lpa); ++} ++ ++static void airoha_pcs_get_state_10gbaser(struct airoha_pcs_priv *priv, int index, ++ struct phylink_link_state *state) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ u32 status, curr_mode; ++ ++ /* Toggle AN Status */ ++ regmap_set_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_6, ++ AIROHA_PCS_USXGMII_TOG_PCS_AUTONEG_STS); ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_6, ++ AIROHA_PCS_USXGMII_TOG_PCS_AUTONEG_STS); ++ ++ regmap_read(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_BASE_R_10GB_T_PCS_STUS_1, ++ &status); ++ regmap_read(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_STATS_0, &curr_mode); ++ ++ state->link = !!(status & AIROHA_PCS_USXGMII_RX_LINK_STUS); ++ ++ switch (curr_mode & AIROHA_PCS_USXGMII_CUR_USXGMII_MODE) { ++ case AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_10G: ++ state->speed = SPEED_10000; ++ break; ++ case AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_5G: ++ state->speed = SPEED_5000; ++ break; ++ case AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_2_5G: ++ state->speed = SPEED_2500; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ state->duplex = DUPLEX_FULL; ++} ++ ++static void airoha_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ airoha_pcs_get_state_sgmii(priv, port->index, state); ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ airoha_pcs_get_state_hsgmii(priv, port->index, state); ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ airoha_pcs_get_state_usxgmii(priv, port->index, state); ++ break; ++ case PHY_INTERFACE_MODE_10GBASER: ++ airoha_pcs_get_state_10gbaser(priv, port->index, state); ++ break; ++ default: ++ return; ++ } ++} ++ ++static int airoha_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ const struct airoha_pcs_match_data *data; ++ struct airoha_pcs_maps *maps; ++ int index = port->index; ++ u32 rate_adapt; ++ int ret; ++ ++ maps = &priv->maps[port->index]; ++ priv->interface = interface; ++ data = priv->data; ++ ++ /* Apply Analog and Digital configuration for PCS */ ++ if (data->bringup) { ++ ret = data->bringup(priv, index, interface); ++ if (ret) ++ return ret; ++ } ++ ++ /* Set final configuration for various modes */ ++ airoha_pcs_init(priv, index, interface); ++ ++ /* Configure Interrupt for various modes */ ++ airoha_pcs_interrupt_init(priv, index, interface); ++ ++ rate_adapt = AIROHA_PCS_HSGMII_RATE_ADAPT_RX_EN | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_TX_EN; ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII) ++ rate_adapt |= AIROHA_PCS_HSGMII_RATE_ADAPT_RX_BYPASS | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_TX_BYPASS; ++ ++ /* AN Auto Settings (Rate Adaptation) */ ++ regmap_update_bits(maps->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_0, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_BYPASS | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_TX_BYPASS | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_EN | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_TX_EN, rate_adapt); ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GBASER) { ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) ++ regmap_set_bits(maps->usxgmii_pcs, ++ AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0, ++ AIROHA_PCS_USXGMII_AN_ENABLE); ++ else ++ regmap_clear_bits(maps->usxgmii_pcs, ++ AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0, ++ AIROHA_PCS_USXGMII_AN_ENABLE); ++ ++ regmap_clear_bits(maps->usxgmii_pcs, ++ AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7, ++ AIROHA_PCS_USXGMII_RATE_UPDATE_MODE); ++ } else { ++ regmap_clear_bits(maps->usxgmii_pcs, ++ AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0, ++ AIROHA_PCS_USXGMII_AN_ENABLE); ++ ++ regmap_set_bits(maps->usxgmii_pcs, ++ AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7, ++ AIROHA_PCS_USXGMII_RATE_UPDATE_MODE); ++ } ++ } ++ ++ /* Clear any force bit that my be set by bootloader */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_2500BASEX) { ++ regmap_clear_bits(maps->multi_sgmii, AIROHA_PCS_MULTI_SGMII_SGMII_STS_CTRL_0, ++ AIROHA_PCS_LINK_MODE_P0 | ++ AIROHA_PCS_FORCE_SPD_MODE_P0 | ++ AIROHA_PCS_FORCE_LINKDOWN_P0 | ++ AIROHA_PCS_FORCE_LINKUP_P0); ++ } ++ ++ /* Toggle Rate Adaption for SGMII/HSGMII mode */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_2500BASEX) { ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) ++ regmap_clear_bits(maps->hsgmii_rate_adp, ++ AIROHA_PCS_HSGMII_RATE_ADP_P0_CTRL_0, ++ AIROHA_PCS_HSGMII_P0_DIS_MII_MODE); ++ else ++ regmap_set_bits(maps->hsgmii_rate_adp, ++ AIROHA_PCS_HSGMII_RATE_ADP_P0_CTRL_0, ++ AIROHA_PCS_HSGMII_P0_DIS_MII_MODE); ++ } ++ ++ /* Setup AN Link Timer */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_1000BASEX) { ++ u32 an_timer; ++ ++ an_timer = phylink_get_link_timer_ns(interface); ++ ++ /* Value needs to be shifted by 4, seems value is internally * 16 */ ++ regmap_update_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_11, ++ AIROHA_PCS_HSGMII_AN_SGMII_LINK_TIMER, ++ FIELD_PREP(AIROHA_PCS_HSGMII_AN_SGMII_LINK_TIMER, ++ an_timer >> 4)); ++ ++ regmap_update_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_3, ++ AIROHA_PCS_HSGMII_PCS_LINK_STSTIME, ++ FIELD_PREP(AIROHA_PCS_HSGMII_PCS_LINK_STSTIME, ++ an_timer >> 4)); ++ } ++ ++ /* Setup SGMII AN and advertisement in DEV_ABILITY */ ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ int advertise = phylink_mii_c22_pcs_encode_advertisement(interface, ++ advertising); ++ if (advertise < 0) ++ return advertise; ++ ++ regmap_update_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_4, ++ AIROHA_PCS_HSGMII_AN_SGMII_DEV_ABILITY, ++ FIELD_PREP(AIROHA_PCS_HSGMII_AN_SGMII_DEV_ABILITY, ++ advertise)); ++ ++ regmap_set_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0, ++ AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE); ++ } else { ++ regmap_clear_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0, ++ AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE); ++ } ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) { ++ regmap_clear_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0, ++ AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE); ++ ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6, ++ AIROHA_PCS_HSGMII_PCS_TX_ENABLE); ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_1000BASEX) { ++ u32 if_mode = AIROHA_PCS_HSGMII_AN_SIDEBAND_EN; ++ ++ /* Toggle SGMII or 1000base-x mode */ ++ if (interface == PHY_INTERFACE_MODE_SGMII) ++ if_mode |= AIROHA_PCS_HSGMII_AN_SGMII_EN; ++ ++ if (neg_mode & PHYLINK_PCS_NEG_INBAND) ++ regmap_set_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13, ++ AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT_DIS); ++ else ++ regmap_clear_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13, ++ AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT_DIS); ++ ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ /* Clear force speed bits and MAC mode */ ++ regmap_clear_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6, ++ AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10 | ++ AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100 | ++ AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000 | ++ AIROHA_PCS_HSGMII_PCS_MAC_MODE | ++ AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL | ++ AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT); ++ } else { ++ /* Enable compatibility with MAC PCS Layer */ ++ if_mode |= AIROHA_PCS_HSGMII_AN_SGMII_COMPAT_EN; ++ ++ /* AN off force rate adaption, speed is set later in Link Up */ ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6, ++ AIROHA_PCS_HSGMII_PCS_MAC_MODE | ++ AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT); ++ } ++ ++ regmap_update_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13, ++ AIROHA_PCS_HSGMII_AN_SGMII_IF_MODE_5_0, if_mode); ++ ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6, ++ AIROHA_PCS_HSGMII_PCS_TX_ENABLE | ++ AIROHA_PCS_HSGMII_PCS_MODE2_EN); ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_1000BASEX && ++ neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_1, ++ AIROHA_PCS_SGMII_SEND_AN_ERR_EN); ++ ++ regmap_set_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_FORCE_CL37, ++ AIROHA_PCS_HSGMII_AN_FORCE_AN_DONE); ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) { ++ regmap_set_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0, ++ AIROHA_PCS_HSGMII_AN_SGMII_RESET_PHY); ++ } ++ ++ /* Configure Flow Control on XFI */ ++ regmap_update_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_TX_FC_EN | AIROHA_PCS_XFI_RX_FC_EN, ++ permit_pause_to_mac ? ++ AIROHA_PCS_XFI_TX_FC_EN | AIROHA_PCS_XFI_RX_FC_EN : ++ 0); ++ ++ return 0; ++} ++ ++static void airoha_pcs_an_restart(struct phylink_pcs *pcs) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ struct airoha_pcs_maps *maps; ++ ++ maps = &priv->maps[port->index]; ++ ++ switch (priv->interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ regmap_set_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0, ++ AIROHA_PCS_HSGMII_AN_SGMII_AN_RESTART); ++ udelay(3); ++ regmap_clear_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0, ++ AIROHA_PCS_HSGMII_AN_SGMII_AN_RESTART); ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ regmap_set_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0, ++ AIROHA_PCS_USXGMII_AN_RESTART); ++ udelay(3); ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0, ++ AIROHA_PCS_USXGMII_AN_RESTART); ++ default: ++ return; ++ } ++} ++ ++static void airoha_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, int speed, int duplex) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ const struct airoha_pcs_match_data *data; ++ struct airoha_pcs_maps *maps; ++ ++ maps = &priv->maps[port->index]; ++ data = priv->data; ++ ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ regmap_update_bits(maps->hsgmii_rate_adp, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_1, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR, ++ FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR, 0x0) | ++ FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR, 0x0)); ++ udelay(1); ++ regmap_update_bits(maps->hsgmii_rate_adp, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_1, ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR | ++ AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR, ++ FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR, 0xf) | ++ FIELD_PREP(AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR, 0x5)); ++ } ++ } else { ++ if (interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GBASER) { ++ u32 mode; ++ u32 rate_adapt; ++ ++ switch (speed) { ++ case SPEED_10000: ++ rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_10000; ++ mode = AIROHA_PCS_USXGMII_MODE_10000; ++ break; ++ case SPEED_5000: ++ rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_5000; ++ mode = AIROHA_PCS_USXGMII_MODE_5000; ++ break; ++ case SPEED_2500: ++ rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_2500; ++ mode = AIROHA_PCS_USXGMII_MODE_2500; ++ break; ++ case SPEED_1000: ++ rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_1000; ++ mode = AIROHA_PCS_USXGMII_MODE_1000; ++ break; ++ case SPEED_100: ++ rate_adapt = AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_100; ++ mode = AIROHA_PCS_USXGMII_MODE_100; ++ break; ++ } ++ ++ /* Force USXGMII to selected speed */ ++ regmap_update_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7, ++ AIROHA_PCS_USXGMII_MODE, mode); ++ ++ if (interface == PHY_INTERFACE_MODE_10GBASER) ++ regmap_update_bits(maps->hsgmii_rate_adp, AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_11, ++ AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_EN | ++ AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE, ++ AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_EN | ++ rate_adapt); ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_1000BASEX) { ++ u32 force_speed; ++ u32 rate_adapt; ++ ++ switch (speed) { ++ case SPEED_1000: ++ force_speed = AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000; ++ rate_adapt = AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_1000; ++ break; ++ case SPEED_100: ++ force_speed = AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100; ++ rate_adapt = AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_100; ++ break; ++ case SPEED_10: ++ force_speed = AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10; ++ rate_adapt = AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_10; ++ break; ++ } ++ ++ regmap_update_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_CTROL_6, ++ AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10 | ++ AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100 | ++ AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000 | ++ AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL, ++ force_speed | rate_adapt); ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ interface == PHY_INTERFACE_MODE_2500BASEX) { ++ u32 ck_gen_mode; ++ u32 speed_reg; ++ u32 if_mode; ++ ++ switch (speed) { ++ case SPEED_2500: ++ speed_reg = AIROHA_PCS_LINK_MODE_P0_2_5G; ++ break; ++ case SPEED_1000: ++ speed_reg = AIROHA_PCS_LINK_MODE_P0_1G; ++ if_mode = AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_1000; ++ ck_gen_mode = AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_1000; ++ break; ++ case SPEED_100: ++ speed_reg = AIROHA_PCS_LINK_MODE_P0_100M; ++ if_mode = AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_100; ++ ck_gen_mode = AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_100; ++ break; ++ case SPEED_10: ++ speed_reg = AIROHA_PCS_LINK_MODE_P0_100M; ++ if_mode = AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_10; ++ ck_gen_mode = AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_10; ++ break; ++ } ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ regmap_update_bits(maps->hsgmii_an, AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13, ++ AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE, ++ if_mode); ++ ++ regmap_update_bits(maps->hsgmii_pcs, AIROHA_PCS_HSGMII_PCS_AN_SGMII_MODE_FORCE, ++ AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE | ++ AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_SEL, ++ ck_gen_mode | ++ AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_SEL); ++ } ++ ++ regmap_update_bits(maps->multi_sgmii, AIROHA_PCS_MULTI_SGMII_SGMII_STS_CTRL_0, ++ AIROHA_PCS_LINK_MODE_P0 | ++ AIROHA_PCS_FORCE_SPD_MODE_P0, ++ speed_reg | ++ AIROHA_PCS_FORCE_SPD_MODE_P0); ++ } ++ } ++ ++ if (data->link_up) ++ data->link_up(priv, port->index); ++ ++ /* BPI BMI enable */ ++ regmap_clear_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_RXMPI_STOP | ++ AIROHA_PCS_XFI_RXMBI_STOP | ++ AIROHA_PCS_XFI_TXMPI_STOP | ++ AIROHA_PCS_XFI_TXMBI_STOP); ++} ++ ++static void airoha_pcs_link_down(struct phylink_pcs *pcs) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ struct airoha_pcs_maps *maps; ++ ++ maps = &priv->maps[port->index]; ++ ++ /* MPI MBI disable */ ++ regmap_set_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_RXMPI_STOP | ++ AIROHA_PCS_XFI_RXMBI_STOP | ++ AIROHA_PCS_XFI_TXMPI_STOP | ++ AIROHA_PCS_XFI_TXMBI_STOP); ++} ++ ++static void airoha_pcs_pre_config(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ struct airoha_pcs_maps *maps; ++ ++ maps = &priv->maps[port->index]; ++ ++ /* Select HSGMII or USXGMII in SCU regs */ ++ airoha_pcs_setup_scu(priv, port->index, interface); ++ ++ /* MPI MBI disable */ ++ regmap_set_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_RXMPI_STOP | ++ AIROHA_PCS_XFI_RXMBI_STOP | ++ AIROHA_PCS_XFI_TXMPI_STOP | ++ AIROHA_PCS_XFI_TXMBI_STOP); ++ ++ /* Write 1 to trigger reset and clear */ ++ regmap_clear_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_LOGIC_RST, ++ AIROHA_PCS_XFI_MAC_LOGIC_RST); ++ regmap_set_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_LOGIC_RST, ++ AIROHA_PCS_XFI_MAC_LOGIC_RST); ++ ++ usleep_range(1000, 2000); ++ ++ /* Clear XFI MAC counter */ ++ regmap_set_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_CNT_CLR, ++ AIROHA_PCS_XFI_GLB_CNT_CLR); ++} ++ ++static int airoha_pcs_post_config(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ struct airoha_pcs_port *port = to_airoha_pcs_port(pcs); ++ struct airoha_pcs_priv *priv = port->priv; ++ struct airoha_pcs_maps *maps; ++ ++ maps = &priv->maps[port->index]; ++ ++ /* Frag disable */ ++ regmap_update_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_RX_FRAG_LEN, ++ FIELD_PREP(AIROHA_PCS_XFI_RX_FRAG_LEN, 31)); ++ regmap_update_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_TX_FRAG_LEN, ++ FIELD_PREP(AIROHA_PCS_XFI_TX_FRAG_LEN, 31)); ++ ++ /* IPG NUM */ ++ regmap_update_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_IPG_NUM, ++ FIELD_PREP(AIROHA_PCS_XFI_IPG_NUM, 10)); ++ ++ /* Enable TX/RX flow control */ ++ regmap_set_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_TX_FC_EN); ++ regmap_set_bits(maps->pcs_mac, AIROHA_PCS_XFI_MAC_XFI_GIB_CFG, ++ AIROHA_PCS_XFI_RX_FC_EN); ++ ++ return 0; ++} ++ ++static unsigned int airoha_pcs_inband_caps(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ return LINK_INBAND_ENABLE | LINK_INBAND_DISABLE; ++} ++ ++static const struct phylink_pcs_ops airoha_pcs_ops = { ++ .pcs_inband_caps = airoha_pcs_inband_caps, ++ .pcs_pre_config = airoha_pcs_pre_config, ++ .pcs_post_config = airoha_pcs_post_config, ++ .pcs_get_state = airoha_pcs_get_state, ++ .pcs_config = airoha_pcs_config, ++ .pcs_an_restart = airoha_pcs_an_restart, ++ .pcs_link_up = airoha_pcs_link_up, ++ .pcs_link_down = airoha_pcs_link_down, ++}; ++ ++static int airoha_pcs_init_named_regmap(struct platform_device *pdev, ++ const char *name, struct regmap **regmap) ++{ ++ struct regmap_config regmap_config = { }; ++ void *base; ++ ++ base = devm_platform_ioremap_resource_byname(pdev, name); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap_config.name = name; ++ regmap_config.reg_bits = 32, ++ regmap_config.val_bits = 32, ++ regmap_config.reg_stride = 4, ++ ++ *regmap = devm_regmap_init_mmio(&pdev->dev, base, ®map_config); ++ ++ return PTR_ERR_OR_ZERO(*regmap); ++} ++ ++static int airoha_pcs_alloc_maps(struct platform_device *pdev, ++ struct airoha_pcs_priv *priv) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[0]; ++ int ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_mac", &maps->pcs_mac); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_an", &maps->hsgmii_an); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_pcs", &maps->hsgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_rate_adp", &maps->hsgmii_rate_adp); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "multi_sgmii", &maps->multi_sgmii); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "usxgmii", &maps->usxgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_pma", &priv->pcs_pma[0]); ++ if (ret) ++ return ret; ++ ++ return airoha_pcs_init_named_regmap(pdev, "pcs_ana", &priv->pcs_ana); ++} ++ ++static int airoha_pcs_usb_alloc_maps(struct platform_device *pdev, ++ struct airoha_pcs_priv *priv) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[0]; ++ int ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_mac", &maps->pcs_mac); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_an", &maps->hsgmii_an); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_pcs", &maps->hsgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_rate_adp", &maps->hsgmii_rate_adp); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "multi_sgmii", &maps->multi_sgmii); ++ if (ret) ++ return ret; ++ ++ return airoha_pcs_init_named_regmap(pdev, "pcs_ana", &priv->pcs_ana); ++} ++ ++static int airoha_pcs_pcie_alloc_maps(struct platform_device *pdev, ++ struct airoha_pcs_priv *priv) ++{ ++ struct airoha_pcs_maps *maps = priv->maps; ++ int ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_mac0", &maps[0].pcs_mac); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_an0", &maps[0].hsgmii_an); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_pcs0", &maps[0].hsgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_rate_adp0", &maps[0].hsgmii_rate_adp); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "multi_sgmii0", &maps[0].multi_sgmii); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "usxgmii0", &maps[0].usxgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_mac1", &maps[1].pcs_mac); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_an1", &maps[1].hsgmii_an); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_pcs1", &maps[1].hsgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_rate_adp1", &maps[1].hsgmii_rate_adp); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "multi_sgmii1", &maps[1].multi_sgmii); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "usxgmii1", &maps[1].usxgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_pma0", &priv->pcs_pma[0]); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_pma1", &priv->pcs_pma[1]); ++ if (ret) ++ return ret; ++ ++ return airoha_pcs_init_named_regmap(pdev, "pcs_ana", &priv->pcs_ana); ++} ++ ++static struct phylink_pcs *airoha_pcs_get(struct fwnode_reference_args *pcsspec, ++ void *data) ++{ ++ struct airoha_pcs_priv *priv = data; ++ struct device *dev = priv->dev; ++ int index = 0; ++ ++ switch (priv->data->port_type) { ++ case AIROHA_PCS_ETH: ++ case AIROHA_PCS_PON: ++ case AIROHA_PCS_USB: ++ if (pcsspec->nargs) { ++ dev_err(dev, "invalid number of cells in 'pcs' property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ break; ++ case AIROHA_PCS_PCIE: ++ if (pcsspec->nargs != 1) { ++ dev_err(dev, "invalid number of cells in 'pcs' property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ break; ++ } ++ ++ if (pcsspec->nargs) ++ index = pcsspec->args[0]; ++ ++ return &priv->ports[index].pcs; ++} ++ ++static int airoha_pcs_probe(struct platform_device *pdev) ++{ ++ const struct airoha_pcs_match_data *data; ++ struct device *dev = &pdev->dev; ++ struct airoha_pcs_priv *priv; ++ int index, ret; ++ ++ data = of_device_get_match_data(dev); ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->ports = devm_kcalloc(dev, data->num_port, ++ sizeof(*priv->ports), GFP_KERNEL); ++ if (!priv->ports) ++ return -ENOMEM; ++ ++ priv->dev = dev; ++ priv->data = data; ++ ++ if (data->port_type == AIROHA_PCS_USB) { ++ struct phy *phy; ++ ++ phy = devm_phy_get(dev, NULL); ++ if (IS_ERR(phy)) ++ return dev_err_probe(dev, PTR_ERR(phy), "failed to get phy\n"); ++ ++ priv->phy = phy; ++ } ++ ++ switch (data->port_type) { ++ case AIROHA_PCS_ETH: ++ case AIROHA_PCS_PON: ++ ret = airoha_pcs_alloc_maps(pdev, priv); ++ if (ret) ++ return ret; ++ ++ break; ++ case AIROHA_PCS_PCIE: ++ ret = airoha_pcs_pcie_alloc_maps(pdev, priv); ++ if (ret) ++ return ret; ++ ++ break; ++ case AIROHA_PCS_USB: ++ ret = airoha_pcs_usb_alloc_maps(pdev, priv); ++ if (ret) ++ return ret; ++ ++ break; ++ } ++ ++ if (data->alloc_regmap_fields) { ++ ret = data->alloc_regmap_fields(priv); ++ if (ret) ++ return ret; ++ } ++ ++ /* SCU is used to toggle XFI or HSGMII in global SoC registers */ ++ if (!priv->phy) { ++ priv->scu = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,scu"); ++ if (IS_ERR(priv->scu)) ++ return PTR_ERR(priv->scu); ++ } ++ ++ priv->rsts[0].id = "mac"; ++ priv->rsts[1].id = "phy"; ++ ret = devm_reset_control_bulk_get_optional_exclusive(dev, ARRAY_SIZE(priv->rsts), ++ priv->rsts); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to get bulk reset lines\n"); ++ ++ /* For Ethernet PCS, read the AN7581 SoC revision to check if ++ * manual rx calibration is needed. This is only limited to ++ * any SoC revision before E2. ++ */ ++ if (device_is_compatible(dev, "airoha,an7581-pcs-eth")) { ++ u32 val; ++ ++ ret = regmap_read(priv->scu, AIROHA_SCU_PDIDR, &val); ++ if (ret) ++ return ret; ++ ++ if (FIELD_GET(AIROHA_SCU_PRODUCT_ID, val) < 0x2) ++ priv->manual_rx_calib = true; ++ } ++ ++ for (index = 0; index < data->num_port; index++) { ++ struct airoha_pcs_port *port = &priv->ports[index]; ++ ++ port->priv = priv; ++ port->index = index; ++ port->pcs.poll = true; ++ port->pcs.neg_mode = true; ++ port->pcs.ops = &airoha_pcs_ops; ++ ++ switch (data->port_type) { ++ case AIROHA_PCS_ETH: ++ case AIROHA_PCS_PON: ++ case AIROHA_PCS_PCIE: ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ port->pcs.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, ++ port->pcs.supported_interfaces); ++ fallthrough; ++ case AIROHA_PCS_USB: ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ port->pcs.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ port->pcs.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ port->pcs.supported_interfaces); ++ break; ++ } ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ ++ return fwnode_pcs_add_provider(dev_fwnode(dev), airoha_pcs_get, ++ priv); ++} ++ ++static void airoha_pcs_remove(struct platform_device *pdev) ++{ ++ struct airoha_pcs_priv *priv = platform_get_drvdata(pdev); ++ const struct airoha_pcs_match_data *data = priv->data; ++ int i; ++ ++ fwnode_pcs_del_provider(dev_fwnode(&pdev->dev)); ++ ++ rtnl_lock(); ++ ++ for (i = 0; i < data->num_port; i++) { ++ struct airoha_pcs_port *port = &priv->ports[i]; ++ ++ phylink_release_pcs(&port->pcs); ++ } ++ ++ rtnl_unlock(); ++} ++ ++static const struct airoha_pcs_match_data an7581_pcs_eth = { ++ .num_port = 1, ++ .port_type = AIROHA_PCS_ETH, ++ .alloc_regmap_fields = an7581_pcs_alloc_regmap_fields, ++ .bringup = an7581_pcs_bringup, ++ .link_up = an7581_pcs_phya_link_up, ++ .rxlock_workaround = an7581_pcs_rxlock_workaround, ++}; ++ ++static const struct airoha_pcs_match_data an7581_pcs_pon = { ++ .num_port = 1, ++ .port_type = AIROHA_PCS_PON, ++ .alloc_regmap_fields = an7581_pcs_alloc_regmap_fields, ++ .bringup = an7581_pcs_bringup, ++ .link_up = an7581_pcs_phya_link_up, ++}; ++ ++static const struct airoha_pcs_match_data an7581_pcs_pcie = { ++ .num_port = 2, ++ .port_type = AIROHA_PCS_PCIE, ++ .alloc_regmap_fields = an7581_pcs_pcie_alloc_regmap_fields, ++ .bringup = an7581_pcs_bringup, ++ .link_up = an7581_pcs_phya_link_up, ++}; ++ ++static const struct airoha_pcs_match_data an7581_pcs_usb = { ++ .num_port = 1, ++ .port_type = AIROHA_PCS_USB, ++ .bringup = an7581_pcs_usb_bringup, ++}; ++ ++static const struct of_device_id airoha_pcs_of_table[] = { ++ { .compatible = "airoha,an7581-pcs-eth", .data = &an7581_pcs_eth }, ++ { .compatible = "airoha,an7581-pcs-pon", .data = &an7581_pcs_pon }, ++ { .compatible = "airoha,an7581-pcs-pcie", .data = &an7581_pcs_pcie }, ++ { .compatible = "airoha,an7581-pcs-usb", .data = &an7581_pcs_usb }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, airoha_pcs_of_table); ++ ++static struct platform_driver airoha_pcs_driver = { ++ .driver = { ++ .name = "airoha-pcs", ++ .of_match_table = airoha_pcs_of_table, ++ }, ++ .probe = airoha_pcs_probe, ++ .remove = airoha_pcs_remove, ++}; ++module_platform_driver(airoha_pcs_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Airoha PCS driver"); ++MODULE_AUTHOR("Christian Marangi "); +--- /dev/null ++++ b/drivers/net/pcs/airoha/pcs-airoha.h +@@ -0,0 +1,1309 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* SCU*/ ++#define AIROHA_SCU_PDIDR 0x5c ++#define AIROHA_SCU_PRODUCT_ID GENMASK(15, 0) ++#define AIROHA_SCU_WAN_CONF 0x70 ++#define AIROHA_SCU_WAN_SEL GENMASK(7, 0) ++#define AIROHA_SCU_WAN_SEL_SGMII FIELD_PREP_CONST(AIROHA_SCU_WAN_SEL, 0x10) ++#define AIROHA_SCU_WAN_SEL_HSGMII FIELD_PREP_CONST(AIROHA_SCU_WAN_SEL, 0x11) ++#define AIROHA_SCU_WAN_SEL_USXGMII FIELD_PREP_CONST(AIROHA_SCU_WAN_SEL, 0x12) ++#define AIROHA_SCU_SSR3 0x94 ++#define AIROHA_SCU_ETH_XSI_SEL GENMASK(14, 13) ++#define AIROHA_SCU_ETH_XSI_USXGMII FIELD_PREP_CONST(AIROHA_SCU_ETH_XSI_SEL, 0x1) ++#define AIROHA_SCU_ETH_XSI_HSGMII FIELD_PREP_CONST(AIROHA_SCU_ETH_XSI_SEL, 0x2) ++#define AIROHA_SCU_SSTR 0x9c ++#define AIROHA_SCU_PCIE_XSI0_SEL GENMASK(14, 13) ++#define AIROHA_SCU_PCIE_XSI0_USXGMII FIELD_PREP_CONST(AIROHA_SCU_PCIE_XSI0_SEL, 0x1) ++#define AIROHA_SCU_PCIE_XSI0_HSGMII FIELD_PREP_CONST(AIROHA_SCU_PCIE_XSI0_SEL, 0x2) ++#define AIROHA_SCU_PCIE_XSI1_SEL GENMASK(12, 11) ++#define AIROHA_SCU_PCIE_XSI1_USXGMII FIELD_PREP_CONST(AIROHA_SCU_PCIE_XSI1_SEL, 0x1) ++#define AIROHA_SCU_PCIE_XSI1_HSGMII FIELD_PREP_CONST(AIROHA_SCU_PCIE_XSI1_SEL, 0x2) ++#define AIROHA_SCU_PON_XSI_SEL GENMASK(10, 9) ++#define AIROHA_SCU_PON_XSI_USXGMII FIELD_PREP_CONST(AIROHA_SCU_PON_XSI_SEL, 0x1) ++#define AIROHA_SCU_PON_XSI_HSGMII FIELD_PREP_CONST(AIROHA_SCU_PON_XSI_SEL, 0x2) ++ ++/* XFI_MAC */ ++#define AIROHA_PCS_XFI_MAC_XFI_GIB_CFG 0x0 ++#define AIROHA_PCS_XFI_RX_FRAG_LEN GENMASK(26, 22) ++#define AIROHA_PCS_XFI_TX_FRAG_LEN GENMASK(21, 17) ++#define AIROHA_PCS_XFI_IPG_NUM GENMASK(15, 10) ++#define AIROHA_PCS_XFI_TX_FC_EN BIT(5) ++#define AIROHA_PCS_XFI_RX_FC_EN BIT(4) ++#define AIROHA_PCS_XFI_RXMPI_STOP BIT(3) ++#define AIROHA_PCS_XFI_RXMBI_STOP BIT(2) ++#define AIROHA_PCS_XFI_TXMPI_STOP BIT(1) ++#define AIROHA_PCS_XFI_TXMBI_STOP BIT(0) ++#define AIROHA_PCS_XFI_MAC_XFI_LOGIC_RST 0x10 ++#define AIROHA_PCS_XFI_MAC_LOGIC_RST BIT(0) ++#define AIROHA_PCS_XFI_MAC_XFI_MACADDRH 0x60 ++#define AIROHA_PCS_XFI_MAC_MACADDRH GENMASK(15, 0) ++#define AIROHA_PCS_XFI_MAC_XFI_MACADDRL 0x64 ++#define AIROHA_PCS_XFI_MAC_MACADDRL GENMASK(31, 0) ++#define AIROHA_PCS_XFI_MAC_XFI_CNT_CLR 0x100 ++#define AIROHA_PCS_XFI_GLB_CNT_CLR BIT(0) ++ ++/* HSGMII_AN */ ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_0 0x0 ++#define AIROHA_PCS_HSGMII_AN_SGMII_RESET_PHY BIT(15) ++#define AIROHA_PCS_HSGMII_AN_SGMII_RA_ENABLE BIT(12) ++#define AIROHA_PCS_HSGMII_AN_SGMII_AN_RESTART BIT(9) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_1 0x4 /* BMSR */ ++#define AIROHA_PCS_HSGMII_AN_SGMII_UNIDIR_ABILITY BIT(6) ++#define AIROHA_PCS_HSGMII_AN_SGMII_AN_COMPLETE BIT(5) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT BIT(4) ++#define AIROHA_PCS_HSGMII_AN_SGMII_AN_ABILITY BIT(3) ++#define AIROHA_PCS_HSGMII_AN_SGMII_LINK_STATUS BIT(2) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_4 0x10 ++#define AIROHA_PCS_HSGMII_AN_SGMII_DEV_ABILITY GENMASK(15, 0) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_5 0x14 /* LPA */ ++#define AIROHA_PCS_HSGMII_AN_SGMII_PARTNER_ABILITY GENMASK(15, 0) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_11 0x2c ++#define AIROHA_PCS_HSGMII_AN_SGMII_LINK_TIMER GENMASK(19, 0) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_13 0x34 ++#define AIROHA_PCS_HSGMII_AN_SGMII_REMOTE_FAULT_DIS BIT(8) ++#define AIROHA_PCS_HSGMII_AN_SGMII_IF_MODE_5_0 GENMASK(5, 0) ++#define AIROHA_PCS_HSGMII_AN_SGMII_COMPAT_EN BIT(5) ++#define AIROHA_PCS_HSGMII_AN_DUPLEX_FORCE_MODE BIT(4) ++#define AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE GENMASK(3, 2) ++#define AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_1000 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE, 0x2) ++#define AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_100 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE, 0x1) ++#define AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE_10 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_AN_SPEED_FORCE_MODE, 0x0) ++#define AIROHA_PCS_HSGMII_AN_SIDEBAND_EN BIT(1) ++#define AIROHA_PCS_HSGMII_AN_SGMII_EN BIT(0) ++#define AIROHA_PCS_HSGMII_AN_SGMII_REG_AN_FORCE_CL37 0x60 ++#define AIROHA_PCS_HSGMII_AN_FORCE_AN_DONE BIT(0) ++ ++/* HSGMII_PCS */ ++#define AIROHA_PCS_HSGMII_PCS_CTROL_1 0x0 ++#define AIROHA_PCS_TBI_10B_MODE BIT(30) ++#define AIROHA_PCS_SGMII_SEND_AN_ERR_EN BIT(24) ++#define AIROHA_PCS_REMOTE_FAULT_DIS BIT(12) ++#define AIROHA_PCS_HSGMII_PCS_CTROL_3 0x8 ++#define AIROHA_PCS_HSGMII_PCS_LINK_STSTIME GENMASK(19, 0) ++#define AIROHA_PCS_HSGMII_PCS_CTROL_6 0x14 ++#define AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_10 BIT(14) ++#define AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_100 BIT(13) ++#define AIROHA_PCS_HSGMII_PCS_SGMII_SPD_FORCE_1000 BIT(12) ++#define AIROHA_PCS_HSGMII_PCS_MAC_MODE BIT(8) ++#define AIROHA_PCS_HSGMII_PCS_TX_ENABLE BIT(4) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL GENMASK(3, 2) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_1000 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL, 0x0) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_100 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL, 0x1) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL_10 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT_VAL, 0x2) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_RATEADAPT BIT(1) ++#define AIROHA_PCS_HSGMII_PCS_MODE2_EN BIT(0) ++#define AIROHA_PCS_HSGMII_PCS_HSGMII_MODE_INTERRUPT 0x20 ++#define AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT_CLEAR BIT(11) ++#define AIROHA_PCS_HSGMII_MODE2_REMOVE_FAULT_OCCUR_INT BIT(10) ++#define AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT_CLEAR BIT(9) ++#define AIROHA_PCS_HSGMII_MODE2_AN_CL37_TIMERDONE_INT BIT(8) ++#define AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT_CLEAR BIT(5) ++#define AIROHA_PCS_HSGMII_MODE2_AN_MIS_INT BIT(4) ++#define AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT_CLEAR BIT(3) ++#define AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT_CLEAR BIT(2) ++#define AIROHA_PCS_HSGMII_MODE2_RX_SYN_DONE_INT BIT(1) ++#define AIROHA_PCS_HSGMII_MODE2_AN_DONE_INT BIT(0) ++#define AIROHA_PCS_HSGMII_PCS_AN_SGMII_MODE_FORCE 0x24 ++#define AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE GENMASK(5, 4) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_1000 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE, 0x0) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_100 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE, 0x1) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_10 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE, 0x2) ++#define AIROHA_PCS_HSGMII_PCS_FORCE_CUR_SGMII_MODE_SEL BIT(0) ++#define ARIOHA_PCS_HSGMII_PCS_STATE_2 0x104 ++#define AIROHA_PCS_HSGMII_PCS_RX_SYNC BIT(5) ++#define AIROHA_PCS_HSGMII_PCS_AN_DONE BIT(0) ++#define AIROHA_PCS_HSGMII_PCS_INT_STATE 0x15c ++#define AIROHA_PCS_HSGMII_PCS_MODE2_REMOTE_FAULT_OCCUR_INT BIT(4) ++#define AIROHA_PCS_HSGMII_PCS_MODE2_AN_MLS BIT(3) ++#define AIROHA_PCS_HSGMII_PCS_MODE2_AN_CL37_TIMERDONE_INT BIT(2) ++#define AIROHA_PCS_HSGMII_PCS_MODE2_RX_SYNC BIT(1) ++#define AIROHA_PCS_HSGMII_PCS_MODE2_AN_DONE BIT(0) ++ ++/* HSGMII_ANA */ ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_6 0x18 ++#define AIROHA_PCS_HSGMII_ANA_FORCE_CDR_BIC BIT(20) ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_8 0x20 ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTR GENMASK(11, 8) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTD1 GENMASK(7, 4) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTD0 GENMASK(3, 0) ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_11 0x2c ++#define AIROHA_PCS_HSGMII_ANA_TPHY_SPEED GENMASK(3, 2) ++#define AIROHA_PCS_HSGMII_ANA_TPHY_SPEED_SGMII FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_TPHY_SPEED, 0x0) ++#define AIROHA_PCS_HSGMII_ANA_TPHY_SPEED_HSGMII FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_TPHY_SPEED, 0x1) ++#define AIROHA_PCS_HSGMII_ANA_TPHY_MODE GENMASK(1, 0) ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_18 0x48 ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_BG_DIV GENMASK(28, 27) ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_19 0x4c ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE GENMASK(25, 10) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_HV GENMASK(15, 8) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_LV GENMASK(7, 0) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG GENMASK(2, 0) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_GND FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x0) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_MONFBK_CK FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x1) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_MONPLL_CK FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x2) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_MONREF_CK FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x3) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_SSUSB_SYSPLL_CKMON FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x4) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_SSUSB_SYSPLL_FBCKMON FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x5) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_TX2500M_A FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x6) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_SSUSB_CDR_250M_CK FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_MONCKBG, 0x7) ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_24 0x60 ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RESERVE GENMASK(31, 24) ++#define AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_26 0x68 ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY GENMASK(7, 6) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY_32 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY, 0x0) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY_64 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY, 0x1) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY_128 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY, 0x2) ++#define AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY_216 FIELD_PREP_CONST(AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY, 0x3) ++ ++/* MULTI_SGMII */ ++#define AIROHA_PCS_MULTI_SGMII_INTERRUPT_EN_0 0x14 ++#define AIROHA_PCS_MULTI_SGMII_PCS_INT_EN_0 BIT(0) ++#define AIROHA_PCS_MULTI_SGMII_SGMII_STS_CTRL_0 0x18 ++#define AIROHA_PCS_LINK_MODE_P0 GENMASK(5, 4) ++#define AIROHA_PCS_LINK_MODE_P0_2_5G FIELD_PREP_CONST(AIROHA_PCS_LINK_MODE_P0, 0x3) ++#define AIROHA_PCS_LINK_MODE_P0_1G FIELD_PREP_CONST(AIROHA_PCS_LINK_MODE_P0, 0x2) ++#define AIROHA_PCS_LINK_MODE_P0_100M FIELD_PREP_CONST(AIROHA_PCS_LINK_MODE_P0, 0x1) ++#define AIROHA_PCS_LINK_MODE_P0_10M FIELD_PREP_CONST(AIROHA_PCS_LINK_MODE_P0, 0x0) ++#define AIROHA_PCS_FORCE_SPD_MODE_P0 BIT(2) ++#define AIROHA_PCS_FORCE_LINKDOWN_P0 BIT(1) ++#define AIROHA_PCS_FORCE_LINKUP_P0 BIT(0) ++#define AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0 0x100 ++#define AIROHA_PCS_HSGMII_XFI_SEL BIT(28) ++#define AIROHA_PCS_MULTI_SGMII_INTERRUPT_SEL 0x14c ++#define AIROHA_PCS_HSGMII_PCS_INT BIT(0) ++#define AIROHA_PCS_MULTI_SGMII_MSG_RX_STS_15 0x43c ++#define AIROHA_PCS_LINK_STS_P0 BIT(3) ++#define AIROHA_PCS_SPEED_STS_P0 GENMASK(2, 0) ++#define AIROHA_PCS_SPEED_STS_P0_1G FIELD_PREP_CONST(AIROHA_PCS_SPEED_STS_P0, 0x2) ++#define AIROHA_PCS_SPEED_STS_P0_100M FIELD_PREP_CONST(AIROHA_PCS_SPEED_STS_P0, 0x1) ++#define AIROHA_PCS_SPEED_STS_P0_10M FIELD_PREP_CONST(AIROHA_PCS_SPEED_STS_P0, 0x0) ++#define AIROHA_PCS_MULTI_SGMII_MSG_RX_STS_18 0x448 ++#define AIROHA_PCS_P0_SGMII_IS_10 BIT(2) ++#define AIROHA_PCS_P0_SGMII_IS_100 BIT(1) ++#define AIROHA_PCS_P0_SGMII_IS_1000 BIT(0) ++ ++/* HSGMII_RATE_ADP */ ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_0 0x0 ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_RX_BYPASS BIT(27) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_TX_BYPASS BIT(26) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_RX_EN BIT(4) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_TX_EN BIT(0) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_1 0x4 ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_WR_THR GENMASK(20, 16) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_RD_THR GENMASK(28, 24) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_6 0x18 ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_L GENMASK(31, 0) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_8 0x20 ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_RX_AFIFO_DOUT_C GENMASK(7, 0) ++#define AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_11 0x2c ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_EN BIT(8) ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE GENMASK(15, 12) ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_10000 \ ++ FIELD_PREP_CONST(AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE, 0x0) ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_5000 \ ++ FIELD_PREP_CONST(AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE, 0x1) ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_2500 \ ++ FIELD_PREP_CONST(AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE, 0x2) ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_1000 \ ++ FIELD_PREP_CONST(AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE, 0x4) ++#define AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE_100 \ ++ FIELD_PREP_CONST(AIROHA_PCS_HSGMII_RATE_ADPT_FORCE_RATE_ADAPT_MODE, 0x6) ++#define AIROHA_PCS_HSGMII_RATE_ADP_P0_CTRL_0 0x100 ++#define AIROHA_PCS_HSGMII_P0_DIS_MII_MODE BIT(31) ++ ++/* USXGMII */ ++#define AIROHA_PCS_USXGMII_PCS_CTROL_1 0x0 ++#define AIROHA_PCS_USXGMII_SPEED_SEL_H BIT(13) ++#define AIROHA_PCS_USXGMII_PCS_STUS_1 0x4 ++#define AIROHA_PCS_USXGMII_PCS_RX_LINK_STATUS BIT(2) ++#define AIROHA_PCS_USXGMII_PCS_RX_LINK_STATUS_UP \ ++ FIELD_PREP_CONST(AIROHA_PCS_USXGMII_PCS_RX_LINK_STATUS, 0x1) ++#define AIROHA_PCS_USXGMII_PCS_RX_LINK_STATUS_DOWN \ ++ FIELD_PREP_CONST(AIROHA_PCS_USXGMII_PCS_RX_LINK_STATUS, 0x0) ++#define AIROHA_PCS_USXGMII_BASE_R_10GB_T_PCS_STUS_1 0x30 ++#define AIROHA_PCS_USXGMII_RX_LINK_STUS BIT(12) ++#define AIROHA_PCS_USXGMII_PRBS9_PATT_TST_ABILITY BIT(3) ++#define AIROHA_PCS_USXGMII_PRBS31_PATT_TST_ABILITY BIT(2) ++#define AIROHA_PCS_USXGMII_PCS_BLK_LK BIT(0) ++#define AIROHA_PCS_USGMII_VENDOR_DEFINE_116 0x22c ++#define AIROHA_PCS_USXGMII_PCS_CTRL_0 0x2c0 ++#define AIROHA_PCS_USXGMII_T_TYPE_T_INT_EN BIT(24) ++#define AIROHA_PCS_USXGMII_T_TYPE_D_INT_EN BIT(16) ++#define AIROHA_PCS_USXGMII_T_TYPE_C_INT_EN BIT(8) ++#define AIROHA_PCS_USXGMII_T_TYPE_S_INT_EN BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_CTRL_1 0x2c4 ++#define AIROHA_PCS_USXGMII_R_TYPE_C_INT_EN BIT(24) ++#define AIROHA_PCS_USXGMII_R_TYPE_S_INT_EN BIT(16) ++#define AIROHA_PCS_USXGMII_TXPCS_FSM_ENC_ERR_INT_EN BIT(8) ++#define AIROHA_PCS_USXGMII_T_TYPE_E_INT_EN BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_CTRL_2 0x2c8 ++#define AIROHA_PCS_USXGMII_RPCS_FSM_DEC_ERR_INT_EN BIT(24) ++#define AIROHA_PCS_USXGMII_R_TYPE_E_INT_EN BIT(16) ++#define AIROHA_PCS_USXGMII_R_TYPE_T_INT_EN BIT(8) ++#define AIROHA_PCS_USXGMII_R_TYPE_D_INT_EN BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_CTRL_3 0x2cc ++#define AIROHA_PCS_USXGMII_FAIL_SYNC_XOR_ST_INT_EN BIT(24) ++#define AIROHA_PCS_USXGMII_RX_BLOCK_LOCK_ST_INT_EN BIT(16) ++#define AIROHA_PCS_USXGMII_LINK_UP_ST_INT_EN BIT(8) ++#define AIROHA_PCS_USXGMII_HI_BER_ST_INT_EN BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_INT_STA_2 0x2d8 ++#define AIROHA_PCS_USXGMII_RPCS_FSM_DEC_ERR_INT BIT(24) ++#define AIROHA_PCS_USXGMII_R_TYPE_E_INT BIT(16) ++#define AIROHA_PCS_USXGMII_R_TYPE_T_INT BIT(8) ++#define AIROHA_PCS_USXGMII_R_TYPE_D_INT BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_INT_STA_3 0x2dc ++#define AIROHA_PCS_USXGMII_FAIL_SYNC_XOR_ST_INT BIT(24) ++#define AIROHA_PCS_USXGMII_RX_BLOCK_LOCK_ST_INT BIT(16) ++#define AIROHA_PCS_USXGMII_LINK_UP_ST_INT BIT(8) ++#define AIROHA_PCS_USXGMII_HI_BER_ST_INT BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_CTRL_4 0x2e0 ++#define AIROHA_PCS_USXGMII_LINK_DOWN_ST_INT_EN BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_INT_STA_4 0x2e4 ++#define AIROHA_PCS_USXGMII_LINK_DOWN_ST_INT BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_AN_CONTROL_0 0x2f8 ++#define AIROHA_PCS_USXGMII_AN_RESTART BIT(8) ++#define AIROHA_PCS_USXGMII_AN_ENABLE BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_AN_STATS_0 0x310 ++#define AIROHA_PCS_USXGMII_CUR_USXGMII_MODE GENMASK(30, 28) ++#define AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_10G FIELD_PREP_CONST(AIROHA_PCS_USXGMII_CUR_USXGMII_MODE, 0x0) ++#define AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_5G FIELD_PREP_CONST(AIROHA_PCS_USXGMII_CUR_USXGMII_MODE, 0x1) ++#define AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_2_5G FIELD_PREP_CONST(AIROHA_PCS_USXGMII_CUR_USXGMII_MODE, 0x2) ++#define AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_1G FIELD_PREP_CONST(AIROHA_PCS_USXGMII_CUR_USXGMII_MODE, 0x3) ++#define AIROHA_PCS_USXGMII_CUR_USXGMII_MODE_100M FIELD_PREP_CONST(AIROHA_PCS_USXGMII_CUR_USXGMII_MODE, 0x4) ++#define AIROHA_PCS_USXGMII_PARTNER_ABILITY GENMASK(15, 0) ++#define AIROHA_PCS_USXGMII_PCS_AN_STATS_2 0x318 ++#define AIROHA_PCS_USXGMII_PCS_AN_COMPLETE BIT(24) ++#define AIROHA_PCS_USXGMII_PCS_AN_CONTROL_6 0x31c ++#define AIROHA_PCS_USXGMII_TOG_PCS_AUTONEG_STS BIT(0) ++#define AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7 0x320 ++#define AIROHA_PCS_USXGMII_RATE_UPDATE_MODE BIT(12) ++#define AIROHA_PCS_USXGMII_MODE GENMASK(10, 8) ++#define AIROHA_PCS_USXGMII_MODE_10000 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x0) ++#define AIROHA_PCS_USXGMII_MODE_5000 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x1) ++#define AIROHA_PCS_USXGMII_MODE_2500 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x2) ++#define AIROHA_PCS_USXGMII_MODE_1000 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x3) ++#define AIROHA_PCS_USXGMII_MODE_100 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x4) ++ ++/* PMA_PHYA */ ++#define AIROHA_PCS_ANA_PXP_CMN_EN 0x0 ++#define AIROHA_PCS_ANA_CMN_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_IB_EXT_EN 0x4 ++#define AIROHA_PCS_ANA_JCPLL_CHP_IOFST GENMASK(29, 24) ++#define AIROHA_PCS_ANA_JCPLL_CHP_IBIAS GENMASK(21, 16) ++#define AIROHA_PCS_ANA_JCPLL_LPF_SHCK_EN BIT(8) ++#define AIROHA_PCS_ANA_PXP_JCPLL_LPF_BR 0x8 ++#define AIROHA_PCS_ANA_JCPLL_LPF_BWR GENMASK(28, 24) ++#define AIROHA_PCS_ANA_JCPLL_LPF_BP GENMASK(20, 16) ++#define AIROHA_PCS_ANA_JCPLL_LPF_BC GENMASK(12, 8) ++#define AIROHA_PCS_ANA_JCPLL_LPF_BR GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_LPF_BWC 0xc ++#define AIROHA_PCS_ANA_JCPLL_KBAND_DIV GENMASK(26, 24) ++#define AIROHA_PCS_ANA_JCPLL_KBAND_CODE GENMASK(23, 16) ++#define AIROHA_PCS_ANA_JCPLL_KBAND_OPTION BIT(8) ++#define AIROHA_PCS_ANA_JCPLL_LPF_BWC GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_KBAND_KFC 0x10 ++#define AIROHA_PCS_ANA_JCPLL_KBAND_KS GENMASK(17, 16) ++#define AIROHA_PCS_ANA_JCPLL_KBAND_KF GENMASK(9, 8) ++#define AIROHA_PCS_ANA_JCPLL_KBAND_KFC GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_MMD_PREDIV_MODE 0x14 ++#define AIROHA_PCS_ANA_JCPLL_POSTDIV_D5 BIT(24) ++#define AIROHA_PCS_ANA_JCPLL_MMD_PREDIV_MODE GENMASK(1, 0) ++#define AIROHA_PCS_ANA_JCPLL_MMD_PREDIV_MODE_2 0x0 ++#define AIROHA_PCS_ANA_JCPLL_MMD_PREDIV_MODE_3 0x1 ++#define AIROHA_PCS_ANA_JCPLL_MMD_PREDIV_MODE_4 0x2 ++#define AIROHA_PCS_ANA_JCPLL_MMD_PREDIV_MODE_1 0x3 ++#define AIROHA_PCS_ANA_PXP_JCPLL_RST_DLY 0x1c ++#define AIROHA_PCS_ANA_JCPLL_SDM_DI_LS GENMASK(25, 24) ++#define AIROHA_PCS_ANA_JCPLL_SDM_DI_LS_2_23 0x0 ++#define AIROHA_PCS_ANA_JCPLL_SDM_DI_LS_2_21 0x1 ++#define AIROHA_PCS_ANA_JCPLL_SDM_DI_LS_2_19 0x2 ++#define AIROHA_PCS_ANA_JCPLL_SDM_DI_LS_2_15 0x3 ++#define AIROHA_PCS_ANA_JCPLL_SDM_DI_EN BIT(16) ++#define AIROHA_PCS_ANA_JCPLL_PLL_RSTB BIT(8) ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY GENMASK(2, 0) ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY_20_25 0x1 ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY_40_50 0x2 ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY_80_100 0x3 ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY_150_200 0x4 ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY_300_400 0x5 ++#define AIROHA_PCS_ANA_JCPLL_RST_DLY_600_800 0x6 ++#define AIROHA_PCS_ANA_PXP_JCPLL_SDM_IFM 0x20 ++#define AIROHA_PCS_ANA_JCPLL_SDM_OUT BIT(24) ++#define AIROHA_PCS_ANA_JCPLL_SDM_ORD GENMASK(17, 16) ++#define AIROHA_PCS_ANA_JCPLL_SDM_ORD_INT 0x0 ++#define AIROHA_PCS_ANA_JCPLL_SDM_ORD_1SDM 0x1 ++#define AIROHA_PCS_ANA_JCPLL_SDM_ORD_2SDM 0x2 ++#define AIROHA_PCS_ANA_JCPLL_SDM_ORD_3SDM 0x3 ++#define AIROHA_PCS_ANA_JCPLL_SDM_MODE GENMASK(9, 8) ++#define AIROHA_PCS_ANA_JCPLL_SDM_IFM BIT(0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_SDM_HREN 0x24 ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_VREF GENMASK(28, 24) ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN GENMASK(18, 16) ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN_2 0x0 ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN_4 0x1 ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN_6 0x2 ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN_8 0x3 ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN_10 0x4 ++#define AIROHA_PCS_ANA_JCPLL_TCL_AMP_EN BIT(8) ++#define AIROHA_PCS_ANA_JCPLL_SDM_HREN BIT(0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_TCL_CMP_EN 0x28 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW GENMASK(26, 24) ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_0_5 0x0 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_1 0x1 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_2 0x2 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_4 0x3 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_8 0x4 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_16 0x6 ++#define AIROHA_PCS_ANA_JCPLL_TCL_LPF_EN BIT(16) ++#define AIROHA_PCS_ANA_PXP_JCPLL_VCODIV 0x2c ++#define AIROHA_PCS_ANA_JCPLL_VCO_SCAPWR GENMASK(26, 24) ++#define AIROHA_PCS_ANA_JCPLL_VCO_HALFLSB_EN BIT(16) ++#define AIROHA_PCS_ANA_JCPLL_VCO_CFIX GENMASK(9, 8) ++#define AIROHA_PCS_ANA_JCPLL_VCODIV GENMASK(1, 0) ++#define AIROHA_PCS_ANA_JCPLL_VCODIV_1 0x0 ++#define AIROHA_PCS_ANA_JCPLL_VCODIV_2 0x1 ++#define AIROHA_PCS_ANA_PXP_JCPLL_VCO_TCLVAR 0x30 ++#define AIROHA_PCS_ANA_JCPLL_SSC_PHASE_INI BIT(17) ++#define AIROHA_PCS_ANA_JCPLL_SSC_EN BIT(16) ++#define AIROHA_PCS_ANA_JCPLL_VCO_VCOVAR_BIAS_L GENMASK(10, 8) ++#define AIROHA_PCS_ANA_JCPLL_VCO_VCOVAR_BIAS_H GENMASK(5, 3) ++#define AIROHA_PCS_ANA_JCPLL_VCO_TCLVAR GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_SSC_TRI_EN 0x34 ++#define AIROHA_PCS_ANA_JCPLL_SSC_DELTA1 GENMASK(23, 8) ++#define AIROHA_PCS_ANA_JCPLL_SSC_TRI_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_SSC_DELTA 0x38 ++#define AIROHA_PCS_ANA_JCPLL_SSC_PERIOD GENMASK(31, 16) ++#define AIROHA_PCS_ANA_JCPLL_SSC_DELTA GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_JCPLL_SPARE_H 0x48 ++#define AIROHA_PCS_ANA_JCPLL_TCL_KBAND_VREF GENMASK(20, 16) ++#define AIROHA_PCS_ANA_JCPLL_SPARE_L GENMASK(15, 8) ++#define AIROHA_PCS_ANA_JCPLL_SPARE_L_LDO BIT(5) ++#define AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS 0x50 ++#define AIROHA_PCS_ANA_TXPLL_LPF_BC GENMASK(28, 24) ++#define AIROHA_PCS_ANA_TXPLL_LPF_BR GENMASK(20, 16) ++#define AIROHA_PCS_ANA_TXPLL_CHP_IOFST GENMASK(13, 8) ++#define AIROHA_PCS_ANA_TXPLL_CHP_IBIAS GENMASK(5, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP 0x54 ++#define AIROHA_PCS_ANA_TXPLL_KBAND_OPTION BIT(24) ++#define AIROHA_PCS_ANA_TXPLL_LPF_BWC GENMASK(20, 16) ++#define AIROHA_PCS_ANA_TXPLL_LPF_BWR GENMASK(12, 8) ++#define AIROHA_PCS_ANA_TXPLL_LPF_BP GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_KBAND_CODE 0x58 ++#define AIROHA_PCS_ANA_TXPLL_KBAND_KF GENMASK(25, 24) ++#define AIROHA_PCS_ANA_TXPLL_KBAND_KFC GENMASK(17, 16) ++#define AIROHA_PCS_ANA_TXPLL_KBAND_DIV GENMASK(10, 8) ++#define AIROHA_PCS_ANA_TXPLL_KBAND_CODE GENMASK(7, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_KBAND_KS 0x5c ++#define AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE GENMASK(17, 16) ++#define AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_2 0x0 ++#define AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_3 0x1 ++#define AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_4 0x2 ++#define AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_1 0x3 ++#define AIROHA_PCS_ANA_TXPLL_POSTDIV_EN BIT(8) ++#define AIROHA_PCS_ANA_TXPLL_KBAND_KS GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL 0x64 ++#define AIROHA_PCS_ANA_TXPLL_PLL_RSTB BIT(24) ++#define AIROHA_PCS_ANA_TXPLL_RST_DLY GENMASK(18, 16) ++#define AIROHA_PCS_ANA_TXPLL_REFIN_DIV GENMASK(9, 8) ++#define AIROHA_PCS_ANA_TXPLL_REFIN_DIV_1 0x0 ++#define AIROHA_PCS_ANA_TXPLL_REFIN_DIV_2 0x1 ++#define AIROHA_PCS_ANA_TXPLL_REFIN_DIV_3 0x2 ++#define AIROHA_PCS_ANA_TXPLL_REFIN_DIV_4 0x3 ++#define AIROHA_PCS_ANA_TXPLL_REFIN_INTERNAL BIT(0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_SDM_DI_EN 0x68 ++#define AIROHA_PCS_ANA_TXPLL_SDM_MODE GENMASK(25, 24) ++#define AIROHA_PCS_ANA_TXPLL_SDM_IFM BIT(16) ++#define AIROHA_PCS_ANA_TXPLL_SDM_DI_LS GENMASK(9, 8) ++#define AIROHA_PCS_ANA_TXPLL_SDM_DI_LS_2_23 0x0 ++#define AIROHA_PCS_ANA_TXPLL_SDM_DI_LS_2_21 0x1 ++#define AIROHA_PCS_ANA_TXPLL_SDM_DI_LS_2_19 0x2 ++#define AIROHA_PCS_ANA_TXPLL_SDM_DI_LS_2_15 0x3 ++#define AIROHA_PCS_ANA_TXPLL_SDM_DI_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD 0x6c ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_EN BIT(24) ++#define AIROHA_PCS_ANA_TXPLL_SDM_HREN BIT(16) ++#define AIROHA_PCS_ANA_TXPLL_SDM_OUT BIT(8) ++#define AIROHA_PCS_ANA_TXPLL_SDM_ORD GENMASK(1, 0) ++#define AIROHA_PCS_ANA_TXPLL_SDM_ORD_INT 0x0 ++#define AIROHA_PCS_ANA_TXPLL_SDM_ORD_1SDM 0x1 ++#define AIROHA_PCS_ANA_TXPLL_SDM_ORD_2SDM 0x2 ++#define AIROHA_PCS_ANA_TXPLL_SDM_ORD_3SDM 0x3 ++#define AIROHA_PCS_ANA_PXP_TXPLL_TCL_AMP_GAIN 0x70 ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_VREF GENMASK(12, 8) ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN GENMASK(2, 0) ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_2 0x0 ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_2_5 0x1 ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_3 0x2 ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_4 0x3 ++#define AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_6 0x4 ++#define AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN 0x74 ++#define AIROHA_PCS_ANA_TXPLL_VCO_CFIX GENMASK(25, 24) ++#define AIROHA_PCS_ANA_TXPLL_VCODIV GENMASK(17, 16) ++#define AIROHA_PCS_ANA_TXPLL_VCODIV_1 0x0 ++#define AIROHA_PCS_ANA_TXPLL_VCODIV_2 0x1 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW GENMASK(10, 8) ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_0_5 0x0 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_1 0x1 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_2 0x2 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_4 0x3 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_8 0x4 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_16 0x6 ++#define AIROHA_PCS_ANA_TXPLL_TCL_LPF_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN 0x78 ++#define AIROHA_PCS_ANA_TXPLL_VCO_VCOVAR_BIAS_L GENMASK(29, 27) ++#define AIROHA_PCS_ANA_TXPLL_VCO_VCOVAR_BIAS_H GENMASK(26, 24) ++#define AIROHA_PCS_ANA_TXPLL_VCO_TCLVAR GENMASK(18, 16) ++#define AIROHA_PCS_ANA_TXPLL_VCO_SCAPWR GENMASK(10, 8) ++#define AIROHA_PCS_ANA_TXPLL_VCO_HALFLSB_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_SSC_EN 0x7c ++#define AIROHA_PCS_ANA_TXPLL_SSC_TRI_EN BIT(16) ++#define AIROHA_PCS_ANA_TXPLL_SSC_PHASE_INI BIT(8) ++#define AIROHA_PCS_ANA_TXPLL_SSC_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_SSC_DELTA1 0x80 ++#define AIROHA_PCS_ANA_TXPLL_SSC_DELTA GENMASK(31, 16) ++#define AIROHA_PCS_ANA_TXPLL_SSC_DELTA1 GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_SSC_PERIOD 0x84 ++#define AIROHA_PCS_ANA_TXPLL_LDO_VCO_OUT GENMASK(25, 24) ++#define AIROHA_PCS_ANA_TXPLL_LDO_OUT GENMASK(17, 16) ++#define AIROHA_PCS_ANA_TXPLL_SSC_PERIOD GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF 0x94 ++#define AIROHA_PCS_ANA_TXPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_TX_CKLDO_EN 0xc4 ++#define AIROHA_PCS_ANA_TX_DMEDGEGEN_EN BIT(24) ++#define AIROHA_PCS_ANA_TX_CKLDO_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_RX_BUSBIT_SEL 0xcc ++#define AIROHA_PCS_ANA_RX_PHY_CK_SEL_FORCE BIT(24) ++#define AIROHA_PCS_ANA_RX_PHY_CK_SEL BIT(16) ++#define AIROHA_PCS_ANA_RX_PHY_CK_SEL_FROM_PR 0x0 ++#define AIROHA_PCS_ANA_RX_PHY_CK_SEL_FROM_DES 0x1 ++#define AIROHA_PCS_ANA_PXP_RX_REV_0 0xd4 ++#define AIROHA_PCS_ANA_RX_REV_1 GENMASK(31, 16) ++#define AIROHA_PCS_ANA_REV_1_FE_EQ_BIAS_CTRL GENMASK(30, 28) ++#define AIROHA_PCS_ANA_REV_1_FE_BUF1_BIAS_CTRL GENMASK(26, 24) ++#define AIROHA_PCS_ANA_REV_1_FE_BUF2_BIAS_CTRL GENMASK(22, 20) ++#define AIROHA_PCS_ANA_REV_1_SIGDET_ILEAK GENMASK(19, 18) ++#define AIROHA_PCS_ANA_REV_1_FECUR_PWDB BIT(16) ++#define AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV 0xd8 ++#define AIROHA_PCS_ANA_RX_TDC_CK_SEL BIT(24) ++#define AIROHA_PCS_ANA_RX_PHYCK_RSTB BIT(16) ++#define AIROHA_PCS_ANA_RX_PHYCK_SEL GENMASK(9, 8) ++#define AIROHA_PCS_ANA_RX_PHYCK_DIV GENMASK(7, 0) ++#define AIROHA_PCS_ANA_PXP_CDR_PD_PICAL_CKD8_INV 0xdc ++#define AIROHA_PCS_ANA_CDR_PD_EDGE_DIS BIT(8) ++#define AIROHA_PCS_ANA_CDR_PD_PICAL_CKD8_INV BIT(0) ++#define AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO 0xe8 ++#define AIROHA_PCS_ANA_CDR_LPF_TOP_LIM GENMASK(26, 8) ++#define AIROHA_PCS_ANA_CDR_LPF_RATIO GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_CDR_PR_INJ_MODE 0xf4 ++#define AIROHA_PCS_ANA_CDR_PR_INJ_FORCE_OFF BIT(24) ++#define AIROHA_PCS_ANA_PXP_CDR_PR_BETA_DAC 0xf8 ++#define AIROHA_PCS_ANA_CDR_PR_KBAND_DIV GENMASK(26, 24) ++#define AIROHA_PCS_ANA_CDR_PR_BETA_SEL GENMASK(19, 16) ++#define AIROHA_PCS_ANA_CDR_PR_VCOADC_OS GENMASK(11, 8) ++#define AIROHA_PCS_ANA_CDR_PR_BETA_DAC GENMASK(6, 0) ++#define AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL 0xfc ++#define AIROHA_PCS_ANA_CDR_PR_FBKSEL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_CDR_PR_DAC_BAND GENMASK(20, 16) ++#define AIROHA_PCS_ANA_CDR_PR_VREG_CKBUF_VAL GENMASK(10, 8) ++#define AIROHA_PCS_ANA_CDR_PR_VREG_IBAND_VAL GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN 0x10c ++#define AIROHA_PCS_ANA_RX_DAC_MON GENMASK(28, 24) ++#define AIROHA_PCS_ANA_CDR_PR_CAP_EN BIT(19) ++#define AIROHA_PCS_ANA_CDR_BUF_IN_SR GENMASK(18, 16) ++#define AIROHA_PCS_ANA_CDR_PR_XFICK_EN BIT(2) ++#define AIROHA_PCS_ANA_CDR_PR_MONPI_EN BIT(1) ++#define AIROHA_PCS_ANA_CDR_PR_MONPR_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_RX_DAC_RANGE 0x110 ++#define AIROHA_PCS_ANA_RX_SIGDET_LPF_CTRL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH 0x114 ++#define AIROHA_PCS_ANA_RX_FE_50OHMS_SEL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_RX_SIGDET_VTH_SEL GENMASK(20, 16) ++#define AIROHA_PCS_ANA_RX_SIGDET_PEAK GENMASK(9, 8) ++#define AIROHA_PCS_ANA_PXP_RX_FE_EQ_HZEN 0x118 ++#define AIROHA_PCS_ANA_RX_FE_VB_EQ3_EN BIT(24) ++#define AIROHA_PCS_ANA_RX_FE_VB_EQ2_EN BIT(16) ++#define AIROHA_PCS_ANA_RX_FE_VB_EQ1_EN BIT(8) ++#define AIROHA_PCS_ANA_RX_FE_EQ_HZEN BIT(0) ++#define AIROHA_PCS_ANA_PXP_RX_FE_VCM_GEN_PWDB 0x11c ++#define AIROHA_PCS_ANA_RX_FE_VCM_GEN_PWDB BIT(0) ++#define AIROHA_PCS_ANA_PXP_RX_OSCAL_WATCH_WNDW 0x120 ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE GENMASK(17, 8) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA2VOS BIT(0) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA2IOS BIT(1) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA1VOS BIT(2) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA1IOS BIT(3) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE2VOS BIT(4) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE2IOS BIT(5) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE1VOS BIT(6) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE1IOS BIT(7) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_LVSH BIT(8) ++#define AIROHA_PCS_ANA_RX_OSCAL_FORCE_COMPOS BIT(9) ++#define AIROHA_PCS_ANA_PXP_AEQ_CFORCE 0x13c ++#define AIROHA_PCS_ANA_AEQ_OFORCE GENMASK(19, 8) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_SAOS BIT(0) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP1 BIT(1) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP2 BIT(2) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP3 BIT(3) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP4 BIT(4) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP5 BIT(5) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP6 BIT(6) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_DFETP7 BIT(7) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_VGA BIT(8) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_CTLE BIT(9) ++#define AIROHA_PCS_ANA_AEQ_OFORCE_ATT BIT(10) ++#define AIROHA_PCS_ANA_PXP_RX_FE_PEAKING_CTRL_MSB 0x144 ++#define AIROHA_PCS_ANA_RX_DAC_D0_BYPASS_AEQ BIT(24) ++#define AIROHA_PCS_ANA_PXP_RX_DAC_D1_BYPASS_AEQ 0x148 ++#define AIROHA_PCS_ANA_RX_DAC_EYE_BYPASS_AEQ BIT(24) ++#define AIROHA_PCS_ANA_RX_DAC_E1_BYPASS_AEQ BIT(16) ++#define AIROHA_PCS_ANA_RX_DAC_E0_BYPASS_AEQ BIT(8) ++#define AIROHA_PCS_ANA_RX_DAC_D1_BYPASS_AEQ BIT(0) ++ ++/* PMA_PHYA 2L */ ++#define AIROHA_PCS_ANA_PXP_2L_CMN_EN 0x0 ++#define AIROHA_PCS_ANA_2L_CMN_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN 0x4 ++#define AIROHA_PCS_ANA_2L_JCPLL_CHP_IOFST GENMASK(29, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_CHP_IBIAS GENMASK(21, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_LPF_SHCK_EN BIT(8) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR 0x8 ++#define AIROHA_PCS_ANA_2L_JCPLL_LPF_BWR GENMASK(28, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_LPF_BP GENMASK(20, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_LPF_BC GENMASK(12, 8) ++#define AIROHA_PCS_ANA_2L_JCPLL_LPF_BR GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC 0xc ++#define AIROHA_PCS_ANA_2L_JCPLL_KBAND_DIV GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_KBAND_CODE GENMASK(23, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_KBAND_OPTION BIT(8) ++#define AIROHA_PCS_ANA_2L_JCPLL_LPF_BWC GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC 0x10 ++#define AIROHA_PCS_ANA_2L_JCPLL_KBAND_KS GENMASK(17, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_KBAND_KF GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_JCPLL_KBAND_KFC GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_MMD_PREDIV_MODE 0x14 ++#define AIROHA_PCS_ANA_2L_JCPLL_POSTDIV_D5 BIT(24) ++#define AIROHA_PCS_ANA_2L_JCPLL_MMD_PREDIV_MODE GENMASK(1, 0) ++#define AIROHA_PCS_ANA_2L_JCPLL_MMD_PREDIV_MODE_2 0x0 ++#define AIROHA_PCS_ANA_2L_JCPLL_MMD_PREDIV_MODE_3 0x1 ++#define AIROHA_PCS_ANA_2L_JCPLL_MMD_PREDIV_MODE_4 0x2 ++#define AIROHA_PCS_ANA_2L_JCPLL_MMD_PREDIV_MODE_1 0x3 ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY 0x1c ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_DI_LS GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_DI_LS_2_23 0x0 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_DI_LS_2_21 0x1 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_DI_LS_2_19 0x2 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_DI_LS_2_15 0x3 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_DI_EN BIT(16) ++#define AIROHA_PCS_ANA_2L_JCPLL_PLL_RSTB BIT(8) ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY GENMASK(2, 0) ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY_20_25 0x1 ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY_40_50 0x2 ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY_80_100 0x3 ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY_150_200 0x4 ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY_300_400 0x5 ++#define AIROHA_PCS_ANA_2L_JCPLL_RST_DLY_600_800 0x6 ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM 0x20 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_OUT BIT(24) ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_ORD GENMASK(17, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_ORD_INT 0x0 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_ORD_1SDM 0x1 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_ORD_2SDM 0x2 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_ORD_3SDM 0x3 ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_MODE GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_IFM BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN 0x24 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_VREF GENMASK(28, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_GAIN GENMASK(18, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_GAIN_2 0x0 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_GAIN_4 0x1 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_GAIN_6 0x2 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_GAIN_8 0x3 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_GAIN_10 0x4 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_AMP_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_JCPLL_SDM_HREN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_CMP_EN 0x28 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW_0_5 0x0 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW_1 0x1 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW_2 0x2 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW_4 0x3 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW_8 0x4 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW_16 0x6 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_EN BIT(16) ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_LPF_BW GENMASK(26, 24) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV 0x2c ++#define AIROHA_PCS_ANA_2L_JCPLL_VCO_SCAPWR GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_VCO_HALFLSB_EN BIT(16) ++#define AIROHA_PCS_ANA_2L_JCPLL_VCO_CFIX GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_JCPLL_VCODIV GENMASK(1, 0) ++#define AIROHA_PCS_ANA_2L_JCPLL_VCODIV_1 0x0 ++#define AIROHA_PCS_ANA_2L_JCPLL_VCODIV_2 0x1 ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR 0x30 ++#define AIROHA_PCS_ANA_2L_JCPLL_VCO_VCOVAR_BIAS_L GENMASK(18, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_VCO_VCOVAR_BIAS_H GENMASK(10, 8) ++#define AIROHA_PCS_ANA_2L_JCPLL_VCO_TCLVAR GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN 0x38 ++#define AIROHA_PCS_ANA_2L_JCPLL_SSC_TRI_EN BIT(16) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_DELTA1 0x3c ++#define AIROHA_PCS_ANA_2L_JCPLL_SSC_DELTA GENMASK(31, 16) ++#define AIROHA_PCS_ANA_2L_JCPLL_SSC_DELTA1 GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_PERIOD 0x40 ++#define AIROHA_PCS_ANA_2L_JCPLL_SSC_PERIOD GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_VTP_EN 0x4c ++#define AIROHA_PCS_ANA_2L_JCPLL_SPARE_L GENMASK(31, 24) ++#define AIROHA_PCS_ANA_2L_JCPLL_SPARE_L_LDO FIELD_PREP_CONST(AIROHA_PCS_ANA_JCPLL_SPARE_L, BIT(5)) ++#define AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_KBAND_VREF 0x50 ++#define AIROHA_PCS_ANA_2L_JCPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_750M_SYS_CK_EN 0x54 ++#define AIROHA_PCS_ANA_2L_TXPLL_CHP_IBIAS GENMASK(29, 24) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST 0x58 ++#define AIROHA_PCS_ANA_2L_TXPLL_LPF_BP GENMASK(28, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_LPF_BC GENMASK(20, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_LPF_BR GENMASK(12, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_CHP_IOFST GENMASK(5, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR 0x5c ++#define AIROHA_PCS_ANA_2L_TXPLL_KBAND_CODE GENMASK(31, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_KBAND_OPTION BIT(16) ++#define AIROHA_PCS_ANA_2L_TXPLL_LPF_BWC GENMASK(12, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_LPF_BWR GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV 0x60 ++#define AIROHA_PCS_ANA_2L_TXPLL_KBAND_KS GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_KBAND_KF GENMASK(17, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_KBAND_KFC GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_KBAND_DIV GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_POSTDIV_EN 0x64 ++#define AIROHA_PCS_ANA_2L_TXPLL_MMD_PREDIV_MODE GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_MMD_PREDIV_MODE_2 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_MMD_PREDIV_MODE_3 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_MMD_PREDIV_MODE_4 0x2 ++#define AIROHA_PCS_ANA_2L_TXPLL_MMD_PREDIV_MODE_1 0x3 ++#define AIROHA_PCS_ANA_2L_TXPLL_POSTDIV_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_PHY_CK2_EN 0x68 ++#define AIROHA_PCS_ANA_2L_TXPLL_REFIN_INTERNAL BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV 0x6c ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_DI_EN BIT(24) ++#define AIROHA_PCS_ANA_2L_TXPLL_PLL_RSTB BIT(16) ++#define AIROHA_PCS_ANA_2L_TXPLL_RST_DLY GENMASK(10, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_REFIN_DIV GENMASK(1, 0) ++#define AIROHA_PCS_ANA_2L_TXPLL_REFIN_DIV_1 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_REFIN_DIV_2 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_REFIN_DIV_3 0x2 ++#define AIROHA_PCS_ANA_2L_TXPLL_REFIN_DIV_4 0x3 ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS 0x70 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_ORD GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_ORD_INT 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_ORD_1SDM 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_ORD_2SDM 0x2 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_ORD_3SDM 0x3 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_MODE GENMASK(17, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_IFM BIT(8) ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_DI_LS GENMASK(1, 0) ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_DI_LS_2_23 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_DI_LS_2_21 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_DI_LS_2_19 0x2 ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_DI_LS_2_15 0x3 ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT 0x74 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_GAIN GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_GAIN_2 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_GAIN_2_5 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_GAIN_3 0x2 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_GAIN_4 0x3 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_GAIN_6 0x4 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_EN BIT(16) ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_HREN BIT(8) ++#define AIROHA_PCS_ANA_2L_TXPLL_SDM_OUT BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_AMP_VREF 0x78 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_EN BIT(24) ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_AMP_VREF GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW 0x7c ++#define AIROHA_PCS_ANA_2L_TXPLL_VCO_HALFLSB_EN BIT(24) ++#define AIROHA_PCS_ANA_2L_TXPLL_VCO_CFIX GENMASK(17, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_VCODIV GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_VCODIV_1 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_VCODIV_2 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW GENMASK(2, 0) ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW_0_5 0x0 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW_1 0x1 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW_2 0x2 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW_4 0x3 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW_8 0x4 ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_LPF_BW_16 0x6 ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR 0x80 ++#define AIROHA_PCS_ANA_2L_TXPLL_VCO_VCOVAR_BIAS_L GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_VCO_VCOVAR_BIAS_H GENMASK(18, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_VCO_TCLVAR GENMASK(10, 8) ++#define AIROHA_PCS_ANA_2L_TXPLL_VCO_SCAPWR GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN 0x84 ++#define AIROHA_PCS_ANA_2L_TXPLL_SSC_TRI_EN BIT(16) ++#define AIROHA_PCS_ANA_2L_TXPLL_SSC_PHASE_INI BIT(8) ++#define AIROHA_PCS_ANA_2L_TXPLL_SSC_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_DELTA1 0x88 ++#define AIROHA_PCS_ANA_2L_TXPLL_SSC_DELTA GENMASK(31, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_SSC_DELTA1 GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD 0x8c ++#define AIROHA_PCS_ANA_2L_TXPLL_LDO_VCO_OUT GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_TXPLL_LDO_OUT GENMASK(17, 16) ++#define AIROHA_PCS_ANA_2L_TXPLL_SSC_PERIOD GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_KBAND_VREF 0x9c ++#define AIROHA_PCS_ANA_2L_TXPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_TX0_CKLDO_EN 0xcc ++#define AIROHA_PCS_ANA_2L_TX0_DMEDGEGEN_EN BIT(24) ++#define AIROHA_PCS_ANA_2L_TX0_CKLDO_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_TX1_CKLDO_EN 0xe8 ++#define AIROHA_PCS_ANA_2L_TX1_DMEDGEGEN_EN BIT(24) ++#define AIROHA_PCS_ANA_2L_TX1_CKLDO_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_BUSBIT_SEL 0xf4 ++#define AIROHA_PCS_ANA_2L_RX0_PHY_CK_SEL_FORCE BIT(24) ++#define AIROHA_PCS_ANA_2L_RX0_PHY_CK_SEL BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_PHY_CK_SEL_FROM_PR 0x0 ++#define AIROHA_PCS_ANA_2L_RX0_PHY_CK_SEL_FROM_DES 0x1 ++#define AIROHA_PCS_ANA_PXP_2L_RX0_REV_0 0xfc ++#define AIROHA_PCS_ANA_2L_RX0_REV_1 GENMASK(31, 16) ++#define AIROHA_PCS_ANA_2L_REV_1_FE_EQ_BIAS_CTRL GENMASK(30, 28) ++#define AIROHA_PCS_ANA_2L_REV_1_FE_BUF1_BIAS_CTRL GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_REV_1_FE_BUF2_BIAS_CTRL GENMASK(22, 20) ++#define AIROHA_PCS_ANA_2L_REV_1_SIGDET_ILEAK GENMASK(19, 18) ++#define AIROHA_PCS_ANA_2L_REV_1_FECUR_PWDB BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_REV_0 GENMASK(15, 0) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_BUF2_BIAS_TYPE GENMASK(13, 12) ++#define AIROHA_PCS_ANA_2L_REV_0_OSCAL_FE_MODE_SET_SEL BIT(11) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_EQ_GAIN_MODE_TRAINING BIT(10) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_BUF_GAIN_MODE_TRAINING GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_EQ_GAIN_MODE_NORMAL BIT(6) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_BUF_GAIN_MODE_NORMAL GENMASK(5, 4) ++#define AIROHA_PCS_ANA_2L_REV_0_VOS_PNINV GENMASK(3, 2) ++#define AIROHA_PCS_ANA_2L_REV_0_PLEYEBD4 BIT(1) ++#define AIROHA_PCS_ANA_2L_REV_0_PLEYE_XOR_MON_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_PHYCK_DIV 0x100 ++#define AIROHA_PCS_ANA_2L_RX0_TDC_CK_SEL BIT(24) ++#define AIROHA_PCS_ANA_2L_RX0_PHYCK_RSTB BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_PHYCK_SEL GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_RX0_PHYCK_DIV GENMASK(7, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PD_PICAL_CKD8_INV 0x104 ++#define AIROHA_PCS_ANA_2L_CDR0_PD_EDGE_DIS BIT(8) ++#define AIROHA_PCS_ANA_2L_CDR0_PD_PICAL_CKD8_INV BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_LPF_RATIO 0x110 ++#define AIROHA_PCS_ANA_2L_CDR0_LPF_TOP_LIM GENMASK(26, 8) ++#define AIROHA_PCS_ANA_2L_CDR0_LPF_RATIO GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PR_INJ_MODE 0x11c ++#define AIROHA_PCS_ANA_2L_CDR0_PR_INJ_FORCE_OFF BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BETA_DAC 0x120 ++#define AIROHA_PCS_ANA_2L_CDR0_PR_KBAND_DIV GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_BETA_SEL GENMASK(19, 16) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_VCOADC_OS GENMASK(11, 8) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_BETA_DAC GENMASK(6, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PR_VREG_IBAND_VAL 0x124 ++#define AIROHA_PCS_ANA_2L_CDR0_PR_FBKSEL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_DAC_BAND GENMASK(20, 16) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_VREG_CKBUF_VAL GENMASK(10, 8) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_VREG_IBAND_VAL GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PR_COR_HBW_EN 0x130 ++#define AIROHA_PCS_ANA_2L_CDR0_PR_MONPR_EN BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PR_MONPI_EN 0x134 ++#define AIROHA_PCS_ANA_2L_CDR0_PR_XFICK_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_MONPI_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BUF_IN_SR 0x138 ++#define AIROHA_PCS_ANA_2L_CDR0_PR_CAP_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_CDR0_PR_BUF_IN_SR GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_DAC_MON 0x13c ++#define AIROHA_PCS_ANA_2L_RX0_DAC_MON GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_DCTEST_EN 0x140 ++#define AIROHA_PCS_ANA_2L_RX0_SIGDET_PEAK GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_RX0_SIGDET_LPF_CTRL GENMASK(9, 8) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_VTH_SEL 0x144 ++#define AIROHA_PCS_ANA_2L_RX0_FE_VB_EQ1_EN BIT(24) ++#define AIROHA_PCS_ANA_2L_RX0_FE_EQ_HZEN BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_SIGDET_VTH_SEL GENMASK(4, 0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_FE_VB_EQ2_EN 0x148 ++#define AIROHA_PCS_ANA_2L_RX0_FE_VCM_GEN_PWDB BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_FE_VB_EQ3_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_RX0_FE_VB_EQ2_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_OSCAL_FORCE 0x150 ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE GENMASK(17, 8) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_VGA2VOS BIT(0) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_VGA2IOS BIT(1) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_VGA1VOS BIT(2) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_VGA1IOS BIT(3) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_CTLE2VOS BIT(4) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_CTLE2IOS BIT(5) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_CTLE1VOS BIT(6) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_CTLE1IOS BIT(7) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_LVSH BIT(8) ++#define AIROHA_PCS_ANA_2L_RX0_OSCAL_FORCE_COMPOS BIT(9) ++#define AIROHA_PCS_ANA_PXP_2L_AEQ0_CFORCE 0x170 ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE GENMASK(19, 8) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_SAOS BIT(0) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP1 BIT(1) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP2 BIT(2) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP3 BIT(3) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP4 BIT(4) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP5 BIT(5) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP6 BIT(6) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_DFETP7 BIT(7) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_VGA BIT(8) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_CTLE BIT(9) ++#define AIROHA_PCS_ANA_2L_AEQ0_OFORCE_ATT BIT(10) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_DAC_D0_BYPASS_AEQ 0x17c ++#define AIROHA_PCS_ANA_2L_RX0_DAC_E1_BYPASS_AEQ BIT(24) ++#define AIROHA_PCS_ANA_2L_RX0_DAC_E0_BYPASS_AEQ BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_DAC_D1_BYPASS_AEQ BIT(8) ++#define AIROHA_PCS_ANA_2L_RX0_DAC_D0_BYPASS_AEQ BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX0_DAC_EYE_BYPASS_AEQ 0x180 ++#define AIROHA_PCS_ANA_2L_RX0_DAC_EYE_BYPASS_AEQ BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_FE_PEACKING_CTRL_LSB 0x234 ++#define AIROHA_PCS_ANA_2L_RX1_DAC_D0_BYPASS_AEQ BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_BUSBIT_SEL 0x1ac ++#define AIROHA_PCS_ANA_2L_RX1_PHY_CK_SEL_FORCE BIT(24) ++#define AIROHA_PCS_ANA_2L_RX1_PHY_CK_SEL BIT(16) ++#define AIROHA_PCS_ANA_2L_RX0_PHY_CK_SEL_FROM_PR 0x0 ++#define AIROHA_PCS_ANA_2L_RX0_PHY_CK_SEL_FROM_DES 0x1 ++#define AIROHA_PCS_ANA_PXP_2L_RX1_REV_0 0x1b4 ++#define AIROHA_PCS_ANA_2L_RX1_REV_1 GENMASK(31, 16) ++#define AIROHA_PCS_ANA_2L_REV_1_FE_EQ_BIAS_CTRL GENMASK(30, 28) ++#define AIROHA_PCS_ANA_2L_REV_1_FE_BUF1_BIAS_CTRL GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_REV_1_FE_BUF2_BIAS_CTRL GENMASK(22, 20) ++#define AIROHA_PCS_ANA_2L_REV_1_SIGDET_ILEAK GENMASK(19, 18) ++#define AIROHA_PCS_ANA_2L_REV_1_FECUR_PWDB BIT(16) ++#define AIROHA_PCS_ANA_2L_RX1_REV_0 GENMASK(15, 0) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_BUF2_BIAS_TYPE GENMASK(13, 12) ++#define AIROHA_PCS_ANA_2L_REV_0_OSCAL_FE_MODE_SET_SEL BIT(11) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_EQ_GAIN_MODE_TRAINING BIT(10) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_BUF_GAIN_MODE_TRAINING GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_EQ_GAIN_MODE_NORMAL BIT(6) ++#define AIROHA_PCS_ANA_2L_REV_0_FE_BUF_GAIN_MODE_NORMAL GENMASK(5, 4) ++#define AIROHA_PCS_ANA_2L_REV_0_VOS_PNINV GENMASK(3, 2) ++#define AIROHA_PCS_ANA_2L_REV_0_PLEYEBD4 BIT(1) ++#define AIROHA_PCS_ANA_2L_REV_0_PLEYE_XOR_MON_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_PHYCK_DIV 0x1b8 ++#define AIROHA_PCS_ANA_2L_RX1_TDC_CK_SEL BIT(24) ++#define AIROHA_PCS_ANA_2L_RX1_PHYCK_RSTB BIT(16) ++#define AIROHA_PCS_ANA_2L_RX1_PHYCK_SEL GENMASK(9, 8) ++#define AIROHA_PCS_ANA_2L_RX1_PHYCK_DIV GENMASK(7, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PD_PICAL_CKD8_INV 0x1bc ++#define AIROHA_PCS_ANA_2L_CDR1_PD_EDGE_DIS BIT(8) ++#define AIROHA_PCS_ANA_2L_CDR1_PD_PICAL_CKD8_INV BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_LPF_RATIO 0x1c8 ++#define AIROHA_PCS_ANA_2L_CDR1_LPF_TOP_LIM GENMASK(26, 8) ++#define AIROHA_PCS_ANA_2L_CDR1_LPF_RATIO GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PR_INJ_MODE 0x1d4 ++#define AIROHA_PCS_ANA_2L_CDR1_PR_INJ_FORCE_OFF BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BETA_DAC 0x1d8 ++#define AIROHA_PCS_ANA_2L_CDR1_PR_KBAND_DIV GENMASK(26, 24) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_BETA_SEL GENMASK(19, 16) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_VCOADC_OS GENMASK(11, 8) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_BETA_DAC GENMASK(6, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PR_VREG_IBAND_VAL 0x1dc ++#define AIROHA_PCS_ANA_2L_CDR1_PR_FBKSEL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_DAC_BAND GENMASK(20, 16) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_VREG_CKBUF_VAL GENMASK(10, 8) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_VREG_IBAND_VAL GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PR_COR_HBW_EN 0x1e8 ++#define AIROHA_PCS_ANA_2L_CDR1_PR_MONPR_EN BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PR_MONPI_EN 0x1ec ++#define AIROHA_PCS_ANA_2L_CDR1_PR_XFICK_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_MONPI_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BUF_IN_SR 0x1f0 ++#define AIROHA_PCS_ANA_2L_RX1_DAC_MON GENMASK(20, 16) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_CAP_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_CDR1_PR_BUF_IN_SR GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_DAC_RANGE_EYE 0x1f4 ++#define AIROHA_PCS_ANA_2L_RX1_SIGDET_LPF_CTRL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_SIGDET_NOVTH 0x1f8 ++#define AIROHA_PCS_ANA_2L_RX1_SIGDET_VTH_SEL GENMASK(20, 16) ++#define AIROHA_PCS_ANA_2L_RX1_SIGDET_PEAK GENMASK(9, 8) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_FE_50OHMS_SEL 0x1fc ++#define AIROHA_PCS_ANA_2L_RX1_FE_EQ_HZEN BIT(24) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_FE_VB_EQ1_EN 0x200 ++#define AIROHA_PCS_ANA_2L_RX1_FE_VCM_GEN_PWDB BIT(24) ++#define AIROHA_PCS_ANA_2L_RX1_FE_VB_EQ3_EN BIT(16) ++#define AIROHA_PCS_ANA_2L_RX1_FE_VB_EQ2_EN BIT(8) ++#define AIROHA_PCS_ANA_2L_RX1_FE_VB_EQ1_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_OSCAL_WATCH_WNDW 0x208 ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE GENMASK(25, 16) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_VGA2VOS BIT(0) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_VGA2IOS BIT(1) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_VGA1VOS BIT(2) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_VGA1IOS BIT(3) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_CTLE2VOS BIT(4) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_CTLE2IOS BIT(5) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_CTLE1VOS BIT(6) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_CTLE1IOS BIT(7) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_LVSH BIT(8) ++#define AIROHA_PCS_ANA_2L_RX1_OSCAL_FORCE_COMPOS BIT(9) ++#define AIROHA_PCS_ANA_PXP_2L_AEQ1_CFORCE 0x228 ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE GENMASK(27, 16) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_SAOS BIT(0) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP1 BIT(1) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP2 BIT(2) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP3 BIT(3) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP4 BIT(4) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP5 BIT(5) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP6 BIT(6) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_DFETP7 BIT(7) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_VGA BIT(8) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_CTLE BIT(9) ++#define AIROHA_PCS_ANA_2L_AEQ1_OFORCE_ATT BIT(10) ++#define AIROHA_PCS_ANA_PXP_2L_RX1_DAC_D1_BYPASS_AEQ 0x238 ++#define AIROHA_PCS_ANA_2L_RX1_DAC_EYE_BYPASS_AEQ BIT(24) ++#define AIROHA_PCS_ANA_2L_RX1_DAC_E1_BYPASS_AEQ BIT(16) ++#define AIROHA_PCS_ANA_2L_RX1_DAC_E0_BYPASS_AEQ BIT(8) ++#define AIROHA_PCS_ANA_2L_RX1_DAC_D1_BYPASS_AEQ BIT(0) ++ ++/* PMA_PHYD */ ++#define AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_0 0x0 ++#define AIROHA_PCS_PMA_SW_LCPLL_EN BIT(24) ++#define AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_1 0x4 ++#define AIROHA_PCS_PMA_LCPLL_MAN_PWDB BIT(0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_2 0x88 ++#define AIROHA_PCS_PMA_DATA_SHIFT BIT(8) ++#define AIROHA_PCS_PMA_EYECNT_FAST BIT(0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_0 0x8c ++#define AIROHA_PCS_PMA_RX_OS_START GENMASK(23, 8) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT GENMASK(2, 0) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_0_05 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x0) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_0_1 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x1) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_0_2 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x2) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_0_4 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x3) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_0_8 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x4) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_1_6 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x5) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_3_2 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x6) ++#define AIROHA_PCS_PMA_OSC_SPEED_OPT_6_4 FIELD_PREP_CONST(AIROHA_PCS_PMA_OSC_SPEED_OPT, 0x7) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_1 0x90 ++#define AIROHA_PCS_PMA_RX_PICAL_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_RX_PICAL_START GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_2 0x94 ++#define AIROHA_PCS_PMA_RX_PDOS_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_RX_PDOS_START GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_3 0x98 ++#define AIROHA_PCS_PMA_RX_FEOS_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_RX_FEOS_START GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_4 0x9c ++#define AIROHA_PCS_PMA_RX_SDCAL_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_RX_SDCAL_START GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_5 0x100 ++#define AIROHA_PCS_PMA_RX_RDY GENMASK(31, 16) ++#define AIROHA_PCS_PMA_RX_BLWC_RDY_EN GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_6 0x104 ++#define AIROHA_PCS_PMA_RX_OS_END GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1 0x10c ++#define AIROHA_PCS_PMA_DISB_RX_RDY BIT(24) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1 0x114 ++#define AIROHA_PCS_PMA_FORCE_RX_RDY BIT(24) ++#define AIROHA_PCS_PMA_PHY_EQ_CTRL_2 0x120 ++#define AIROHA_PCS_PMA_EQ_DEBUG_SEL GENMASK(17, 16) ++#define AIROHA_PCS_PMA_FOM_NUM_ORDER GENMASK(12, 8) ++#define AIROHA_PCS_PMA_A_SEL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_SS_RX_FREQ_DET_1 0x14c ++#define AIROHA_PCS_PMA_UNLOCK_CYCLECNT GENMASK(31, 16) ++#define AIROHA_PCS_PMA_LOCK_CYCLECNT GENMASK(15, 0) ++#define AIROHA_PCS_PMA_SS_RX_FREQ_DET_2 0x150 ++#define AIROHA_PCS_PMA_LOCK_TARGET_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_LOCK_TARGET_BEG GENMASK(15, 0) ++#define AIROHA_PCS_PMA_SS_RX_FREQ_DET_3 0x154 ++#define AIROHA_PCS_PMA_UNLOCK_TARGET_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_UNLOCK_TARGET_BEG GENMASK(15, 0) ++#define AIROHA_PCS_PMA_SS_RX_FREQ_DET_4 0x158 ++#define AIROHA_PCS_PMA_LOCK_UNLOCKTH GENMASK(15, 12) ++#define AIROHA_PCS_PMA_LOCK_LOCKTH GENMASK(11, 8) ++#define AIROHA_PCS_PMA_FREQLOCK_DET_EN GENMASK(2, 0) ++#define AIROHA_PCS_PMA_FREQLOCK_DET_EN_FORCE_0 FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x0) ++#define AIROHA_PCS_PMA_FREQLOCK_DET_EN_FORCE_1 FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x1) ++#define AIROHA_PCS_PMA_FREQLOCK_DET_EN_WAIT FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x2) ++#define AIROHA_PCS_PMA_FREQLOCK_DET_EN_NORMAL FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x3) ++#define AIROHA_PCS_PMA_FREQLOCK_DET_EN_RX_STATE FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x7) ++#define AIROHA_PCS_PMA_SS_RX_SIGDET_1 0x16c ++#define AIROHA_PCS_PMA_SIGDET_EN BIT(0) ++#define AIROHA_PCS_PMA_RX_FLL_1 0x174 ++#define AIROHA_PCS_PMA_LPATH_IDAC GENMASK(10, 0) ++#define AIROHA_PCS_PMA_RX_FLL_2 0x178 ++#define AIROHA_PCS_PMA_CK_RATE GENMASK(18, 16) ++#define AIROHA_PCS_PMA_CK_RATE_20 FIELD_PREP_CONST(AIROHA_PCS_PMA_CK_RATE, 0x0) ++#define AIROHA_PCS_PMA_CK_RATE_10 FIELD_PREP_CONST(AIROHA_PCS_PMA_CK_RATE, 0x1) ++#define AIROHA_PCS_PMA_CK_RATE_5 FIELD_PREP_CONST(AIROHA_PCS_PMA_CK_RATE, 0x2) ++#define AIROHA_PCS_PMA_RX_FLL_5 0x184 ++#define AIROHA_PCS_PMA_FLL_IDAC_MIN GENMASK(26, 16) ++#define AIROHA_PCS_PMA_FLL_IDAC_MAX GENMASK(10, 0) ++#define AIROHA_PCS_PMA_RX_FLL_B 0x19c ++#define AIROHA_PCS_PMA_LOAD_EN BIT(0) ++#define AIROHA_PCS_PMA_RX_RESET_1 0x208 ++#define AIROHA_PCS_PMA_SIGDET_RST_B BIT(8) ++#define AIROHA_PCS_PMA_TX_RST_B 0x260 ++#define AIROHA_PCS_PMA_TXCALIB_RST_B BIT(8) ++#define AIROHA_PCS_PMA_TX_TOP_RST_B BIT(0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_4 0x320 ++#define AIROHA_PCS_PMA_DISB_BLWC_OFFSET BIT(24) ++#define AIROHA_PCS_PMA_RX_FORCE_MODE_9 0x330 ++#define AIROHA_PCS_PMA_FORCE_FBCK_LOCK BIT(0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_8 0x33c ++#define AIROHA_PCS_PMA_DISB_FBCK_LOCK BIT(0) ++#define AIROHA_PCS_PMA_SS_DA_XPON_PWDB_0 0x34c ++#define AIROHA_PCS_PMA_XPON_CDR_PD_PWDB BIT(24) ++#define AIROHA_PCS_PMA_XPON_CDR_PR_PIEYE_PWDB BIT(16) ++#define AIROHA_PCS_PMA_XPON_CDR_PW_PWDB BIT(8) ++#define AIROHA_PCS_PMA_XPON_RX_FE_PWDB BIT(0) ++#define AIROHA_PCS_PMA_SS_DA_XPON_PWDB_1 0x350 ++#define AIROHA_PCS_PMA_RX_SIDGET_PWDB BIT(0) ++#define AIROHA_PCS_PMA_DIG_RESERVE_0 0x360 ++#define AIROHA_PCS_TRIGGER_RX_SIDGET_SCAN GENMASK(17, 16) ++#define AIROHA_PCS_PMA_XPON_RX_RESERVED_1 0x374 ++#define AIROHA_PCS_PMA_XPON_RX_RATE_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_DIG_RO_RESERVE_2 0x380 ++#define AIROHA_PCS_RX_SIGDET BIT(8) ++#define AIROHA_PCS_PMA_RX_SYS_EN_SEL_0 0x38c ++#define AIROHA_PCS_PMA_RX_SYS_EN_SEL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_PLL_TDC_FREQDET_0 0x390 ++#define AIROHA_PCS_PMA_PLL_LOCK_CYCLECNT GENMASK(15, 0) ++#define AIROHA_PCS_PMA_PLL_TDC_FREQDET_1 0x394 ++#define AIROHA_PCS_PMA_PLL_LOCK_TARGET_END GENMASK(31, 16) ++#define AIROHA_PCS_PMA_PLL_LOCK_TARGET_BEG GENMASK(15, 0) ++#define AIROHA_PCS_PMA_PLL_TDC_FREQDET_3 0x39c ++#define AIROHA_PCS_PMA_PLL_LOCK_LOCKTH GENMASK(11, 8) ++#define AIROHA_PCS_PMA_ADD_XPON_MODE_1 0x414 ++#define AIROHA_PCS_PMA_XFI_RX_MODE GENMASK(11, 9) ++#define AIROHA_PCS_PMA_XFI_RX_MODE_10G3 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x0) ++#define AIROHA_PCS_PMA_XFI_RX_MODE_5G15 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x1) ++#define AIROHA_PCS_PMA_XFI_RX_MODE_6G25 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x2) ++#define AIROHA_PCS_PMA_XFI_RX_MODE_2G57 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x3) ++#define AIROHA_PCS_PMA_XFI_RX_MODE_3G12 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x4) ++#define AIROHA_PCS_PMA_XFI_RX_MODE_1G25 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x5) ++#define AIROHA_PCS_PMA_R2T_MODE BIT(8) ++#define AIROHA_PCS_PMA_XFI_TX_MODE GENMASK(5, 3) ++#define AIROHA_PCS_PMA_XFI_TX_MODE_10G3 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x0) ++#define AIROHA_PCS_PMA_XFI_TX_MODE_5G15 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x1) ++#define AIROHA_PCS_PMA_XFI_TX_MODE_6G25 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x2) ++#define AIROHA_PCS_PMA_XFI_TX_MODE_2G57 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x3) ++#define AIROHA_PCS_PMA_XFI_TX_MODE_3G12 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x4) ++#define AIROHA_PCS_PMA_XFI_TX_MODE_1G25 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x5) ++#define AIROHA_PCS_PMA_SW_RST_SET 0x460 ++#define AIROHA_PCS_PMA_SW_HSG_RXPCS_RST_N BIT(11) ++#define AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N BIT(10) ++#define AIROHA_PCS_PMA_SW_XFI_RXPCS_BIST_RST_N BIT(9) ++#define AIROHA_PCS_PMA_SW_XFI_RXPCS_RST_N BIT(8) ++#define AIROHA_PCS_PMA_SW_XFI_TXPCS_RST_N BIT(7) ++#define AIROHA_PCS_PMA_SW_TX_FIFO_RST_N BIT(6) ++#define AIROHA_PCS_PMA_SW_REF_RST_N BIT(5) ++#define AIROHA_PCS_PMA_SW_ALLPCS_RST_N BIT(4) ++#define AIROHA_PCS_PMA_SW_PMA_RST_N BIT(3) ++#define AIROHA_PCS_PMA_SW_TX_RST_N BIT(2) ++#define AIROHA_PCS_PMA_SW_RX_RST_N BIT(1) ++#define AIROHA_PCS_PMA_SW_RX_FIFO_RST_N BIT(0) ++#define AIROHA_PCS_PMA_XPON_INT_EN_3 0x474 ++#define AIROHA_PCS_PMA_RX_SIGDET_INT_EN BIT(16) ++#define AIROHA_PCS_PMA_XPON_INT_STA_3 0x47c ++#define AIROHA_PCS_PMA_RX_SIGDET_INT BIT(16) ++#define AIROHA_PCS_PMA_RX_EXTRAL_CTRL 0x48c ++#define AIROHA_PCS_PMA_DISB_LEQ BIT(0) ++#define AIROHA_PCS_PMA_RX_FREQDET 0x530 ++#define AIROHA_PCS_PMA_FL_OUT GENMASK(31, 16) ++#define AIROHA_PCS_PMA_FBCK_LOCK BIT(0) ++#define AIROHA_PCS_PMA_XPON_TX_RATE_CTRL 0x580 ++#define AIROHA_PCS_PMA_PON_TX_RATE_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN 0x768 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL GENMASK(19, 16) ++#define AIROHA_PCS_PMA_PXP_AEQ_SPEED 0x76c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_OSR_SEL BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_OSR_SEL GENMASK(17, 16) ++#define AIROHA_PCS_PMA_PXP_TX_FIR_C0B 0x778 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_CN1 BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_FIR_CN1 GENMASK(20, 16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C0B BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C0B GENMASK(5, 0) ++#define AIROHA_PCS_PMA_PXP_TX_TERM_SEL 0x77c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_DIVISOR BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_DIVISOR GENMASK(19, 16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_TERM_SEL BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_TERM_SEL GENMASK(2, 0) ++#define AIROHA_PCS_PMA_PXP_TX_FIR_C1 0x780 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C2 BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C2 GENMASK(20, 16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C1 BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C1 GENMASK(5, 0) ++#define AIROHA_PCS_PMA_PXP_TX_RATE_CTRL 0x784 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_RATE_CTRL BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_RATE_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_PXP_CDR_PR_IDAC 0x794 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW BIT(24) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_IDAC BIT(16) ++#define AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC GENMASK(10, 0) ++#define AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC_MAJOR GENMASK(10, 8) ++#define AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW 0x798 ++#define AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW GENMASK(30, 0) ++#define AIROHA_PCS_PMA_PXP_RX_FE_VOS 0x79c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_SDM_PCW BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_FE_VOS BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_FE_VOS GENMASK(5, 0) ++#define AIROHA_PCS_PMA_PXP_JCPLL_SDM_PCW 0x800 ++#define AIROHA_PCS_PMA_FORCE_DA_JCPLL_SDM_PCW GENMASK(30, 0) ++#define AIROHA_PCS_PMA_PXP_AEQ_BYPASS 0x80c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_AEQ_CKON BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_AEQ_CKON BIT(16) ++#define AIROHA_PCS_PMA_PXP_AEQ_RSTB 0x814 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_INJCK_SEL BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_INJCK_SEL BIT(16) ++#define AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA 0x818 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA BIT(0) ++#define AIROHA_PCS_PMA_PXP_CDR_PD_PWDB 0x81c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_KBAND_RSTB BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_KBAND_RSTB BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PD_PWDB BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PD_PWDB BIT(0) ++#define AIROHA_PCS_PMA_PXP_CDR_PR_LPF_C_EN 0x820 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_R_EN BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_LPF_R_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_C_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_LPF_C_EN BIT(0) ++#define AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB 0x824 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PWDB BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE_PWDB BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE_PWDB BIT(0) ++#define AIROHA_PCS_PMA_PXP_JCPLL_CKOUT_EN 0x828 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_EN BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_JCPLL_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_CKOUT_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_JCPLL_CKOUT_EN BIT(0) ++#define AIROHA_PCS_PMA_PXP_RX_SCAN_RST_B 0x84c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_PWDB BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_PWDB BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SCAN_RST_B BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_SCAN_RST_B BIT(0) ++#define AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN 0x854 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_EN BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_TXPLL_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_CKOUT_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TXPLL_CKOUT_EN BIT(0) ++#define AIROHA_PCS_PMA_PXP_TX_ACJTAG_EN 0x874 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_SEL BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_SEL BIT(16) ++#define AIROHA_PCS_PMA_PXP_FE_GAIN_CTRL 0x88c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_GAIN_CTRL BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_PXP_RX_FE_PWDB 0x894 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_PDOSCAL_EN BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_PDOSCAL_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PWDB BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_FE_PWDB BIT(0) ++#define AIROHA_PCS_PMA_DIG_RESERVE_29 0x910 ++#define AIROHA_PCS_PMA_2L_TX_RATE_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_2L_RX_RATE_CTRL GENMASK(5, 4) ++ ++#define AIROHA_PCS_MAX_CALIBRATION_TRY 50 ++#define AIROHA_PCS_MAX_NUM_RSTS 2 ++ ++enum xfi_port_type { ++ AIROHA_PCS_ETH, ++ AIROHA_PCS_PON, ++ AIROHA_PCS_USB, ++ AIROHA_PCS_PCIE, ++}; ++ ++struct airoha_pcs_maps { ++ struct regmap *pcs_mac; ++ struct regmap *hsgmii_an; ++ struct regmap *hsgmii_pcs; ++ struct regmap *hsgmii_rate_adp; ++ struct regmap *multi_sgmii; ++ struct regmap *usxgmii_pcs; ++}; ++ ++struct airoha_pcs_priv { ++ struct device *dev; ++ const struct airoha_pcs_match_data *data; ++ phy_interface_t interface; ++ ++ struct airoha_pcs_port *ports; ++ ++ struct regmap *scu; ++ ++ struct airoha_pcs_maps maps[2]; ++ ++ struct regmap *pcs_pma[2]; ++ struct regmap *pcs_ana; ++ struct regmap_field **pcs_ana_fields[2]; ++ ++ struct reset_control_bulk_data rsts[AIROHA_PCS_MAX_NUM_RSTS]; ++ ++ struct phy *phy; ++ ++ bool manual_rx_calib; ++}; ++ ++struct airoha_pcs_port { ++ struct airoha_pcs_priv *priv; ++ int index; ++ ++ struct phylink_pcs pcs; ++}; ++ ++struct airoha_pcs_match_data { ++ int num_port; ++ enum xfi_port_type port_type; ++ ++ int (*alloc_regmap_fields)(struct airoha_pcs_priv *priv); ++ int (*bringup)(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface); ++ void (*link_up)(struct airoha_pcs_priv *priv, int index); ++ int (*rxlock_workaround)(struct airoha_pcs_priv *priv, int index); ++}; ++ ++#define to_airoha_pcs_port(n) container_of(n, struct airoha_pcs_port, pcs) ++ ++#ifdef CONFIG_PCS_AIROHA_AN7581 ++int an7581_pcs_alloc_regmap_fields(struct airoha_pcs_priv *priv); ++int an7581_pcs_pcie_alloc_regmap_fields(struct airoha_pcs_priv *priv); ++int an7581_pcs_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface); ++int an7581_pcs_usb_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface); ++ ++void an7581_pcs_phya_link_up(struct airoha_pcs_priv *priv, int index); ++int an7581_pcs_rxlock_workaround(struct airoha_pcs_priv *priv, int index); ++#else ++static inline int an7581_pcs_alloc_regmap_fields(struct airoha_pcs_priv *priv) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int an7581_pcs_pcie_alloc_regmap_fields(struct airoha_pcs_priv *priv) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int an7581_pcs_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int an7581_pcs_usb_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void an7581_pcs_phya_link_up(struct airoha_pcs_priv *priv, ++ int index) ++{ ++} ++ ++static inline int an7581_pcs_rxlock_workaround(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ return 0; ++} ++#endif +--- /dev/null ++++ b/drivers/net/pcs/airoha/pcs-an7581.c +@@ -0,0 +1,2093 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Christian Marangi ++ */ ++#include ++#include ++#include ++ ++#include "pcs-airoha.h" ++ ++#include ++ ++enum { ++ AN7581_PCS_CMN_EN, ++ ++ AN7581_PCS_JCPLL_SPARE_L, ++ AN7581_PCS_JCPLL_RST_DLY, ++ AN7581_PCS_JCPLL_PLL_RSTB, ++ AN7581_PCS_JCPLL_SDM_DI_LS, ++ AN7581_PCS_JCPLL_SDM_DI_EN, ++ ++ AN7581_PCS_JCPLL_SDM_OUT, ++ AN7581_PCS_JCPLL_SDM_ORD, ++ AN7581_PCS_JCPLL_SDM_MODE, ++ AN7581_PCS_JCPLL_SDM_IFM, ++ AN7581_PCS_JCPLL_SDM_HREN, ++ ++ AN7581_PCS_JCPLL_CHP_IOFST, ++ AN7581_PCS_JCPLL_CHP_IBIAS, ++ AN7581_PCS_JCPLL_LPF_SHCK_EN, ++ ++ AN7581_PCS_JCPLL_LPF_BWR, ++ AN7581_PCS_JCPLL_LPF_BP, ++ AN7581_PCS_JCPLL_LPF_BC, ++ AN7581_PCS_JCPLL_LPF_BR, ++ AN7581_PCS_JCPLL_LPF_BWC, ++ ++ AN7581_PCS_JCPLL_VCO_SCAPWR, ++ AN7581_PCS_JCPLL_VCO_HALFLSB_EN, ++ AN7581_PCS_JCPLL_VCO_CFIX, ++ AN7581_PCS_JCPLL_VCODIV, ++ AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_L, ++ AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_H, ++ AN7581_PCS_JCPLL_VCO_TCLVAR, ++ ++ AN7581_PCS_JCPLL_POSTDIV_D5, ++ AN7581_PCS_JCPLL_MMD_PREDIV_MODE, ++ ++ AN7581_PCS_JCPLL_KBAND_KS, ++ AN7581_PCS_JCPLL_KBAND_KF, ++ AN7581_PCS_JCPLL_KBAND_KFC, ++ AN7581_PCS_JCPLL_KBAND_DIV, ++ AN7581_PCS_JCPLL_KBAND_CODE, ++ AN7581_PCS_JCPLL_KBAND_OPTION, ++ ++ AN7581_PCS_JCPLL_TCL_AMP_VREF, ++ AN7581_PCS_JCPLL_TCL_AMP_GAIN, ++ AN7581_PCS_JCPLL_TCL_AMP_EN, ++ ++ AN7581_PCS_JCPLL_TCL_LPF_BW, ++ AN7581_PCS_JCPLL_TCL_LPF_EN, ++ ++ AN7581_PCS_JCPLL_SSC_DELTA, ++ AN7581_PCS_JCPLL_SSC_DELTA1, ++ AN7581_PCS_JCPLL_SSC_PERIOD, ++ AN7581_PCS_JCPLL_SSC_TRI_EN, ++ AN7581_PCS_JCPLL_SSC_EN, ++ AN7581_PCS_JCPLL_SSC_PHASE_INI, ++ AN7581_PCS_JCPLL_TCL_KBAND_VREF, ++ ++ AN7581_PCS_TXPLL_LDO_VCO_OUT, ++ AN7581_PCS_TXPLL_LDO_OUT, ++ AN7581_PCS_TXPLL_PLL_RSTB, ++ AN7581_PCS_TXPLL_RST_DLY, ++ AN7581_PCS_TXPLL_REFIN_DIV, ++ AN7581_PCS_TXPLL_REFIN_INTERNAL, ++ AN7581_PCS_TXPLL_SDM_MODE, ++ AN7581_PCS_TXPLL_SDM_IFM, ++ AN7581_PCS_TXPLL_SDM_DI_LS, ++ AN7581_PCS_TXPLL_SDM_DI_EN, ++ AN7581_PCS_TXPLL_SDM_HREN, ++ AN7581_PCS_TXPLL_SDM_ORD, ++ AN7581_PCS_TXPLL_SDM_OUT, ++ AN7581_PCS_TXPLL_SSC_DELTA1, ++ AN7581_PCS_TXPLL_SSC_DELTA, ++ AN7581_PCS_TXPLL_SSC_TRI_EN, ++ AN7581_PCS_TXPLL_SSC_PHASE_INI, ++ AN7581_PCS_TXPLL_SSC_EN, ++ AN7581_PCS_TXPLL_SSC_PERIOD, ++ AN7581_PCS_TXPLL_LPF_BC, ++ AN7581_PCS_TXPLL_LPF_BR, ++ AN7581_PCS_TXPLL_LPF_BP, ++ AN7581_PCS_TXPLL_LPF_BWC, ++ AN7581_PCS_TXPLL_LPF_BWR, ++ AN7581_PCS_TXPLL_CHP_IOFST, ++ AN7581_PCS_TXPLL_CHP_IBIAS, ++ AN7581_PCS_TXPLL_VCO_CFIX, ++ AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_L, ++ AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_H, ++ AN7581_PCS_TXPLL_VCO_TCLVAR, ++ AN7581_PCS_TXPLL_VCO_SCAPWR, ++ AN7581_PCS_TXPLL_VCO_HALFLSB_EN, ++ AN7581_PCS_TXPLL_KBAND_CODE, ++ AN7581_PCS_TXPLL_KBAND_OPTION, ++ AN7581_PCS_TXPLL_KBAND_KS, ++ AN7581_PCS_TXPLL_KBAND_KF, ++ AN7581_PCS_TXPLL_KBAND_KFC, ++ AN7581_PCS_TXPLL_KBAND_DIV, ++ AN7581_PCS_TXPLL_MMD_PREDIV_MODE, ++ AN7581_PCS_TXPLL_POSTDIV_EN, ++ AN7581_PCS_TXPLL_VCODIV, ++ AN7581_PCS_TXPLL_TCL_KBAND_VREF, ++ AN7581_PCS_TXPLL_TCL_AMP_GAIN, ++ AN7581_PCS_TXPLL_TCL_AMP_VREF, ++ AN7581_PCS_TXPLL_TCL_LPF_BW, ++ AN7581_PCS_TXPLL_TCL_LPF_EN, ++ AN7581_PCS_TXPLL_TCL_AMP_EN, ++ ++ AN7581_PCS_TX_DMEDGEGEN_EN, ++ AN7581_PCS_TX_CKLDO_EN, ++ ++ AN7581_PCS_RX_DAC_EYE_BYPASS_AEQ, ++ AN7581_PCS_RX_DAC_E1_BYPASS_AEQ, ++ AN7581_PCS_RX_DAC_E0_BYPASS_AEQ, ++ AN7581_PCS_RX_DAC_D1_BYPASS_AEQ, ++ AN7581_PCS_RX_DAC_D0_BYPASS_AEQ, ++ AN7581_PCS_RX_FE_VCM_GEN_PWDB, ++ AN7581_PCS_RX_OSCAL_FORCE, ++ AN7581_PCS_RX_DAC_MON, ++ AN7581_PCS_RX_REV_1_FE_BUF1_BIAS_CTRL, ++ AN7581_PCS_RX_REV_1_FE_BUF2_BIAS_CTRL, ++ AN7581_PCS_RX_REV_1_SIGDET_ILEAK, ++ AN7581_PCS_RX_FE_VB_EQ3_EN, ++ AN7581_PCS_RX_FE_VB_EQ2_EN, ++ AN7581_PCS_RX_FE_VB_EQ1_EN, ++ AN7581_PCS_RX_FE_EQ_HZEN, ++ AN7581_PCS_RX_SIGDET_VTH_SEL, ++ AN7581_PCS_RX_SIGDET_PEAK, ++ AN7581_PCS_RX_SIGDET_LPF_CTRL, ++ AN7581_PCS_RX_TDC_CK_SEL, ++ AN7581_PCS_RX_PHYCK_RSTB, ++ AN7581_PCS_RX_PHYCK_SEL, ++ AN7581_PCS_RX_PHYCK_DIV, ++ AN7581_PCS_RX_PHY_CK_SEL_FORCE, ++ AN7581_PCS_RX_PHY_CK_SEL, ++ ++ AN7581_PCS_AEQ_OFORCE, ++ ++ AN7581_PCS_CDR_PD_EDGE_DIS, ++ AN7581_PCS_CDR_PD_PICAL_CKD8_INV, ++ ++ AN7581_PCS_CDR_PR_XFICK_EN, ++ AN7581_PCS_CDR_PR_MONPI_EN, ++ AN7581_PCS_CDR_PR_MONPR_EN, ++ AN7581_PCS_CDR_PR_KBAND_DIV, ++ AN7581_PCS_CDR_PR_BETA_SEL, ++ AN7581_PCS_CDR_PR_VCOADC_OS, ++ AN7581_PCS_CDR_PR_BETA_DAC, ++ AN7581_PCS_CDR_PR_FBKSEL, ++ AN7581_PCS_CDR_PR_DAC_BAND, ++ AN7581_PCS_CDR_PR_VREG_CKBUF_VAL, ++ AN7581_PCS_CDR_PR_VREG_IBAND_VAL, ++ AN7581_PCS_CDR_PR_CAP_EN, ++ AN7581_PCS_CDR_PR_INJ_FORCE_OFF, ++ ++ AN7581_PCS_CDR_BUF_IN_SR, ++ ++ AN7581_PCS_CDR_LPF_TOP_LIM, ++ AN7581_PCS_CDR_LPF_RATIO, ++ ++ AN7581_PCS_FIELDS_MAX, ++}; ++ ++static const struct reg_field an7581_pcs_fields[AN7581_PCS_FIELDS_MAX] = { ++ [AN7581_PCS_CMN_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_CMN_EN, 0, 0), ++ ++ [AN7581_PCS_JCPLL_SPARE_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SPARE_H, 8, 15), ++ ++ [AN7581_PCS_JCPLL_RST_DLY] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_RST_DLY, 0, 2), ++ [AN7581_PCS_JCPLL_PLL_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_RST_DLY, 8, 8), ++ [AN7581_PCS_JCPLL_SDM_DI_LS] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_RST_DLY, 16, 16), ++ [AN7581_PCS_JCPLL_SDM_DI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_RST_DLY, 24, 25), ++ ++ [AN7581_PCS_JCPLL_SDM_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_IFM, 24, 24), ++ [AN7581_PCS_JCPLL_SDM_ORD] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_IFM, 16, 17), ++ [AN7581_PCS_JCPLL_SDM_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_IFM, 8, 9), ++ [AN7581_PCS_JCPLL_SDM_IFM] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_IFM, 0, 0), ++ [AN7581_PCS_JCPLL_SDM_HREN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_HREN, 0, 0), ++ ++ [AN7581_PCS_JCPLL_SSC_PERIOD] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SSC_DELTA, 16, 31), ++ [AN7581_PCS_JCPLL_SSC_DELTA] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SSC_DELTA, 0, 15), ++ [AN7581_PCS_JCPLL_SSC_DELTA1] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SSC_TRI_EN, 8, 23), ++ [AN7581_PCS_JCPLL_SSC_TRI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SSC_TRI_EN, 0, 0), ++ [AN7581_PCS_JCPLL_SSC_PHASE_INI] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCO_TCLVAR, 17, 17), ++ [AN7581_PCS_JCPLL_SSC_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCO_TCLVAR, 16, 16), ++ [AN7581_PCS_JCPLL_TCL_KBAND_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SPARE_H, 16, 20), ++ ++ [AN7581_PCS_JCPLL_CHP_IOFST] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_IB_EXT_EN, 24, 29), ++ [AN7581_PCS_JCPLL_CHP_IBIAS] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_IB_EXT_EN, 16, 21), ++ [AN7581_PCS_JCPLL_LPF_SHCK_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_IB_EXT_EN, 8, 8), ++ ++ [AN7581_PCS_JCPLL_LPF_BWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BR, 24, 28), ++ [AN7581_PCS_JCPLL_LPF_BP] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BR, 16, 20), ++ [AN7581_PCS_JCPLL_LPF_BC] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BR, 8, 12), ++ [AN7581_PCS_JCPLL_LPF_BR] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BR, 0, 4), ++ [AN7581_PCS_JCPLL_LPF_BWC] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BWC, 0, 4), ++ ++ [AN7581_PCS_JCPLL_VCO_SCAPWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCODIV, 24, 26), ++ [AN7581_PCS_JCPLL_VCO_HALFLSB_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCODIV, 16, 16), ++ [AN7581_PCS_JCPLL_VCO_CFIX] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCODIV, 8, 9), ++ [AN7581_PCS_JCPLL_VCODIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCODIV, 0, 1), ++ [AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCO_TCLVAR, 8, 10), ++ [AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_H] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCO_TCLVAR, 3, 5), ++ [AN7581_PCS_JCPLL_VCO_TCLVAR] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_VCO_TCLVAR, 0, 2), ++ ++ [AN7581_PCS_JCPLL_POSTDIV_D5] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_MMD_PREDIV_MODE, 24, 24), ++ [AN7581_PCS_JCPLL_MMD_PREDIV_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_MMD_PREDIV_MODE, 0, 1), ++ ++ [AN7581_PCS_JCPLL_KBAND_KS] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_KBAND_KFC, 16, 17), ++ [AN7581_PCS_JCPLL_KBAND_KF] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_KBAND_KFC, 8, 9), ++ [AN7581_PCS_JCPLL_KBAND_KFC] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_KBAND_KFC, 0, 1), ++ [AN7581_PCS_JCPLL_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BWC, 24, 26), ++ [AN7581_PCS_JCPLL_KBAND_CODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BWC, 16, 23), ++ [AN7581_PCS_JCPLL_KBAND_OPTION] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_LPF_BWC, 8, 8), ++ ++ [AN7581_PCS_JCPLL_TCL_AMP_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_HREN, 24, 28), ++ [AN7581_PCS_JCPLL_TCL_AMP_GAIN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_HREN, 16, 18), ++ [AN7581_PCS_JCPLL_TCL_AMP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_SDM_HREN, 8, 8), ++ ++ [AN7581_PCS_JCPLL_TCL_LPF_BW] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_TCL_CMP_EN, 24, 26), ++ [AN7581_PCS_JCPLL_TCL_LPF_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_JCPLL_TCL_CMP_EN, 16, 16), ++ ++ [AN7581_PCS_TXPLL_LDO_VCO_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_PERIOD, 24, 25), ++ [AN7581_PCS_TXPLL_LDO_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_PERIOD, 16, 17), ++ [AN7581_PCS_TXPLL_PLL_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL, 24, 24), ++ [AN7581_PCS_TXPLL_RST_DLY] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL, 16, 18), ++ [AN7581_PCS_TXPLL_REFIN_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL, 8, 9), ++ [AN7581_PCS_TXPLL_REFIN_INTERNAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL, 0, 0), ++ [AN7581_PCS_TXPLL_SDM_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_DI_EN, 24, 25), ++ [AN7581_PCS_TXPLL_SDM_IFM] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_DI_EN, 16, 16), ++ [AN7581_PCS_TXPLL_SDM_DI_LS] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_DI_EN, 8, 9), ++ [AN7581_PCS_TXPLL_SDM_DI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_DI_EN, 0, 0), ++ [AN7581_PCS_TXPLL_SDM_HREN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD, 16, 16), ++ [AN7581_PCS_TXPLL_SDM_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD, 8, 8), ++ [AN7581_PCS_TXPLL_SDM_ORD] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD, 0, 1), ++ [AN7581_PCS_TXPLL_SSC_DELTA1] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_DELTA1, 16, 31), ++ [AN7581_PCS_TXPLL_SSC_DELTA] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_DELTA1, 0, 15), ++ [AN7581_PCS_TXPLL_SSC_TRI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_EN, 16, 16), ++ [AN7581_PCS_TXPLL_SSC_PHASE_INI] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_EN, 8, 8), ++ [AN7581_PCS_TXPLL_SSC_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_EN, 0, 0), ++ [AN7581_PCS_TXPLL_SSC_PERIOD] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SSC_PERIOD, 0, 15), ++ [AN7581_PCS_TXPLL_LPF_BC] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS, 24, 28), ++ [AN7581_PCS_TXPLL_LPF_BR] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS, 16, 20), ++ [AN7581_PCS_TXPLL_CHP_IOFST] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS, 8, 13), ++ [AN7581_PCS_TXPLL_CHP_IBIAS] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS, 0, 5), ++ [AN7581_PCS_TXPLL_LPF_BWC] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP, 16, 20), ++ [AN7581_PCS_TXPLL_LPF_BWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP, 8, 12), ++ [AN7581_PCS_TXPLL_LPF_BP] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP, 0, 4), ++ [AN7581_PCS_TXPLL_VCO_CFIX] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, 24, 25), ++ [AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN, 27, 29), ++ [AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_H] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN, 24, 26), ++ [AN7581_PCS_TXPLL_VCO_TCLVAR] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN, 16, 18), ++ [AN7581_PCS_TXPLL_VCO_SCAPWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN, 8, 10), ++ [AN7581_PCS_TXPLL_VCO_HALFLSB_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN, 0, 0), ++ [AN7581_PCS_TXPLL_KBAND_CODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_CODE, 0, 7), ++ [AN7581_PCS_TXPLL_KBAND_OPTION] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP, 24, 24), ++ [AN7581_PCS_TXPLL_KBAND_KS] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_KS, 0, 1), ++ [AN7581_PCS_TXPLL_KBAND_KF] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_CODE, 24, 25), ++ [AN7581_PCS_TXPLL_KBAND_KFC] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_CODE, 16, 17), ++ [AN7581_PCS_TXPLL_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_CODE, 8, 10), ++ [AN7581_PCS_TXPLL_MMD_PREDIV_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_KS, 16, 17), ++ [AN7581_PCS_TXPLL_POSTDIV_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_KBAND_KS, 8, 8), ++ [AN7581_PCS_TXPLL_VCODIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, 16, 17), ++ [AN7581_PCS_TXPLL_TCL_KBAND_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF, 0, 4), ++ [AN7581_PCS_TXPLL_TCL_AMP_GAIN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_AMP_GAIN, 0, 2), ++ [AN7581_PCS_TXPLL_TCL_AMP_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_AMP_GAIN, 8, 12), ++ [AN7581_PCS_TXPLL_TCL_LPF_BW] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, 8, 10), ++ [AN7581_PCS_TXPLL_TCL_LPF_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, 0, 0), ++ [AN7581_PCS_TXPLL_TCL_AMP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD, 24, 24), ++ ++ [AN7581_PCS_TX_DMEDGEGEN_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TX_CKLDO_EN, 24, 24), ++ [AN7581_PCS_TX_CKLDO_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TX_CKLDO_EN, 0, 0), ++ ++ [AN7581_PCS_RX_DAC_EYE_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_DAC_D1_BYPASS_AEQ, 24, 24), ++ [AN7581_PCS_RX_DAC_E1_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_DAC_D1_BYPASS_AEQ, 16, 16), ++ [AN7581_PCS_RX_DAC_E0_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_DAC_D1_BYPASS_AEQ, 8, 8), ++ [AN7581_PCS_RX_DAC_D1_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_DAC_D1_BYPASS_AEQ, 0, 0), ++ [AN7581_PCS_RX_DAC_D0_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_FE_PEAKING_CTRL_MSB, 24, 24), ++ [AN7581_PCS_RX_FE_VCM_GEN_PWDB] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_FE_VCM_GEN_PWDB, 0, 0), ++ ++ [AN7581_PCS_AEQ_OFORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_AEQ_CFORCE, 8, 19), ++ [AN7581_PCS_RX_OSCAL_FORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_OSCAL_WATCH_WNDW, 8, 17), ++ ++ [AN7581_PCS_CDR_PD_EDGE_DIS] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PD_PICAL_CKD8_INV, 8, 8), ++ [AN7581_PCS_CDR_PD_PICAL_CKD8_INV] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PD_PICAL_CKD8_INV, 0, 0), ++ ++ [AN7581_PCS_RX_DAC_MON] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, 24, 28), ++ [AN7581_PCS_CDR_PR_XFICK_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, 2, 2), ++ [AN7581_PCS_CDR_PR_MONPI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, 1, 1), ++ [AN7581_PCS_CDR_PR_MONPR_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, 0, 0), ++ ++ [AN7581_PCS_RX_REV_1_FE_BUF1_BIAS_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_REV_0, 24, 26), ++ [AN7581_PCS_RX_REV_1_FE_BUF2_BIAS_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_REV_0, 20, 22), ++ [AN7581_PCS_RX_REV_1_SIGDET_ILEAK] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_REV_0, 18, 19), ++ ++ [AN7581_PCS_CDR_LPF_TOP_LIM] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO, 8, 26), ++ [AN7581_PCS_CDR_LPF_RATIO] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO, 0, 1), ++ ++ [AN7581_PCS_CDR_PR_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_BETA_DAC, 24, 26), ++ [AN7581_PCS_CDR_PR_BETA_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_BETA_DAC, 16, 19), ++ [AN7581_PCS_CDR_PR_VCOADC_OS] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_BETA_DAC, 8, 11), ++ [AN7581_PCS_CDR_PR_BETA_DAC] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_BETA_DAC, 0, 6), ++ [AN7581_PCS_CDR_PR_FBKSEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, 24, 25), ++ [AN7581_PCS_CDR_PR_DAC_BAND] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, 16, 20), ++ [AN7581_PCS_CDR_PR_VREG_CKBUF_VAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, 8, 10), ++ [AN7581_PCS_CDR_PR_VREG_IBAND_VAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, 0, 2), ++ ++ [AN7581_PCS_RX_FE_VB_EQ3_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_FE_EQ_HZEN, 24, 24), ++ [AN7581_PCS_RX_FE_VB_EQ2_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_FE_EQ_HZEN, 16, 16), ++ [AN7581_PCS_RX_FE_VB_EQ1_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_FE_EQ_HZEN, 8, 8), ++ [AN7581_PCS_RX_FE_EQ_HZEN] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_FE_EQ_HZEN, 0, 0), ++ ++ [AN7581_PCS_CDR_PR_CAP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, 19, 19), ++ [AN7581_PCS_CDR_BUF_IN_SR] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, 16, 18), ++ ++ [AN7581_PCS_RX_SIGDET_VTH_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH, 16, 20), ++ [AN7581_PCS_RX_SIGDET_PEAK] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH, 8, 9), ++ [AN7581_PCS_RX_SIGDET_LPF_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_DAC_RANGE, 24, 25), ++ ++ [AN7581_PCS_RX_TDC_CK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, 24, 24), ++ [AN7581_PCS_RX_PHYCK_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, 16, 16), ++ [AN7581_PCS_RX_PHYCK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, 8, 9), ++ [AN7581_PCS_RX_PHYCK_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, 0, 7), ++ [AN7581_PCS_RX_PHY_CK_SEL_FORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_BUSBIT_SEL, 24, 24), ++ [AN7581_PCS_RX_PHY_CK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_RX_BUSBIT_SEL, 16, 16), ++ ++ [AN7581_PCS_CDR_PR_INJ_FORCE_OFF] = REG_FIELD(AIROHA_PCS_ANA_PXP_CDR_PR_INJ_MODE, 24, 24), ++}; ++ ++static const struct reg_field an7581_pcs_pcie0_fields[AN7581_PCS_FIELDS_MAX] = { ++ [AN7581_PCS_CMN_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CMN_EN, 0, 0), ++ ++ [AN7581_PCS_JCPLL_SPARE_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_VTP_EN, 24, 31), ++ ++ [AN7581_PCS_JCPLL_RST_DLY] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 0, 2), ++ [AN7581_PCS_JCPLL_PLL_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 8, 8), ++ [AN7581_PCS_JCPLL_SDM_DI_LS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 16, 16), ++ [AN7581_PCS_JCPLL_SDM_DI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 24, 25), ++ ++ [AN7581_PCS_JCPLL_SDM_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 24, 24), ++ [AN7581_PCS_JCPLL_SDM_ORD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 16, 17), ++ [AN7581_PCS_JCPLL_SDM_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 8, 9), ++ [AN7581_PCS_JCPLL_SDM_IFM] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 0, 0), ++ [AN7581_PCS_JCPLL_SDM_HREN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 0, 0), ++ ++ [AN7581_PCS_JCPLL_CHP_IOFST] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN, 24, 29), ++ [AN7581_PCS_JCPLL_CHP_IBIAS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN, 16, 21), ++ [AN7581_PCS_JCPLL_LPF_SHCK_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN, 8, 8), ++ ++ [AN7581_PCS_JCPLL_LPF_BWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 24, 28), ++ [AN7581_PCS_JCPLL_LPF_BP] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 16, 20), ++ [AN7581_PCS_JCPLL_LPF_BC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 8, 12), ++ [AN7581_PCS_JCPLL_LPF_BR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 0, 4), ++ [AN7581_PCS_JCPLL_LPF_BWC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 0, 4), ++ ++ [AN7581_PCS_JCPLL_VCO_SCAPWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 24, 26), ++ [AN7581_PCS_JCPLL_VCO_HALFLSB_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 16, 16), ++ [AN7581_PCS_JCPLL_VCO_CFIX] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 8, 9), ++ [AN7581_PCS_JCPLL_VCODIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 0, 1), ++ [AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR, 16, 18), ++ [AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_H] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR, 8, 10), ++ [AN7581_PCS_JCPLL_VCO_TCLVAR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR, 0, 2), ++ ++ [AN7581_PCS_JCPLL_SSC_DELTA] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_DELTA1, 16, 31), ++ [AN7581_PCS_JCPLL_SSC_DELTA1] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_DELTA1, 0, 15), ++ [AN7581_PCS_JCPLL_SSC_PERIOD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_PERIOD, 0, 15), ++ [AN7581_PCS_JCPLL_SSC_TRI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN, 16, 16), ++ [AN7581_PCS_JCPLL_SSC_PHASE_INI] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN, 8, 8), ++ [AN7581_PCS_JCPLL_SSC_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN, 0, 0), ++ [AN7581_PCS_JCPLL_TCL_KBAND_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_KBAND_VREF, 0, 4), ++ ++ [AN7581_PCS_JCPLL_POSTDIV_D5] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_MMD_PREDIV_MODE, 24, 24), ++ [AN7581_PCS_JCPLL_MMD_PREDIV_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_MMD_PREDIV_MODE, 0, 1), ++ ++ [AN7581_PCS_JCPLL_KBAND_KS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC, 16, 17), ++ [AN7581_PCS_JCPLL_KBAND_KF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC, 8, 9), ++ [AN7581_PCS_JCPLL_KBAND_KFC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC, 0, 1), ++ [AN7581_PCS_JCPLL_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 24, 26), ++ [AN7581_PCS_JCPLL_KBAND_CODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 16, 23), ++ [AN7581_PCS_JCPLL_KBAND_OPTION] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 8, 8), ++ ++ [AN7581_PCS_JCPLL_TCL_AMP_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 24, 28), ++ [AN7581_PCS_JCPLL_TCL_AMP_GAIN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 16, 18), ++ [AN7581_PCS_JCPLL_TCL_AMP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 8, 8), ++ ++ [AN7581_PCS_JCPLL_TCL_LPF_BW] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_CMP_EN, 24, 26), ++ [AN7581_PCS_JCPLL_TCL_LPF_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_CMP_EN, 16, 16), ++ ++ [AN7581_PCS_TXPLL_LDO_VCO_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD, 24, 25), ++ [AN7581_PCS_TXPLL_LDO_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD, 16, 17), ++ [AN7581_PCS_TXPLL_PLL_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 16, 16), ++ [AN7581_PCS_TXPLL_RST_DLY] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 8, 10), ++ [AN7581_PCS_TXPLL_REFIN_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 0, 1), ++ [AN7581_PCS_TXPLL_REFIN_INTERNAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_PHY_CK2_EN, 24, 24), ++ [AN7581_PCS_TXPLL_SDM_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 16, 17), ++ [AN7581_PCS_TXPLL_SDM_IFM] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 8, 8), ++ [AN7581_PCS_TXPLL_SDM_DI_LS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 0, 1), ++ [AN7581_PCS_TXPLL_SDM_DI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 24, 24), ++ [AN7581_PCS_TXPLL_SDM_HREN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT, 8, 8), ++ [AN7581_PCS_TXPLL_SDM_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT, 0, 0), ++ [AN7581_PCS_TXPLL_SDM_ORD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 24, 25), ++ [AN7581_PCS_TXPLL_SSC_DELTA1] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_DELTA1, 16, 31), ++ [AN7581_PCS_TXPLL_SSC_DELTA] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_DELTA1, 0, 15), ++ [AN7581_PCS_TXPLL_SSC_TRI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN, 16, 16), ++ [AN7581_PCS_TXPLL_SSC_PHASE_INI] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN, 8, 8), ++ [AN7581_PCS_TXPLL_SSC_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN, 0, 0), ++ [AN7581_PCS_TXPLL_SSC_PERIOD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD, 0, 15), ++ [AN7581_PCS_TXPLL_LPF_BC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 16, 20), ++ [AN7581_PCS_TXPLL_LPF_BR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 8, 12), ++ [AN7581_PCS_TXPLL_CHP_IOFST] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 0, 5), ++ [AN7581_PCS_TXPLL_CHP_IBIAS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_750M_SYS_CK_EN, 24, 29), ++ [AN7581_PCS_TXPLL_LPF_BWC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 8, 12), ++ [AN7581_PCS_TXPLL_LPF_BWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 0, 4), ++ [AN7581_PCS_TXPLL_LPF_BP] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 24, 28), ++ [AN7581_PCS_TXPLL_VCO_CFIX] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 16, 17), ++ [AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 24, 26), ++ [AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_H] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 16, 18), ++ [AN7581_PCS_TXPLL_VCO_TCLVAR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 8, 10), ++ [AN7581_PCS_TXPLL_VCO_SCAPWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 0, 2), ++ [AN7581_PCS_TXPLL_VCO_HALFLSB_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 24, 24), ++ [AN7581_PCS_TXPLL_KBAND_CODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 24, 31), ++ [AN7581_PCS_TXPLL_KBAND_OPTION] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 16, 16), ++ [AN7581_PCS_TXPLL_KBAND_KS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 24, 25), ++ [AN7581_PCS_TXPLL_KBAND_KF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 16, 17), ++ [AN7581_PCS_TXPLL_KBAND_KFC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 8, 9), ++ [AN7581_PCS_TXPLL_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 0, 2), ++ [AN7581_PCS_TXPLL_MMD_PREDIV_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_POSTDIV_EN, 8, 9), ++ [AN7581_PCS_TXPLL_POSTDIV_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_POSTDIV_EN, 0, 0), ++ [AN7581_PCS_TXPLL_VCODIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 8, 9), ++ [AN7581_PCS_TXPLL_TCL_KBAND_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_KBAND_VREF, 0, 4), ++ [AN7581_PCS_TXPLL_TCL_AMP_GAIN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT, 24, 26), ++ [AN7581_PCS_TXPLL_TCL_AMP_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_AMP_VREF, 0, 4), ++ [AN7581_PCS_TXPLL_TCL_LPF_BW] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 0, 2), ++ [AN7581_PCS_TXPLL_TCL_LPF_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, 0, 0), ++ [AN7581_PCS_TXPLL_TCL_AMP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_AMP_VREF, 24, 24), ++ ++ [AN7581_PCS_TX_DMEDGEGEN_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TX0_CKLDO_EN, 24, 24), ++ [AN7581_PCS_TX_CKLDO_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TX0_CKLDO_EN, 0, 0), ++ ++ [AN7581_PCS_RX_DAC_EYE_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_DAC_EYE_BYPASS_AEQ, 0, 0), ++ [AN7581_PCS_RX_DAC_E1_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_DAC_D0_BYPASS_AEQ, 24, 24), ++ [AN7581_PCS_RX_DAC_E0_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_DAC_D0_BYPASS_AEQ, 16, 16), ++ [AN7581_PCS_RX_DAC_D1_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_DAC_D0_BYPASS_AEQ, 8, 8), ++ [AN7581_PCS_RX_DAC_D0_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_DAC_D0_BYPASS_AEQ, 0, 0), ++ [AN7581_PCS_RX_FE_VCM_GEN_PWDB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_FE_VB_EQ2_EN, 16, 16), ++ ++ [AN7581_PCS_AEQ_OFORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_AEQ0_CFORCE, 8, 19), ++ [AN7581_PCS_RX_OSCAL_FORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_OSCAL_FORCE, 8, 17), ++ ++ [AN7581_PCS_CDR_PD_EDGE_DIS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PD_PICAL_CKD8_INV, 8, 8), ++ [AN7581_PCS_CDR_PD_PICAL_CKD8_INV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PD_PICAL_CKD8_INV, 0, 0), ++ ++ [AN7581_PCS_RX_DAC_MON] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_DAC_MON, 0, 4), ++ [AN7581_PCS_CDR_PR_XFICK_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_MONPI_EN, 8, 8), ++ [AN7581_PCS_CDR_PR_MONPI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_MONPI_EN, 0, 0), ++ [AN7581_PCS_CDR_PR_MONPR_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_COR_HBW_EN, 24, 24), ++ ++ [AN7581_PCS_RX_REV_1_FE_BUF1_BIAS_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_REV_0, 24, 26), ++ [AN7581_PCS_RX_REV_1_FE_BUF2_BIAS_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_REV_0, 20, 22), ++ [AN7581_PCS_RX_REV_1_SIGDET_ILEAK] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_REV_0, 18, 19), ++ ++ [AN7581_PCS_CDR_LPF_TOP_LIM] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_LPF_RATIO, 8, 26), ++ [AN7581_PCS_CDR_LPF_RATIO] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_LPF_RATIO, 0, 1), ++ ++ [AN7581_PCS_CDR_PR_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BETA_DAC, 24, 26), ++ [AN7581_PCS_CDR_PR_BETA_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BETA_DAC, 16, 19), ++ [AN7581_PCS_CDR_PR_VCOADC_OS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BETA_DAC, 8, 11), ++ [AN7581_PCS_CDR_PR_BETA_DAC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BETA_DAC, 0, 6), ++ [AN7581_PCS_CDR_PR_FBKSEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_VREG_IBAND_VAL, 24, 25), ++ [AN7581_PCS_CDR_PR_DAC_BAND] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_VREG_IBAND_VAL, 16, 20), ++ [AN7581_PCS_CDR_PR_VREG_CKBUF_VAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_VREG_IBAND_VAL, 8, 10), ++ [AN7581_PCS_CDR_PR_VREG_IBAND_VAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_VREG_IBAND_VAL, 0, 2), ++ ++ [AN7581_PCS_RX_FE_VB_EQ3_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_FE_VB_EQ2_EN, 8, 8), ++ [AN7581_PCS_RX_FE_VB_EQ2_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_FE_VB_EQ2_EN, 0, 0), ++ [AN7581_PCS_RX_FE_VB_EQ1_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_VTH_SEL, 24, 24), ++ [AN7581_PCS_RX_FE_EQ_HZEN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_VTH_SEL, 16, 16), ++ ++ [AN7581_PCS_CDR_PR_CAP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BUF_IN_SR, 8, 8), ++ [AN7581_PCS_CDR_BUF_IN_SR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_BUF_IN_SR, 0, 2), ++ ++ [AN7581_PCS_RX_SIGDET_VTH_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_VTH_SEL, 0, 4), ++ [AN7581_PCS_RX_SIGDET_PEAK] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_DCTEST_EN, 24, 25), ++ [AN7581_PCS_RX_SIGDET_LPF_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_SIGDET_DCTEST_EN, 8, 9), ++ ++ [AN7581_PCS_RX_TDC_CK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_PHYCK_DIV, 24, 24), ++ [AN7581_PCS_RX_PHYCK_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_PHYCK_DIV, 16, 16), ++ [AN7581_PCS_RX_PHYCK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_PHYCK_DIV, 8, 9), ++ [AN7581_PCS_RX_PHYCK_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_PHYCK_DIV, 0, 7), ++ [AN7581_PCS_RX_PHY_CK_SEL_FORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_BUSBIT_SEL, 24, 24), ++ [AN7581_PCS_RX_PHY_CK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX0_BUSBIT_SEL, 16, 16), ++ ++ [AN7581_PCS_CDR_PR_INJ_FORCE_OFF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR0_PR_INJ_MODE, 24, 24), ++}; ++ ++static const struct reg_field an7581_pcs_pcie1_fields[AN7581_PCS_FIELDS_MAX] = { ++ [AN7581_PCS_CMN_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CMN_EN, 0, 0), ++ ++ [AN7581_PCS_JCPLL_SPARE_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_VTP_EN, 24, 31), ++ ++ [AN7581_PCS_JCPLL_RST_DLY] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 0, 2), ++ [AN7581_PCS_JCPLL_PLL_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 8, 8), ++ [AN7581_PCS_JCPLL_SDM_DI_LS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 16, 16), ++ [AN7581_PCS_JCPLL_SDM_DI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_RST_DLY, 24, 25), ++ ++ [AN7581_PCS_JCPLL_SDM_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 24, 24), ++ [AN7581_PCS_JCPLL_SDM_ORD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 16, 17), ++ [AN7581_PCS_JCPLL_SDM_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 8, 9), ++ [AN7581_PCS_JCPLL_SDM_IFM] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_IFM, 0, 0), ++ [AN7581_PCS_JCPLL_SDM_HREN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 0, 0), ++ ++ [AN7581_PCS_JCPLL_CHP_IOFST] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN, 24, 29), ++ [AN7581_PCS_JCPLL_CHP_IBIAS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN, 16, 21), ++ [AN7581_PCS_JCPLL_LPF_SHCK_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_IB_EXT_EN, 8, 8), ++ ++ [AN7581_PCS_JCPLL_LPF_BWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 24, 28), ++ [AN7581_PCS_JCPLL_LPF_BP] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 16, 20), ++ [AN7581_PCS_JCPLL_LPF_BC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 8, 12), ++ [AN7581_PCS_JCPLL_LPF_BR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BR, 0, 4), ++ [AN7581_PCS_JCPLL_LPF_BWC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 0, 4), ++ ++ [AN7581_PCS_JCPLL_VCO_SCAPWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 24, 26), ++ [AN7581_PCS_JCPLL_VCO_HALFLSB_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 16, 16), ++ [AN7581_PCS_JCPLL_VCO_CFIX] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 8, 9), ++ [AN7581_PCS_JCPLL_VCODIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCODIV, 0, 1), ++ [AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR, 16, 18), ++ [AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_H] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR, 8, 10), ++ [AN7581_PCS_JCPLL_VCO_TCLVAR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_VCO_TCLVAR, 0, 2), ++ ++ [AN7581_PCS_JCPLL_SSC_DELTA] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_DELTA1, 16, 31), ++ [AN7581_PCS_JCPLL_SSC_DELTA1] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_DELTA1, 0, 15), ++ [AN7581_PCS_JCPLL_SSC_PERIOD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_PERIOD, 0, 15), ++ [AN7581_PCS_JCPLL_SSC_TRI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN, 16, 16), ++ [AN7581_PCS_JCPLL_SSC_PHASE_INI] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN, 8, 8), ++ [AN7581_PCS_JCPLL_SSC_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SSC_EN, 0, 0), ++ [AN7581_PCS_JCPLL_TCL_KBAND_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_KBAND_VREF, 0, 4), ++ ++ [AN7581_PCS_JCPLL_POSTDIV_D5] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_MMD_PREDIV_MODE, 24, 24), ++ [AN7581_PCS_JCPLL_MMD_PREDIV_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_MMD_PREDIV_MODE, 0, 1), ++ ++ [AN7581_PCS_JCPLL_KBAND_KS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC, 16, 17), ++ [AN7581_PCS_JCPLL_KBAND_KF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC, 8, 9), ++ [AN7581_PCS_JCPLL_KBAND_KFC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_KBAND_KFC, 0, 1), ++ [AN7581_PCS_JCPLL_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 24, 26), ++ [AN7581_PCS_JCPLL_KBAND_CODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 16, 23), ++ [AN7581_PCS_JCPLL_KBAND_OPTION] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_LPF_BWC, 8, 8), ++ ++ [AN7581_PCS_JCPLL_TCL_AMP_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 24, 28), ++ [AN7581_PCS_JCPLL_TCL_AMP_GAIN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 16, 18), ++ [AN7581_PCS_JCPLL_TCL_AMP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_SDM_HREN, 8, 8), ++ ++ [AN7581_PCS_JCPLL_TCL_LPF_BW] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_CMP_EN, 24, 26), ++ [AN7581_PCS_JCPLL_TCL_LPF_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_JCPLL_TCL_CMP_EN, 16, 16), ++ ++ [AN7581_PCS_TXPLL_LDO_VCO_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD, 24, 25), ++ [AN7581_PCS_TXPLL_LDO_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD, 16, 17), ++ [AN7581_PCS_TXPLL_PLL_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 16, 16), ++ [AN7581_PCS_TXPLL_RST_DLY] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 8, 10), ++ [AN7581_PCS_TXPLL_REFIN_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 0, 1), ++ [AN7581_PCS_TXPLL_REFIN_INTERNAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_PHY_CK2_EN, 24, 24), ++ [AN7581_PCS_TXPLL_SDM_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 16, 17), ++ [AN7581_PCS_TXPLL_SDM_IFM] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 8, 8), ++ [AN7581_PCS_TXPLL_SDM_DI_LS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 0, 1), ++ [AN7581_PCS_TXPLL_SDM_DI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_REFIN_DIV, 24, 24), ++ [AN7581_PCS_TXPLL_SDM_HREN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT, 8, 8), ++ [AN7581_PCS_TXPLL_SDM_OUT] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT, 0, 0), ++ [AN7581_PCS_TXPLL_SDM_ORD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_DI_LS, 24, 25), ++ [AN7581_PCS_TXPLL_SSC_DELTA1] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_DELTA1, 16, 31), ++ [AN7581_PCS_TXPLL_SSC_DELTA] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_DELTA1, 0, 15), ++ [AN7581_PCS_TXPLL_SSC_TRI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN, 16, 16), ++ [AN7581_PCS_TXPLL_SSC_PHASE_INI] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN, 8, 8), ++ [AN7581_PCS_TXPLL_SSC_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_EN, 0, 0), ++ [AN7581_PCS_TXPLL_SSC_PERIOD] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SSC_PERIOD, 0, 15), ++ [AN7581_PCS_TXPLL_LPF_BC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 16, 20), ++ [AN7581_PCS_TXPLL_LPF_BR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 8, 12), ++ [AN7581_PCS_TXPLL_CHP_IOFST] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 0, 5), ++ [AN7581_PCS_TXPLL_CHP_IBIAS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_750M_SYS_CK_EN, 24, 29), ++ [AN7581_PCS_TXPLL_LPF_BWC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 8, 12), ++ [AN7581_PCS_TXPLL_LPF_BWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 0, 4), ++ [AN7581_PCS_TXPLL_LPF_BP] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_CHP_IOFST, 24, 28), ++ [AN7581_PCS_TXPLL_VCO_CFIX] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 16, 17), ++ [AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_L] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 24, 26), ++ [AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_H] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 16, 18), ++ [AN7581_PCS_TXPLL_VCO_TCLVAR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 8, 10), ++ [AN7581_PCS_TXPLL_VCO_SCAPWR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_VCO_SCAPWR, 0, 2), ++ [AN7581_PCS_TXPLL_VCO_HALFLSB_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 24, 24), ++ [AN7581_PCS_TXPLL_KBAND_CODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 24, 31), ++ [AN7581_PCS_TXPLL_KBAND_OPTION] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_LPF_BWR, 16, 16), ++ [AN7581_PCS_TXPLL_KBAND_KS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 24, 25), ++ [AN7581_PCS_TXPLL_KBAND_KF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 16, 17), ++ [AN7581_PCS_TXPLL_KBAND_KFC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 8, 9), ++ [AN7581_PCS_TXPLL_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_KBAND_DIV, 0, 2), ++ [AN7581_PCS_TXPLL_MMD_PREDIV_MODE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_POSTDIV_EN, 8, 9), ++ [AN7581_PCS_TXPLL_POSTDIV_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_POSTDIV_EN, 0, 0), ++ [AN7581_PCS_TXPLL_VCODIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 8, 9), ++ [AN7581_PCS_TXPLL_TCL_KBAND_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_KBAND_VREF, 0, 4), ++ [AN7581_PCS_TXPLL_TCL_AMP_GAIN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_SDM_OUT, 24, 26), ++ [AN7581_PCS_TXPLL_TCL_AMP_VREF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_AMP_VREF, 0, 4), ++ [AN7581_PCS_TXPLL_TCL_LPF_BW] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_LPF_BW, 0, 2), ++ [AN7581_PCS_TXPLL_TCL_LPF_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, 0, 0), ++ [AN7581_PCS_TXPLL_TCL_AMP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TXPLL_TCL_AMP_VREF, 24, 24), ++ ++ [AN7581_PCS_TX_DMEDGEGEN_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TX1_CKLDO_EN, 24, 24), ++ [AN7581_PCS_TX_CKLDO_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_TX1_CKLDO_EN, 0, 0), ++ ++ [AN7581_PCS_RX_DAC_EYE_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_DAC_D1_BYPASS_AEQ, 24, 24), ++ [AN7581_PCS_RX_DAC_E1_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_DAC_D1_BYPASS_AEQ, 16, 16), ++ [AN7581_PCS_RX_DAC_E0_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_DAC_D1_BYPASS_AEQ, 8, 8), ++ [AN7581_PCS_RX_DAC_D1_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_DAC_D1_BYPASS_AEQ, 0, 0), ++ [AN7581_PCS_RX_DAC_D0_BYPASS_AEQ] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_FE_PEACKING_CTRL_LSB, 24, 24), ++ [AN7581_PCS_RX_FE_VCM_GEN_PWDB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_FE_VB_EQ1_EN, 24, 24), ++ ++ [AN7581_PCS_AEQ_OFORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_AEQ1_CFORCE, 16, 27), ++ [AN7581_PCS_RX_OSCAL_FORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_OSCAL_WATCH_WNDW, 16, 25), ++ ++ [AN7581_PCS_CDR_PD_EDGE_DIS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PD_PICAL_CKD8_INV, 8, 8), ++ [AN7581_PCS_CDR_PD_PICAL_CKD8_INV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PD_PICAL_CKD8_INV, 0, 0), ++ ++ [AN7581_PCS_RX_DAC_MON] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BUF_IN_SR, 16, 20), ++ [AN7581_PCS_CDR_PR_XFICK_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_MONPI_EN, 8, 8), ++ [AN7581_PCS_CDR_PR_MONPI_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_MONPI_EN, 0, 0), ++ [AN7581_PCS_CDR_PR_MONPR_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_COR_HBW_EN, 24, 24), ++ ++ [AN7581_PCS_RX_REV_1_FE_BUF1_BIAS_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_REV_0, 24, 26), ++ [AN7581_PCS_RX_REV_1_FE_BUF2_BIAS_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_REV_0, 20, 22), ++ [AN7581_PCS_RX_REV_1_SIGDET_ILEAK] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_REV_0, 18, 19), ++ ++ [AN7581_PCS_CDR_LPF_TOP_LIM] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_LPF_RATIO, 8, 26), ++ [AN7581_PCS_CDR_LPF_RATIO] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_LPF_RATIO, 0, 1), ++ ++ [AN7581_PCS_CDR_PR_KBAND_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BETA_DAC, 24, 26), ++ [AN7581_PCS_CDR_PR_BETA_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BETA_DAC, 16, 19), ++ [AN7581_PCS_CDR_PR_VCOADC_OS] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BETA_DAC, 8, 11), ++ [AN7581_PCS_CDR_PR_BETA_DAC] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BETA_DAC, 0, 6), ++ [AN7581_PCS_CDR_PR_FBKSEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_VREG_IBAND_VAL, 24, 25), ++ [AN7581_PCS_CDR_PR_DAC_BAND] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_VREG_IBAND_VAL, 16, 20), ++ [AN7581_PCS_CDR_PR_VREG_CKBUF_VAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_VREG_IBAND_VAL, 8, 10), ++ [AN7581_PCS_CDR_PR_VREG_IBAND_VAL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_VREG_IBAND_VAL, 0, 2), ++ ++ [AN7581_PCS_RX_FE_VB_EQ3_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_FE_VB_EQ1_EN, 16, 16), ++ [AN7581_PCS_RX_FE_VB_EQ2_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_FE_VB_EQ1_EN, 8, 8), ++ [AN7581_PCS_RX_FE_VB_EQ1_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_FE_VB_EQ1_EN, 0, 0), ++ [AN7581_PCS_RX_FE_EQ_HZEN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_FE_50OHMS_SEL, 24, 24), ++ ++ [AN7581_PCS_CDR_PR_CAP_EN] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BUF_IN_SR, 8, 8), ++ [AN7581_PCS_CDR_BUF_IN_SR] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_BUF_IN_SR, 0, 2), ++ ++ [AN7581_PCS_RX_SIGDET_VTH_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_SIGDET_NOVTH, 16, 20), ++ [AN7581_PCS_RX_SIGDET_PEAK] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_SIGDET_NOVTH, 8, 9), ++ [AN7581_PCS_RX_SIGDET_LPF_CTRL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_DAC_RANGE_EYE, 24, 25), ++ ++ [AN7581_PCS_RX_TDC_CK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_PHYCK_DIV, 24, 24), ++ [AN7581_PCS_RX_PHYCK_RSTB] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_PHYCK_DIV, 16, 16), ++ [AN7581_PCS_RX_PHYCK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_PHYCK_DIV, 8, 9), ++ [AN7581_PCS_RX_PHYCK_DIV] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_PHYCK_DIV, 0, 7), ++ [AN7581_PCS_RX_PHY_CK_SEL_FORCE] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_BUSBIT_SEL, 24, 24), ++ [AN7581_PCS_RX_PHY_CK_SEL] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_RX1_BUSBIT_SEL, 16, 16), ++ ++ [AN7581_PCS_CDR_PR_INJ_FORCE_OFF] = REG_FIELD(AIROHA_PCS_ANA_PXP_2L_CDR1_PR_INJ_MODE, 24, 24), ++}; ++ ++static void an7581_pcs_jcpll_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap_field **pcs_ana_fields = priv->pcs_ana_fields[index]; ++ struct regmap *pcs_pma; ++ u32 kband_vref; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ kband_vref = 0x10; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ kband_vref = 0xf; ++ break; ++ default: ++ return; ++ } ++ ++ /* This comment only apply to Serdes PCIe that expose ++ * 2 PCS. ++ * ++ * The Serdes PCIe expose 2 PCS but always require ++ * the PMA for the first PCS to be configured ++ * for correct functionality for JCPLL. ++ */ ++ pcs_pma = priv->pcs_pma[0]; ++ ++ /* Setup LDO */ ++ usleep_range(200, 300); ++ ++ regmap_field_set_bits(pcs_ana_fields[AN7581_PCS_JCPLL_SPARE_L], ++ AIROHA_PCS_ANA_JCPLL_SPARE_L_LDO); ++ ++ /* Setup RSTB */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_RST_DLY], ++ AIROHA_PCS_ANA_JCPLL_RST_DLY_150_200); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_PLL_RSTB], 0x1); ++ ++ /* Enable PLL force selection and Force Disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_JCPLL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_EN); ++ ++ /* Setup SDM */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_DI_LS], ++ AIROHA_PCS_ANA_JCPLL_SDM_DI_LS_2_23); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_DI_EN], 0x0); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_OUT], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_ORD], ++ AIROHA_PCS_ANA_JCPLL_SDM_ORD_3SDM); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_MODE], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_IFM], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SDM_HREN], 0x0); ++ ++ /* Setup SSC */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SSC_DELTA], 0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SSC_DELTA1], 0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SSC_PERIOD], 0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SSC_TRI_EN], 0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SSC_EN], 0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_SSC_PHASE_INI], 0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_TCLVAR], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_L], 0); ++ ++ /* Setup LPF */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_CHP_IOFST], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_CHP_IBIAS], 0x18); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_LPF_SHCK_EN], 0); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_LPF_BWR], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_LPF_BP], 0x10); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_LPF_BC], 0x1f); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_LPF_BR], BIT(3) | BIT(1)); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_LPF_BWC], 0x0); ++ ++ /* Setup VCO */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_SCAPWR], 0x4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_HALFLSB_EN], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_CFIX], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_L], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_VCOVAR_BIAS_H], 0x3); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCO_TCLVAR], 0x3); ++ ++ /* Setup PCW */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_PCW, ++ AIROHA_PCS_PMA_FORCE_DA_JCPLL_SDM_PCW, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_JCPLL_SDM_PCW, 0x25800000)); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_VOS, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_SDM_PCW); ++ ++ /* Setup DIV */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_POSTDIV_D5], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_MMD_PREDIV_MODE], ++ AIROHA_PCS_ANA_JCPLL_MMD_PREDIV_MODE_2); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_VCODIV], ++ AIROHA_PCS_ANA_JCPLL_VCODIV_1); ++ ++ /* Setup KBand */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_KBAND_KS], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_KBAND_KF], 0x3); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_KBAND_KFC], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_KBAND_DIV], 0x2); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_KBAND_CODE], 0xe4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_KBAND_OPTION], 0x0); ++ ++ /* Setup TCL */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_TCL_KBAND_VREF], ++ kband_vref); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_TCL_AMP_VREF], 0x5); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_TCL_AMP_GAIN], ++ AIROHA_PCS_ANA_JCPLL_TCL_AMP_GAIN_4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_TCL_AMP_EN], 0x1); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_TCL_LPF_BW], ++ AIROHA_PCS_ANA_JCPLL_TCL_LPF_BW_1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_JCPLL_TCL_LPF_EN], 0x1); ++ ++ /* Enable PLL */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_DA_JCPLL_EN); ++ ++ /* Enale PLL Output */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_CKOUT_EN | ++ AIROHA_PCS_PMA_FORCE_DA_JCPLL_CKOUT_EN); ++} ++ ++static void an7581_pcs_txpll_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap_field **pcs_ana_fields = priv->pcs_ana_fields[index]; ++ u32 lpf_chp_ibias, lpf_bp, lpf_bwr, lpf_bwc; ++ struct regmap *pcs_pma; ++ u32 tcl_amp_vref; ++ bool sdm_hren; ++ u32 vco_cfix; ++ bool vcodiv; ++ u32 pcw; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ lpf_chp_ibias = 0xf; ++ lpf_bp = BIT(1); ++ lpf_bwr = BIT(3) | BIT(1) | BIT(0); ++ lpf_bwc = BIT(4) | BIT(3); ++ vco_cfix = BIT(1) | BIT(0); ++ pcw = BIT(27); ++ tcl_amp_vref = BIT(3) | BIT(1) | BIT(0); ++ vcodiv = false; ++ sdm_hren = false; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ lpf_chp_ibias = 0xa; ++ lpf_bp = BIT(2) | BIT(0); ++ lpf_bwr = 0; ++ lpf_bwc = 0; ++ vco_cfix = 0; ++ pcw = BIT(27) | BIT(25); ++ tcl_amp_vref = BIT(3) | BIT(2) | BIT(0); ++ vcodiv = true; ++ sdm_hren = false; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ lpf_chp_ibias = 0xf; ++ lpf_bp = BIT(1); ++ lpf_bwr = BIT(3) | BIT(1) | BIT(0); ++ lpf_bwc = BIT(4) | BIT(3); ++ vco_cfix = BIT(0); ++ pcw = BIT(27) | BIT(22); ++ tcl_amp_vref = BIT(3) | BIT(1) | BIT(0); ++ vcodiv = false; ++ sdm_hren = true; ++ break; ++ default: ++ return; ++ } ++ ++ /* This comment only apply to Serdes PCIe that expose ++ * 2 PCS. ++ * ++ * The Serdes PCIe expose 2 PCS but always require ++ * the PMA for the first PCS to be configured ++ * for correct functionality for TXPLL. ++ */ ++ pcs_pma = priv->pcs_pma[0]; ++ ++ /* Setup VCO LDO Output */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LDO_VCO_OUT], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LDO_OUT], 0x1); ++ ++ /* Setup RSTB */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_PLL_RSTB], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_RST_DLY], 0x4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_REFIN_DIV], ++ AIROHA_PCS_ANA_TXPLL_REFIN_DIV_1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_REFIN_INTERNAL], 0x1); ++ ++ /* Enable PLL force selection and Force Disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_EN); ++ ++ /* Setup SDM */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_MODE], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_IFM], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_DI_LS], ++ AIROHA_PCS_ANA_TXPLL_SDM_DI_LS_2_23); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_DI_EN], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_HREN], sdm_hren); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_OUT], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SDM_ORD], ++ AIROHA_PCS_ANA_TXPLL_SDM_ORD_3SDM); ++ ++ /* Setup SSC */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SSC_DELTA1], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SSC_DELTA], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SSC_TRI_EN], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SSC_PHASE_INI], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SSC_EN], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_SSC_PERIOD], 0x0); ++ ++ /* Setup LPF */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_CHP_IBIAS], ++ lpf_chp_ibias); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_CHP_IOFST], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LPF_BC], 0x1f); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LPF_BR], 0x5); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LPF_BWC], lpf_bwc); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LPF_BWR], lpf_bwr); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_LPF_BP], lpf_bp); ++ ++ /* Setup VCO */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCO_CFIX], vco_cfix); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_L], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCO_VCOVAR_BIAS_H], 0x4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCO_TCLVAR], 0x4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCO_SCAPWR], 0x7); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCO_HALFLSB_EN], 0x1); ++ ++ /* Setup PCW */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW, ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW, pcw); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_IDAC, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW); ++ ++ /* Setup KBand */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_KBAND_CODE], 0xe4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_KBAND_OPTION], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_KBAND_KS], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_KBAND_KF], 0x3); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_KBAND_KFC], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_KBAND_DIV], 0x4); ++ ++ /* Setup DIV */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_POSTDIV_EN], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_MMD_PREDIV_MODE], ++ AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_2); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_VCODIV], ++ vcodiv ? AIROHA_PCS_ANA_TXPLL_VCODIV_2 : ++ AIROHA_PCS_ANA_TXPLL_VCODIV_1); ++ ++ /* Setup TCL */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_TCL_KBAND_VREF], 0xf); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_TCL_AMP_VREF], ++ tcl_amp_vref); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_TCL_AMP_GAIN], ++ AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_TCL_LPF_BW], ++ AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_0_5); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_TCL_LPF_EN], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TXPLL_TCL_AMP_EN], 0x1); ++ ++ /* Enable PLL */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_EN); ++ ++ /* Enale PLL Output */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_CKOUT_EN | ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_CKOUT_EN); ++} ++ ++static void an7581_pcs_tx_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap_field **pcs_ana_fields = priv->pcs_ana_fields[index]; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 fir_cn1, fir_c0b, fir_c1; ++ u32 tx_rate_ctrl; ++ u32 ckin_divisor; ++ u32 xfi_tx_mode; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ ckin_divisor = BIT(1); ++ tx_rate_ctrl = BIT(0); ++ fir_cn1 = 0; ++ fir_c0b = 12; ++ fir_c1 = 0; ++ xfi_tx_mode = AIROHA_PCS_PMA_XFI_TX_MODE_1G25; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ ckin_divisor = BIT(2); ++ tx_rate_ctrl = BIT(0); ++ fir_cn1 = 0; ++ fir_c0b = 11; ++ fir_c1 = 1; ++ xfi_tx_mode = AIROHA_PCS_PMA_XFI_TX_MODE_3G12; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ ckin_divisor = BIT(2) | BIT(0); ++ tx_rate_ctrl = BIT(1); ++ fir_cn1 = 1; ++ fir_c0b = 1; ++ fir_c1 = 11; ++ xfi_tx_mode = AIROHA_PCS_PMA_XFI_TX_MODE_10G3; ++ break; ++ default: ++ return; ++ } ++ ++ /* Set TX rate ctrl */ ++ if (priv->data->port_type == AIROHA_PCS_PCIE) { ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_DIG_RESERVE_29, ++ AIROHA_PCS_PMA_2L_TX_RATE_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_2L_TX_RATE_CTRL, ++ tx_rate_ctrl)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_ADD_XPON_MODE_1, ++ AIROHA_PCS_PMA_XFI_TX_MODE, ++ xfi_tx_mode); ++ } else { ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_XPON_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_PON_TX_RATE_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_PON_TX_RATE_CTRL, ++ tx_rate_ctrl)); ++ } ++ ++ /* Setup TX Config */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TX_DMEDGEGEN_EN], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_TX_CKLDO_EN], 0x1); ++ ++ udelay(1); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_ACJTAG_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_SEL); ++ ++ /* FIXME: Ask Airoha TX term is OK to reset? */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_TERM_SEL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_DIVISOR | ++ AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_DIVISOR | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_TERM_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_TX_TERM_SEL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_DIVISOR | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_DIVISOR, ++ ckin_divisor) | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_TERM_SEL, 0x0)); ++ ++ if (priv->data->port_type != AIROHA_PCS_PCIE) { ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_RATE_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_RATE_CTRL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_RATE_CTRL, ++ tx_rate_ctrl)); ++ } ++ ++ /* Setup TX FIR Load Parameters (Reference 660mV) */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_FIR_C0B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_CN1 | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_CN1 | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C0B | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C0B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_CN1 | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_CN1, fir_cn1) | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C0B | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C0B, fir_c0b)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_FIR_C1, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C2 | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C2 | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C1 | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C1, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C1 | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C1, fir_c1)); ++ ++ /* Reset TX Bar */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_TX_RST_B, ++ AIROHA_PCS_PMA_TXCALIB_RST_B | AIROHA_PCS_PMA_TX_TOP_RST_B); ++} ++ ++static void an7581_pcs_rx_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap_field **pcs_ana_fields = priv->pcs_ana_fields[index]; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 phyck_div, phyck_sel; ++ u32 pr_cdr_beta_dac; ++ u32 cdr_pr_buf_in_sr; ++ bool cdr_pr_cap_en; ++ u32 sigdet_vth_sel; ++ u32 rx_rate_ctrl; ++ u32 xfi_rx_mode; ++ u32 osr; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ osr = BIT(1) | BIT(0); /* 1.25G */ ++ pr_cdr_beta_dac = BIT(3); ++ rx_rate_ctrl = 0; ++ cdr_pr_cap_en = false; ++ cdr_pr_buf_in_sr = BIT(2) | BIT(1) | BIT(0); ++ sigdet_vth_sel = BIT(2) | BIT(1); ++ phyck_div = BIT(5) | BIT(3) | BIT(0); ++ phyck_sel = BIT(0); ++ xfi_rx_mode = AIROHA_PCS_PMA_XFI_TX_MODE_1G25; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ osr = BIT(0); /* 2.5G */ ++ pr_cdr_beta_dac = BIT(2) | BIT(1); ++ rx_rate_ctrl = 0; ++ cdr_pr_cap_en = true; ++ cdr_pr_buf_in_sr = BIT(2) | BIT(1); ++ sigdet_vth_sel = BIT(2) | BIT(1); ++ phyck_div = BIT(3) | BIT(1) | BIT(0); ++ phyck_sel = BIT(0); ++ xfi_rx_mode = AIROHA_PCS_PMA_XFI_RX_MODE_3G12; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ osr = 0; /* 10G */ ++ cdr_pr_cap_en = false; ++ pr_cdr_beta_dac = BIT(3); ++ rx_rate_ctrl = BIT(1); ++ cdr_pr_buf_in_sr = BIT(2) | BIT(1) | BIT(0); ++ sigdet_vth_sel = BIT(1); ++ phyck_div = BIT(6) | BIT(1); ++ phyck_sel = BIT(1); ++ xfi_rx_mode = AIROHA_PCS_PMA_XFI_RX_MODE_10G3; ++ break; ++ default: ++ return; ++ } ++ ++ /* Set RX rate ctrl */ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_2, ++ AIROHA_PCS_PMA_CK_RATE, ++ AIROHA_PCS_PMA_CK_RATE_10); ++ ++ if (priv->data->port_type == AIROHA_PCS_PCIE) { ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_DIG_RESERVE_29, ++ AIROHA_PCS_PMA_2L_RX_RATE_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_2L_RX_RATE_CTRL, ++ rx_rate_ctrl)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_ADD_XPON_MODE_1, ++ AIROHA_PCS_PMA_XFI_RX_MODE, ++ xfi_rx_mode); ++ } else { ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_XPON_RX_RESERVED_1, ++ AIROHA_PCS_PMA_XPON_RX_RATE_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_XPON_RX_RATE_CTRL, ++ rx_rate_ctrl)); ++ } ++ ++ /* Setup RX Path */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_5, ++ AIROHA_PCS_PMA_FLL_IDAC_MIN | ++ AIROHA_PCS_PMA_FLL_IDAC_MAX, ++ FIELD_PREP(AIROHA_PCS_PMA_FLL_IDAC_MIN, 0x400) | ++ FIELD_PREP(AIROHA_PCS_PMA_FLL_IDAC_MAX, 0x3ff)); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_DAC_EYE_BYPASS_AEQ], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_DAC_E1_BYPASS_AEQ], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_DAC_E0_BYPASS_AEQ], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_DAC_D1_BYPASS_AEQ], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_DAC_D0_BYPASS_AEQ], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_FE_VCM_GEN_PWDB], 0x1); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_1, ++ AIROHA_PCS_PMA_LCPLL_MAN_PWDB); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_AEQ_OFORCE], ++ AIROHA_PCS_ANA_AEQ_OFORCE_CTLE); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_OSCAL_FORCE], ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA2VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA2IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA1VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA1IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE2VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE2IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE1VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE1IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_LVSH | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_COMPOS); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_4, ++ AIROHA_PCS_PMA_DISB_BLWC_OFFSET); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EXTRAL_CTRL, ++ AIROHA_PCS_PMA_DISB_LEQ); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PD_EDGE_DIS], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PD_PICAL_CKD8_INV], 0x0); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_AEQ_BYPASS, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_AEQ_CKON | ++ AIROHA_PCS_PMA_FORCE_DA_AEQ_CKON, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_AEQ_CKON); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_AEQ_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_INJCK_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_INJCK_SEL); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_DAC_MON], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_XFICK_EN], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_MONPI_EN], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_MONPR_EN], 0x0); ++ ++ /* Setup FE Gain and FE Peacking */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_FE_GAIN_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_GAIN_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, 0x0)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, 0x0)); ++ ++ /* Setup FE VOS */ ++ if (interface != PHY_INTERFACE_MODE_USXGMII && ++ interface != PHY_INTERFACE_MODE_10GBASER) ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_VOS, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_FE_VOS | ++ AIROHA_PCS_PMA_FORCE_DA_FE_VOS, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_FE_VOS | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_FE_VOS, 0x0)); ++ ++ /* Setup FLL PR FMeter (no bypass mode)*/ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PLL_TDC_FREQDET_0, ++ AIROHA_PCS_PMA_PLL_LOCK_CYCLECNT, ++ FIELD_PREP(AIROHA_PCS_PMA_PLL_LOCK_CYCLECNT, 0x1)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PLL_TDC_FREQDET_1, ++ AIROHA_PCS_PMA_PLL_LOCK_TARGET_END | ++ AIROHA_PCS_PMA_PLL_LOCK_TARGET_BEG, ++ FIELD_PREP(AIROHA_PCS_PMA_PLL_LOCK_TARGET_END, 0xffff) | ++ FIELD_PREP(AIROHA_PCS_PMA_PLL_LOCK_TARGET_BEG, 0x0)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PLL_TDC_FREQDET_3, ++ AIROHA_PCS_PMA_PLL_LOCK_LOCKTH, ++ FIELD_PREP(AIROHA_PCS_PMA_PLL_LOCK_LOCKTH, 0x1)); ++ ++ /* FIXME: Warn and Ask Airoha about typo in air_eth_xsgmii.c line 1391 */ ++ /* AIROHA_PCS_ANA_REV_1_FE_BUF1_BIAS_CTRL is set 0x0 in SDK but seems a typo */ ++ /* Setup REV */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_REV_1_FE_BUF1_BIAS_CTRL], ++ BIT(2)); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_REV_1_FE_BUF2_BIAS_CTRL], ++ BIT(2)); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_REV_1_SIGDET_ILEAK], 0x0); ++ ++ /* Setup Rdy Timeout */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_5, ++ AIROHA_PCS_PMA_RX_RDY | ++ AIROHA_PCS_PMA_RX_BLWC_RDY_EN, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_RDY, 0xa) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_BLWC_RDY_EN, 0x5)); ++ ++ /* Setup CaBoundry Init */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_0, ++ AIROHA_PCS_PMA_RX_OS_START | ++ AIROHA_PCS_PMA_OSC_SPEED_OPT, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_OS_START, 0x1) | ++ AIROHA_PCS_PMA_OSC_SPEED_OPT_0_1); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_6, ++ AIROHA_PCS_PMA_RX_OS_END, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_OS_END, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_1, ++ AIROHA_PCS_PMA_RX_PICAL_END | ++ AIROHA_PCS_PMA_RX_PICAL_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PICAL_END, 0x32) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PICAL_START, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_4, ++ AIROHA_PCS_PMA_RX_SDCAL_END | ++ AIROHA_PCS_PMA_RX_SDCAL_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_SDCAL_END, 0x32) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_SDCAL_START, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_2, ++ AIROHA_PCS_PMA_RX_PDOS_END | ++ AIROHA_PCS_PMA_RX_PDOS_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PDOS_END, 0x32) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PDOS_START, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_3, ++ AIROHA_PCS_PMA_RX_FEOS_END | ++ AIROHA_PCS_PMA_RX_FEOS_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_FEOS_END, 0x32) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_FEOS_START, 0x2)); ++ ++ /* Setup By Serdes*/ ++ /* Setup RX OSR */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_AEQ_SPEED, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_OSR_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_OSR_SEL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_OSR_SEL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_OSR_SEL, osr)); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PD_EDGE_DIS], !!osr); ++ ++ /* Setup CDR LPF Ratio */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_LPF_TOP_LIM], 0x20000); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_LPF_RATIO], osr); ++ ++ /* Setup CDR PR */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_KBAND_DIV], 0x4); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_BETA_SEL], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_VCOADC_OS], 0x8); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_BETA_DAC], ++ pr_cdr_beta_dac); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_FBKSEL], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_DAC_BAND], ++ pr_cdr_beta_dac); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_VREG_CKBUF_VAL], 0x6); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_VREG_IBAND_VAL], 0x6); ++ ++ /* Setup Eye Mon */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_2, ++ AIROHA_PCS_PMA_EQ_DEBUG_SEL | ++ AIROHA_PCS_PMA_FOM_NUM_ORDER | ++ AIROHA_PCS_PMA_A_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_DEBUG_SEL, 0x0) | ++ FIELD_PREP(AIROHA_PCS_PMA_FOM_NUM_ORDER, 0x1) | ++ FIELD_PREP(AIROHA_PCS_PMA_A_SEL, 0x3)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_2, ++ AIROHA_PCS_PMA_DATA_SHIFT | ++ AIROHA_PCS_PMA_EYECNT_FAST, ++ AIROHA_PCS_PMA_EYECNT_FAST); ++ ++ /* Calibration Start */ ++ ++ /* Enable SYS */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_SYS_EN_SEL_0, ++ AIROHA_PCS_PMA_RX_SYS_EN_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_SYS_EN_SEL, 0x1)); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_0, ++ AIROHA_PCS_PMA_SW_LCPLL_EN); ++ ++ usleep_range(500, 600); ++ ++ /* Setup FLL PR FMeter (bypass mode)*/ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_FBCK_LOCK); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_FBCK_LOCK); ++ ++ /* Enable CMLEQ */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_FE_EQ_HZEN], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_FE_VB_EQ3_EN], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_FE_VB_EQ2_EN], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_FE_VB_EQ1_EN], 0x1); ++ ++ /* Setup CDR PR */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_CAP_EN], ++ cdr_pr_cap_en); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_BUF_IN_SR], ++ cdr_pr_buf_in_sr); ++ ++ /* Setup CDR xxx Pwdb, set force and disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PWDB | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE_PWDB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PD_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_KBAND_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_KBAND_RSTB | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PD_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PD_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PD_PWDB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_PDOSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_PDOSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PWDB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_SCAN_RST_B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_PWDB | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SCAN_RST_B | ++ AIROHA_PCS_PMA_FORCE_DA_RX_SCAN_RST_B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_PWDB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SS_DA_XPON_PWDB_0, ++ AIROHA_PCS_PMA_XPON_CDR_PD_PWDB | ++ AIROHA_PCS_PMA_XPON_CDR_PR_PIEYE_PWDB | ++ AIROHA_PCS_PMA_XPON_CDR_PW_PWDB | ++ AIROHA_PCS_PMA_XPON_RX_FE_PWDB); ++ ++ /* FIXME: Ask Airoha WHY it's cleared? */ ++ /* regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH, ++ * AIROHA_PCS_ANA_RX_FE_50OHMS_SEL); ++ */ ++ ++ /* Setup SigDet */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_SIGDET_VTH_SEL], ++ sigdet_vth_sel); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_SIGDET_PEAK], ++ BIT(1)); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_SIGDET_LPF_CTRL], ++ BIT(1) | BIT(0)); ++ ++ /* Disable SigDet Pwdb */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SS_DA_XPON_PWDB_1, ++ AIROHA_PCS_PMA_RX_SIDGET_PWDB); ++ ++ /* Setup PHYCK */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_TDC_CK_SEL], 0x0); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_PHYCK_RSTB], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_PHYCK_SEL], ++ phyck_sel); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_PHYCK_DIV], ++ phyck_div); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_PHY_CK_SEL_FORCE], 0x1); ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_RX_PHY_CK_SEL], 0x0); ++ ++ usleep_range(100, 200); ++ ++ /* Enable CDR xxx Pwdb */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PD_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PD_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_SCAN_RST_B, ++ AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_DA_XPON_PWDB_0, ++ AIROHA_PCS_PMA_XPON_CDR_PD_PWDB | ++ AIROHA_PCS_PMA_XPON_CDR_PR_PIEYE_PWDB | ++ AIROHA_PCS_PMA_XPON_CDR_PW_PWDB | ++ AIROHA_PCS_PMA_XPON_RX_FE_PWDB); ++ ++ /* Enable SigDet Pwdb */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_DA_XPON_PWDB_1, ++ AIROHA_PCS_PMA_RX_SIDGET_PWDB); ++} ++ ++static unsigned int an7581_pcs_apply_cdr_pr_idac(struct airoha_pcs_priv *priv, ++ int index, u32 cdr_pr_idac) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 val; ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_IDAC, ++ AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC, ++ cdr_pr_idac)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_4, ++ AIROHA_PCS_PMA_FREQLOCK_DET_EN, ++ AIROHA_PCS_PMA_FREQLOCK_DET_EN_FORCE_0); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_4, ++ AIROHA_PCS_PMA_FREQLOCK_DET_EN, ++ AIROHA_PCS_PMA_FREQLOCK_DET_EN_NORMAL); ++ ++ usleep_range(5000, 7000); ++ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_RX_FREQDET, &val); ++ ++ return FIELD_GET(AIROHA_PCS_PMA_FL_OUT, val); ++} ++ ++static u32 an7581_pcs_rx_prcal_idac_major(struct airoha_pcs_priv *priv, ++ int index, u32 target_fl_out) ++{ ++ unsigned int fl_out_diff = UINT_MAX; ++ unsigned int prcal_search; ++ u32 cdr_pr_idac = 0; ++ ++ for (prcal_search = 0; prcal_search < 8 ; prcal_search++) { ++ unsigned int fl_out_diff_new; ++ unsigned int fl_out; ++ u32 cdr_pr_idac_tmp; ++ ++ /* try to find the upper value by setting the last 3 bit */ ++ cdr_pr_idac_tmp = FIELD_PREP(AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC_MAJOR, ++ prcal_search); ++ fl_out = an7581_pcs_apply_cdr_pr_idac(priv, index, cdr_pr_idac_tmp); ++ ++ /* Use absolute values to find the closest one to target */ ++ fl_out_diff_new = abs(fl_out - target_fl_out); ++ dev_dbg(priv->dev, "Tested CDR Pr Idac: %x Fl Out: %x Diff: %u\n", ++ cdr_pr_idac_tmp, fl_out, fl_out_diff_new); ++ if (fl_out_diff_new < fl_out_diff) { ++ cdr_pr_idac = cdr_pr_idac_tmp; ++ fl_out_diff = fl_out_diff_new; ++ } ++ } ++ ++ return cdr_pr_idac; ++} ++ ++static u32 an7581_pcs_rx_prcal_idac_minor(struct airoha_pcs_priv *priv, int index, ++ u32 target_fl_out, u32 cdr_pr_idac_major) ++{ ++ unsigned int remaining_prcal_search_bits = 0; ++ u32 cdr_pr_idac = cdr_pr_idac_major; ++ unsigned int fl_out, fl_out_diff; ++ int best_prcal_search_bit = -1; ++ int prcal_search_bit; ++ ++ fl_out = an7581_pcs_apply_cdr_pr_idac(priv, index, cdr_pr_idac); ++ fl_out_diff = abs(fl_out - target_fl_out); ++ ++ /* Deadline search part. ++ * We start from top bits to bottom as we progressively decrease the ++ * signal. ++ */ ++ for (prcal_search_bit = 7; prcal_search_bit >= 0; prcal_search_bit--) { ++ unsigned int fl_out_diff_new; ++ u32 cdr_pr_idac_tmp; ++ ++ cdr_pr_idac_tmp = cdr_pr_idac | BIT(prcal_search_bit); ++ fl_out = an7581_pcs_apply_cdr_pr_idac(priv, index, cdr_pr_idac_tmp); ++ ++ /* Use absolute values to find the closest one to target */ ++ fl_out_diff_new = abs(fl_out - target_fl_out); ++ dev_dbg(priv->dev, "Tested CDR Pr Idac: %x Fl Out: %x Diff: %u\n", ++ cdr_pr_idac_tmp, fl_out, fl_out_diff_new); ++ if (fl_out_diff_new < fl_out_diff) { ++ best_prcal_search_bit = prcal_search_bit; ++ fl_out_diff = fl_out_diff_new; ++ } ++ } ++ ++ /* Set the idac with the best value we found and ++ * reset the search bit to start from bottom to top. ++ */ ++ if (best_prcal_search_bit >= 0) { ++ cdr_pr_idac |= BIT(best_prcal_search_bit); ++ remaining_prcal_search_bits = best_prcal_search_bit; ++ prcal_search_bit = 0; ++ } ++ ++ /* Fine tune part. ++ * Test remaining bits to find an even closer signal level to target ++ * by increasing the signal. ++ */ ++ while (remaining_prcal_search_bits) { ++ unsigned int fl_out_diff_new; ++ u32 cdr_pr_idac_tmp; ++ ++ cdr_pr_idac_tmp = cdr_pr_idac | BIT(prcal_search_bit); ++ fl_out = an7581_pcs_apply_cdr_pr_idac(priv, index, cdr_pr_idac_tmp); ++ ++ /* Use absolute values to find the closest one to target */ ++ fl_out_diff_new = abs(fl_out - target_fl_out); ++ /* Assume we found the deadline when the new absolue signal difference ++ * from target is greater than the previous and the difference is at ++ * least 10% greater between the old and new value. ++ * This is to account for signal detection level tollerance making ++ * sure we are actually over a deadline (AKA we are getting farther ++ * from target) ++ */ ++ dev_dbg(priv->dev, "Tested CDR Pr Idac: %x Fl Out: %x Diff: %u\n", ++ cdr_pr_idac_tmp, fl_out, fl_out_diff_new); ++ if (fl_out_diff_new > fl_out_diff && ++ (abs(fl_out_diff_new - fl_out_diff) * 100) / fl_out_diff > 10) { ++ /* Exit early if we are already at the deadline */ ++ if (prcal_search_bit == 0) ++ break; ++ ++ /* We found the deadline, set the value to the previous ++ * bit, and reset the loop to fine tune with the ++ * remaining values. ++ */ ++ cdr_pr_idac |= BIT(prcal_search_bit - 1); ++ remaining_prcal_search_bits = prcal_search_bit - 1; ++ prcal_search_bit = 0; ++ } else { ++ /* Update the signal level diff and try the next bit */ ++ fl_out_diff = fl_out_diff_new; ++ ++ /* If we didn't found the deadline, set the last bit ++ * and reset the loop to fine tune with the remainig ++ * values. ++ */ ++ if (prcal_search_bit == remaining_prcal_search_bits - 1) { ++ cdr_pr_idac |= BIT(prcal_search_bit); ++ remaining_prcal_search_bits = prcal_search_bit; ++ prcal_search_bit = 0; ++ } else { ++ prcal_search_bit++; ++ } ++ } ++ } ++ ++ return cdr_pr_idac; ++} ++ ++static void an7581_pcs_rx_prcal(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap_field **pcs_ana_fields = priv->pcs_ana_fields[index]; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 cdr_pr_idac_major, cdr_pr_idac; ++ unsigned int fl_out, fl_out_diff; ++ ++ u32 target_fl_out; ++ u32 cyclecnt; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: /* DS_1.25G / US_1.25G */ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ target_fl_out = 0xa3d6; ++ cyclecnt = 32767; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: /* DS_9.95328G / US_9.95328G */ ++ target_fl_out = 0xa000; ++ cyclecnt = 20000; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: /* DS_10.3125G / US_1.25G */ ++ case PHY_INTERFACE_MODE_10GBASER: ++ target_fl_out = 0x9edf; ++ cyclecnt = 32767; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ usleep_range(100, 200); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_2, ++ AIROHA_PCS_PMA_LOCK_TARGET_END | ++ AIROHA_PCS_PMA_LOCK_TARGET_BEG, ++ FIELD_PREP(AIROHA_PCS_PMA_LOCK_TARGET_END, target_fl_out + 100) | ++ FIELD_PREP(AIROHA_PCS_PMA_LOCK_TARGET_BEG, target_fl_out - 100)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_1, ++ AIROHA_PCS_PMA_UNLOCK_CYCLECNT | ++ AIROHA_PCS_PMA_LOCK_CYCLECNT, ++ FIELD_PREP(AIROHA_PCS_PMA_UNLOCK_CYCLECNT, cyclecnt) | ++ FIELD_PREP(AIROHA_PCS_PMA_LOCK_CYCLECNT, cyclecnt)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_4, ++ AIROHA_PCS_PMA_LOCK_UNLOCKTH | ++ AIROHA_PCS_PMA_LOCK_LOCKTH, ++ FIELD_PREP(AIROHA_PCS_PMA_LOCK_UNLOCKTH, 3) | ++ FIELD_PREP(AIROHA_PCS_PMA_LOCK_LOCKTH, 3)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_3, ++ AIROHA_PCS_PMA_UNLOCK_TARGET_END | ++ AIROHA_PCS_PMA_UNLOCK_TARGET_BEG, ++ FIELD_PREP(AIROHA_PCS_PMA_UNLOCK_TARGET_END, target_fl_out + 100) | ++ FIELD_PREP(AIROHA_PCS_PMA_UNLOCK_TARGET_BEG, target_fl_out - 100)); ++ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_INJ_FORCE_OFF], 0x1); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_LPF_C_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_R_EN | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_LPF_R_EN | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_C_EN | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_LPF_C_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_R_EN | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_LPF_R_EN | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_C_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_IDAC, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_IDAC); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PWDB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB); ++ ++ /* Calibration logic: ++ * First check the major value by looping with every ++ * value in the last 3 bit of CDR_PR_IDAC. ++ * Get the signal level and save the value that is closer to ++ * the target. ++ * ++ * Then check each remaining 7 bits in search of the deadline ++ * where the signal gets farther than signal target. ++ * ++ * Finally fine tune for the remaining bits to find the one that ++ * produce the closest signal level. ++ */ ++ cdr_pr_idac_major = an7581_pcs_rx_prcal_idac_major(priv, index, target_fl_out); ++ ++ cdr_pr_idac = an7581_pcs_rx_prcal_idac_minor(priv, index, ++ target_fl_out, cdr_pr_idac_major); ++ ++ fl_out = an7581_pcs_apply_cdr_pr_idac(priv, index, cdr_pr_idac); ++ fl_out_diff = abs(fl_out - target_fl_out); ++ if (fl_out_diff > 100) { ++ u32 pr_idac_major = FIELD_GET(AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC_MAJOR, ++ cdr_pr_idac_major); ++ unsigned int fl_out_tmp, fl_out_diff_tmp; ++ u32 cdr_pr_idac_tmp; ++ ++ if (pr_idac_major > 0) { ++ cdr_pr_idac_tmp = FIELD_PREP(AIROHA_PCS_PMA_FORCE_CDR_PR_IDAC_MAJOR, ++ pr_idac_major - 1); ++ ++ dev_dbg(priv->dev, "Fl Out is %d far from target %d with Pr Idac %x. Trying with Pr Idac %x.\n", ++ fl_out_diff, target_fl_out, cdr_pr_idac_major, cdr_pr_idac_tmp); ++ ++ cdr_pr_idac_tmp = an7581_pcs_rx_prcal_idac_minor(priv, index, ++ target_fl_out, ++ cdr_pr_idac_tmp); ++ ++ fl_out_tmp = an7581_pcs_apply_cdr_pr_idac(priv, index, ++ cdr_pr_idac_tmp); ++ fl_out_diff_tmp = abs(fl_out_tmp - target_fl_out); ++ if (fl_out_diff_tmp < fl_out_diff) { ++ fl_out = fl_out_tmp; ++ fl_out_diff = fl_out_diff_tmp; ++ cdr_pr_idac = cdr_pr_idac_tmp; ++ } ++ } ++ } ++ dev_dbg(priv->dev, "Selected CDR Pr Idac: %x Fl Out: %x\n", cdr_pr_idac, fl_out); ++ if (fl_out_diff > 100) ++ dev_dbg(priv->dev, "Fl Out is %d far from target %d on intermediate calibration.\n", ++ fl_out_diff, target_fl_out); ++ ++ /* Setup Load Band */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CDR_PR_INJ_FORCE_OFF], 0x0); ++ ++ /* Disable force of LPF C previously enabled */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_LPF_C_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_LPF_C_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_IDAC, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_IDAC); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_B, ++ AIROHA_PCS_PMA_LOAD_EN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_1, ++ AIROHA_PCS_PMA_LPATH_IDAC, ++ FIELD_PREP(AIROHA_PCS_PMA_LPATH_IDAC, cdr_pr_idac)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PWDB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ usleep_range(100, 200); ++} ++ ++/* This is used to both calibrate and lock to signal (after a previous ++ * calibration) after a global reset. ++ */ ++static void an7581_pcs_cdr_reset(struct airoha_pcs_priv *priv, int index, ++ phy_interface_t interface, bool calibrate) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Setup LPF L2D force and disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA); ++ ++ /* Calibrate IDAC and setup Load Band */ ++ if (calibrate) ++ an7581_pcs_rx_prcal(priv, index, interface); ++ ++ /* Setup LPF RSTB force and disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB); ++ ++ usleep_range(700, 1000); ++ ++ /* Force Enable LPF RSTB */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB); ++ ++ usleep_range(100, 200); ++ ++ /* Force Enable LPF L2D */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA); ++ ++ /* Disable LPF RSTB force bit */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB); ++ ++ /* Disable LPF L2D force bit */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA); ++} ++ ++static int an7581_pcs_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ int calibration_try = 0; ++ u32 val; ++ ++ an7581_pcs_tx_bringup(priv, index, interface); ++ an7581_pcs_rx_bringup(priv, index, interface); ++ ++ usleep_range(100, 200); ++ ++retry_calibration: ++ an7581_pcs_cdr_reset(priv, index, interface, priv->manual_rx_calib); ++ ++ /* Global reset clear */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_HSG_RXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_RXPCS_BIST_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_RXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_TXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_TX_FIFO_RST_N | ++ AIROHA_PCS_PMA_SW_REF_RST_N | ++ AIROHA_PCS_PMA_SW_ALLPCS_RST_N | ++ AIROHA_PCS_PMA_SW_PMA_RST_N | ++ AIROHA_PCS_PMA_SW_TX_RST_N | ++ AIROHA_PCS_PMA_SW_RX_RST_N | ++ AIROHA_PCS_PMA_SW_RX_FIFO_RST_N, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ usleep_range(100, 200); ++ ++ /* Global reset */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_HSG_RXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_RXPCS_BIST_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_RXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_TXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_TX_FIFO_RST_N | ++ AIROHA_PCS_PMA_SW_REF_RST_N | ++ AIROHA_PCS_PMA_SW_ALLPCS_RST_N | ++ AIROHA_PCS_PMA_SW_PMA_RST_N | ++ AIROHA_PCS_PMA_SW_TX_RST_N | ++ AIROHA_PCS_PMA_SW_RX_RST_N | ++ AIROHA_PCS_PMA_SW_RX_FIFO_RST_N); ++ ++ usleep_range(5000, 7000); ++ ++ an7581_pcs_cdr_reset(priv, index, interface, false); ++ ++ /* Manual RX calibration is required only for SoC before E2 ++ * revision. E2+ SoC autocalibrate RX and only CDR reset is needed. ++ */ ++ if (!priv->manual_rx_calib) ++ return 0; ++ ++ /* It was discovered that after a global reset and auto mode gets ++ * actually enabled, the fl_out from calibration might change and ++ * might deviates a lot from the expected value it was calibrated for. ++ * To correctly work, the PCS FreqDet module needs to Lock to the fl_out ++ * (frequency level output) or no signal can correctly be transmitted. ++ * This is detected by checking the FreqDet module Lock bit. ++ * ++ * If it's detected that the FreqDet module is not locked, retry ++ * calibration. From observation on real hardware with a 10g SFP module, ++ * it required a maximum of an additional calibration to actually make ++ * the FreqDet module to lock. Try 10 times before failing to handle ++ * really strange case. ++ */ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_RX_FREQDET, &val); ++ if (!(val & AIROHA_PCS_PMA_FBCK_LOCK)) { ++ if (calibration_try > AIROHA_PCS_MAX_CALIBRATION_TRY) { ++ dev_err(priv->dev, "No FBCK Lock from FreqDet module after %d calibration try. PCS won't work.\n", ++ AIROHA_PCS_MAX_CALIBRATION_TRY); ++ return -EIO; ++ } ++ ++ calibration_try++; ++ ++ dev_dbg(priv->dev, "No FBCK Lock from FreqDet module, retry calibration.\n"); ++ goto retry_calibration; ++ } ++ ++ return 0; ++} ++ ++static void an7581_pcs_pll_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ an7581_pcs_jcpll_bringup(priv, index, interface); ++ ++ usleep_range(200, 300); ++ ++ an7581_pcs_txpll_bringup(priv, index, interface); ++ ++ usleep_range(200, 300); ++} ++ ++int an7581_pcs_bringup(struct airoha_pcs_priv *priv, int index, ++ phy_interface_t interface) ++{ ++ struct regmap_field **pcs_ana_fields = priv->pcs_ana_fields[index]; ++ ++ /* Enable Analog Common Lane */ ++ regmap_field_write(pcs_ana_fields[AN7581_PCS_CMN_EN], 0x1); ++ ++ /* Setup PLL */ ++ an7581_pcs_pll_bringup(priv, index, interface); ++ ++ msleep(100); ++ ++ /* Setup PHYA */ ++ return an7581_pcs_phya_bringup(priv, index, interface); ++} ++ ++int an7581_pcs_usb_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ int ret; ++ ++ ret = phy_set_mode_ext(priv->phy, PHY_MODE_ETHERNET, interface); ++ if (ret) ++ return ret; ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) { ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_8, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTR | ++ AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTD1 | ++ AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTD0, ++ FIELD_PREP(AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTR, 0xf) | ++ FIELD_PREP(AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTD1, 0xc) | ++ FIELD_PREP(AIROHA_PCS_HSGMII_ANA_SSUSB_CDR_BICLTD0, 0x3)); ++ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_6, ++ AIROHA_PCS_HSGMII_ANA_FORCE_CDR_BIC); ++ } else { ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_6, ++ AIROHA_PCS_HSGMII_ANA_FORCE_CDR_BIC); ++ } ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_26, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RST_DLY_32); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_24, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_LN0_CDR_RESERVE); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_18, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_BG_DIV, ++ FIELD_PREP(AIROHA_PCS_HSGMII_ANA_SSUSB_BG_DIV, 0x1)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_19, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE, ++ FIELD_PREP(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE, ++ FIELD_PREP(AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_HV, ++ AIROHA_PCS_HSGMII_ANA_SSUSB_XTAL_TOP_RESERVE_NS_MONPLL_CK))); ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_11, ++ AIROHA_PCS_HSGMII_ANA_TPHY_SPEED, ++ AIROHA_PCS_HSGMII_ANA_TPHY_SPEED_HSGMII); ++ else ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_HSGMII_ANA_SGMII_PHYA_11, ++ AIROHA_PCS_HSGMII_ANA_TPHY_SPEED, ++ AIROHA_PCS_HSGMII_ANA_TPHY_SPEED_SGMII); ++ ++ return 0; ++} ++ ++void an7581_pcs_phya_link_up(struct airoha_pcs_priv *priv, int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Reset TXPCS on link up */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N); ++ ++ usleep_range(100, 200); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N); ++} ++ ++static int __an7581_pcs_alloc_regmap_fields(struct airoha_pcs_priv *priv, int index, ++ const struct reg_field *fields) ++{ ++ struct device *dev = priv->dev; ++ int i; ++ ++ priv->pcs_ana_fields[index] = devm_kcalloc(dev, AN7581_PCS_FIELDS_MAX, ++ sizeof(*priv->pcs_ana_fields[index]), ++ GFP_KERNEL); ++ if (!priv->pcs_ana_fields[index]) ++ return -ENOMEM; ++ ++ for (i = 0; i < AN7581_PCS_FIELDS_MAX; i++) { ++ struct regmap_field *field; ++ ++ field = devm_regmap_field_alloc(dev, priv->pcs_ana, ++ fields[i]); ++ if (IS_ERR(field)) ++ return PTR_ERR(field); ++ ++ priv->pcs_ana_fields[index][i] = field; ++ } ++ ++ return 0; ++} ++ ++int an7581_pcs_alloc_regmap_fields(struct airoha_pcs_priv *priv) ++{ ++ return __an7581_pcs_alloc_regmap_fields(priv, 0, an7581_pcs_fields); ++} ++ ++int an7581_pcs_pcie_alloc_regmap_fields(struct airoha_pcs_priv *priv) ++{ ++ int ret; ++ ++ ret = __an7581_pcs_alloc_regmap_fields(priv, 0, an7581_pcs_pcie0_fields); ++ if (ret) ++ return ret; ++ ++ return __an7581_pcs_alloc_regmap_fields(priv, 1, an7581_pcs_pcie1_fields); ++} ++ ++static bool an7581_pcs_have_rx_signal(struct airoha_pcs_priv *priv, int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ unsigned int count = 0; ++ u32 val; ++ int i; ++ ++ regmap_write(pcs_pma, AIROHA_PCS_PMA_DIG_RESERVE_0, ++ AIROHA_PCS_TRIGGER_RX_SIDGET_SCAN); ++ ++ /* Scan 5 times for RX sigdet module to detect RX signal */ ++ for (i = 0; i <= 5; i++) { ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_DIG_RO_RESERVE_2, ++ &val); ++ if (val & AIROHA_PCS_RX_SIGDET) ++ count++; ++ } ++ ++ /* Consider signal presence if we detect signal at least 4 times */ ++ return count >= 4; ++} ++ ++int an7581_pcs_rxlock_workaround(struct airoha_pcs_priv *priv, int index) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ u32 val; ++ ++ /* Check if PCS is UP or Down */ ++ regmap_read(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_STUS_1, &val); ++ if (val & AIROHA_PCS_USXGMII_PCS_RX_LINK_STATUS_UP) ++ return 0; ++ ++ /* Validate if this is consistent with RX SigDet module */ ++ if (!an7581_pcs_have_rx_signal(priv, index)) ++ return 0; ++ ++ /* If PCS is down but RX SigDet module detected signal, ++ * trigger CDR reset. ++ */ ++ an7581_pcs_cdr_reset(priv, index, PHY_INTERFACE_MODE_NA, false); ++ ++ /* Report there is an error with Link Detection and we ++ * should test again later. ++ */ ++ return -EINVAL; ++} diff --git a/target/linux/airoha/patches-6.12/310-10-net-airoha-add-phylink-support-for-GDM2-3-4.patch b/target/linux/airoha/patches-6.12/310-10-net-airoha-add-phylink-support-for-GDM2-3-4.patch new file mode 100644 index 00000000000..7421c01c06e --- /dev/null +++ b/target/linux/airoha/patches-6.12/310-10-net-airoha-add-phylink-support-for-GDM2-3-4.patch @@ -0,0 +1,281 @@ +From ee93671d30d7741a39026c2aaaa6a7729929c347 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 17 Jan 2025 13:23:13 +0100 +Subject: [PATCH 2/2] net: airoha: add phylink support for GDM2/3/4 + +Add phylink support for GDM2/3/4 port that require configuration of the +PCS to make the external PHY or attached SFP cage work. + +These needs to be defined in the GDM port node using the pcs-handle +property. + +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/Kconfig | 1 + + drivers/net/ethernet/airoha/airoha_eth.c | 146 +++++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_eth.h | 3 + + drivers/net/ethernet/airoha/airoha_regs.h | 12 ++ + 4 files changed, 161 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/Kconfig ++++ b/drivers/net/ethernet/airoha/Kconfig +@@ -20,6 +20,7 @@ config NET_AIROHA + depends on NET_DSA || !NET_DSA + select NET_AIROHA_NPU + select PAGE_POOL ++ select PHYLINK + help + This driver supports the gigabit ethernet MACs in the + Airoha SoC family. +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -71,6 +72,11 @@ static void airoha_qdma_irq_disable(stru + airoha_qdma_set_irqmask(irq_bank, index, mask, 0); + } + ++static bool airhoa_is_phy_external(struct airoha_gdm_port *port) ++{ ++ return port->id != 1; ++} ++ + static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) + { + struct airoha_eth *eth = port->qdma->eth; +@@ -1716,6 +1722,17 @@ static int airoha_dev_open(struct net_de + struct airoha_qdma *qdma = port->qdma; + u32 pse_port = FE_PSE_PORT_PPE1; + ++ if (airhoa_is_phy_external(port)) { ++ err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0); ++ if (err) { ++ netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, ++ err); ++ return err; ++ } ++ ++ phylink_start(port->phylink); ++ } ++ + netif_tx_start_all_queues(dev); + err = airoha_set_vip_for_gdm_port(port, true); + if (err) +@@ -1777,6 +1794,11 @@ static int airoha_dev_stop(struct net_de + } + } + ++ if (airhoa_is_phy_external(port)) { ++ phylink_stop(port->phylink); ++ phylink_disconnect_phy(port->phylink); ++ } ++ + return 0; + } + +@@ -2916,6 +2938,11 @@ static const struct ethtool_ops airoha_e + .get_link = ethtool_op_get_link, + }; + ++static void airoha_mac_config(struct phylink_config *config, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++} ++ + static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port) + { + int i; +@@ -2960,6 +2987,119 @@ bool airoha_is_valid_gdm_port(struct air + return false; + } + ++static void airoha_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 airoha_gdm_port *port = container_of(config, struct airoha_gdm_port, ++ phylink_config); ++ struct airoha_qdma *qdma = port->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ u32 frag_size_tx, frag_size_rx; ++ ++ if (port->id != 4) ++ return; ++ ++ switch (speed) { ++ case SPEED_10000: ++ case SPEED_5000: ++ frag_size_tx = 8; ++ frag_size_rx = 8; ++ break; ++ case SPEED_2500: ++ frag_size_tx = 2; ++ frag_size_rx = 1; ++ break; ++ default: ++ frag_size_tx = 1; ++ frag_size_rx = 0; ++ } ++ ++ /* Configure TX/RX frag based on speed */ ++ airoha_fe_rmw(eth, REG_GDMA4_TMBI_FRAG, ++ GDMA4_SGMII0_TX_FRAG_SIZE_MASK, ++ FIELD_PREP(GDMA4_SGMII0_TX_FRAG_SIZE_MASK, ++ frag_size_tx)); ++ ++ airoha_fe_rmw(eth, REG_GDMA4_RMBI_FRAG, ++ GDMA4_SGMII0_RX_FRAG_SIZE_MASK, ++ FIELD_PREP(GDMA4_SGMII0_RX_FRAG_SIZE_MASK, ++ frag_size_rx)); ++} ++ ++static void airoha_mac_link_down(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++} ++ ++static const struct phylink_mac_ops airoha_phylink_ops = { ++ .mac_config = airoha_mac_config, ++ .mac_link_up = airoha_mac_link_up, ++ .mac_link_down = airoha_mac_link_down, ++}; ++ ++static int airoha_fill_available_pcs(struct phylink_config *config, ++ struct phylink_pcs **available_pcs, ++ unsigned int num_available_pcs) ++{ ++ struct device *dev = config->dev; ++ ++ return fwnode_phylink_pcs_parse(dev_fwnode(dev), available_pcs, ++ &num_available_pcs); ++} ++ ++static int airoha_setup_phylink(struct net_device *dev) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct device_node *np = dev->dev.of_node; ++ phy_interface_t phy_mode; ++ struct phylink *phylink; ++ int err; ++ ++ err = of_get_phy_mode(np, &phy_mode); ++ if (err) { ++ dev_err(&dev->dev, "incorrect phy-mode\n"); ++ return err; ++ } ++ ++ port->phylink_config.dev = &dev->dev; ++ port->phylink_config.type = PHYLINK_NETDEV; ++ port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | ++ MAC_5000FD | MAC_10000FD; ++ ++ err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL, ++ &port->phylink_config.num_available_pcs); ++ if (err) ++ return err; ++ ++ port->phylink_config.fill_available_pcs = airoha_fill_available_pcs; ++ ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_USXGMII, ++ port->phylink_config.supported_interfaces); ++ ++ phy_interface_copy(port->phylink_config.pcs_interfaces, ++ port->phylink_config.supported_interfaces); ++ ++ phylink = phylink_create(&port->phylink_config, ++ of_fwnode_handle(np), ++ phy_mode, &airoha_phylink_ops); ++ if (IS_ERR(phylink)) ++ return PTR_ERR(phylink); ++ ++ port->phylink = phylink; ++ ++ return err; ++} ++ + static int airoha_alloc_gdm_port(struct airoha_eth *eth, + struct device_node *np) + { +@@ -3033,6 +3173,12 @@ static int airoha_alloc_gdm_port(struct + port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + eth->ports[p] = port; + ++ if (airhoa_is_phy_external(port)) { ++ err = airoha_setup_phylink(dev); ++ if (err) ++ return err; ++ } ++ + return airoha_metadata_dst_alloc(port); + } + +@@ -3160,8 +3306,11 @@ error_napi_stop: + if (!port) + continue; + +- if (port->dev->reg_state == NETREG_REGISTERED) ++ if (port->dev->reg_state == NETREG_REGISTERED) { ++ if (airhoa_is_phy_external(port)) ++ phylink_destroy(port->phylink); + unregister_netdev(port->dev); ++ } + airoha_metadata_dst_free(port); + } + airoha_hw_cleanup(eth); +@@ -3186,6 +3335,8 @@ static void airoha_remove(struct platfor + if (!port) + continue; + ++ if (airhoa_is_phy_external(port)) ++ phylink_destroy(port->phylink); + unregister_netdev(port->dev); + airoha_metadata_dst_free(port); + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -542,6 +542,9 @@ struct airoha_gdm_port { + int id; + int nbq; + ++ struct phylink *phylink; ++ struct phylink_config phylink_config; ++ + struct airoha_hw_stats stats; + + DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -358,6 +358,18 @@ + #define IP_FRAGMENT_PORT_MASK GENMASK(8, 5) + #define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0) + ++#define REG_GDMA4_TMBI_FRAG 0x2028 ++#define GDMA4_SGMII1_TX_WEIGHT_MASK GENMASK(31, 26) ++#define GDMA4_SGMII1_TX_FRAG_SIZE_MASK GENMASK(25, 16) ++#define GDMA4_SGMII0_TX_WEIGHT_MASK GENMASK(15, 10) ++#define GDMA4_SGMII0_TX_FRAG_SIZE_MASK GENMASK(9, 0) ++ ++#define REG_GDMA4_RMBI_FRAG 0x202c ++#define GDMA4_SGMII1_RX_WEIGHT_MASK GENMASK(31, 26) ++#define GDMA4_SGMII1_RX_FRAG_SIZE_MASK GENMASK(25, 16) ++#define GDMA4_SGMII0_RX_WEIGHT_MASK GENMASK(15, 10) ++#define GDMA4_SGMII0_RX_FRAG_SIZE_MASK GENMASK(9, 0) ++ + #define REG_MC_VLAN_EN 0x2100 + #define MC_VLAN_EN_MASK BIT(0) + diff --git a/target/linux/airoha/patches-6.12/401-02-v6.16-net-dsa-mt7530-Add-AN7583-support.patch b/target/linux/airoha/patches-6.12/401-02-v6.16-net-dsa-mt7530-Add-AN7583-support.patch new file mode 100644 index 00000000000..bca3d7b3f10 --- /dev/null +++ b/target/linux/airoha/patches-6.12/401-02-v6.16-net-dsa-mt7530-Add-AN7583-support.patch @@ -0,0 +1,143 @@ +From d76556db10bf41cd3ae1ad1d705245afe077a701 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 22 May 2025 18:53:10 +0200 +Subject: [PATCH 2/3] net: dsa: mt7530: Add AN7583 support + +Add Airoha AN7583 Switch support. This is based on Airoha EN7581 that is +based on Mediatek MT7988 Switch. + +Airoha AN7583 require additional tweak to the GEPHY_CONN_CFG register to +make the internal PHY work. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250522165313.6411-3-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530-mmio.c | 1 + + drivers/net/dsa/mt7530.c | 24 ++++++++++++++++++++++-- + drivers/net/dsa/mt7530.h | 18 ++++++++++++++---- + 3 files changed, 37 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/mt7530-mmio.c ++++ b/drivers/net/dsa/mt7530-mmio.c +@@ -11,6 +11,7 @@ + #include "mt7530.h" + + static const struct of_device_id mt7988_of_match[] = { ++ { .compatible = "airoha,an7583-switch", .data = &mt753x_table[ID_AN7583], }, + { .compatible = "airoha,en7581-switch", .data = &mt753x_table[ID_EN7581], }, + { .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], }, + { /* sentinel */ }, +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1160,7 +1160,7 @@ mt753x_cpu_port_enable(struct dsa_switch + * is affine to the inbound user port. + */ + if (priv->id == ID_MT7531 || priv->id == ID_MT7988 || +- priv->id == ID_EN7581) ++ priv->id == ID_EN7581 || priv->id == ID_AN7583) + mt7530_set(priv, MT7531_CFC, MT7531_CPU_PMAP(BIT(port))); + + /* CPU port gets connected to all user ports of +@@ -2600,7 +2600,7 @@ mt7531_setup_common(struct dsa_switch *d + mt7530_set(priv, MT753X_AGC, LOCAL_EN); + + /* Enable Special Tag for rx frames */ +- if (priv->id == ID_EN7581) ++ if (priv->id == ID_EN7581 || priv->id == ID_AN7583) + mt7530_write(priv, MT753X_CPORT_SPTAG_CFG, + CPORT_SW2FE_STAG_EN | CPORT_FE2SW_STAG_EN); + +@@ -3168,6 +3168,16 @@ static int mt7988_setup(struct dsa_switc + reset_control_deassert(priv->rstc); + usleep_range(20, 50); + ++ /* AN7583 require additional tweak to CONN_CFG */ ++ if (priv->id == ID_AN7583) ++ mt7530_rmw(priv, AN7583_GEPHY_CONN_CFG, ++ AN7583_CSR_DPHY_CKIN_SEL | ++ AN7583_CSR_PHY_CORE_REG_CLK_SEL | ++ AN7583_CSR_ETHER_AFE_PWD, ++ AN7583_CSR_DPHY_CKIN_SEL | ++ AN7583_CSR_PHY_CORE_REG_CLK_SEL | ++ FIELD_PREP(AN7583_CSR_ETHER_AFE_PWD, 0)); ++ + /* Reset the switch PHYs */ + mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST); + +@@ -3264,6 +3274,16 @@ const struct mt753x_info mt753x_table[] + .pcs_ops = &mt7530_pcs_ops, + .sw_setup = mt7988_setup, + .phy_read_c22 = mt7531_ind_c22_phy_read, ++ .phy_write_c22 = mt7531_ind_c22_phy_write, ++ .phy_read_c45 = mt7531_ind_c45_phy_read, ++ .phy_write_c45 = mt7531_ind_c45_phy_write, ++ .mac_port_get_caps = en7581_mac_port_get_caps, ++ }, ++ [ID_AN7583] = { ++ .id = ID_AN7583, ++ .pcs_ops = &mt7530_pcs_ops, ++ .sw_setup = mt7988_setup, ++ .phy_read_c22 = mt7531_ind_c22_phy_read, + .phy_write_c22 = mt7531_ind_c22_phy_write, + .phy_read_c45 = mt7531_ind_c45_phy_read, + .phy_write_c45 = mt7531_ind_c45_phy_write, +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -20,6 +20,7 @@ enum mt753x_id { + ID_MT7531 = 2, + ID_MT7988 = 3, + ID_EN7581 = 4, ++ ID_AN7583 = 5, + }; + + #define NUM_TRGMII_CTRL 5 +@@ -66,7 +67,8 @@ enum mt753x_id { + + #define MT753X_MIRROR_REG(id) ((id == ID_MT7531 || \ + id == ID_MT7988 || \ +- id == ID_EN7581) ? \ ++ id == ID_EN7581 || \ ++ id == ID_AN7583) ? \ + MT7531_CFC : MT753X_MFC) + + #define MT753X_MIRROR_EN(id) ((id == ID_MT7531 || \ +@@ -76,19 +78,22 @@ enum mt753x_id { + + #define MT753X_MIRROR_PORT_MASK(id) ((id == ID_MT7531 || \ + id == ID_MT7988 || \ +- id == ID_EN7581) ? \ ++ id == ID_EN7581 || \ ++ id == ID_AN7583) ? \ + MT7531_MIRROR_PORT_MASK : \ + MT7530_MIRROR_PORT_MASK) + + #define MT753X_MIRROR_PORT_GET(id, val) ((id == ID_MT7531 || \ + id == ID_MT7988 || \ +- id == ID_EN7581) ? \ ++ id == ID_EN7581 || \ ++ id == ID_AN7583) ? \ + MT7531_MIRROR_PORT_GET(val) : \ + MT7530_MIRROR_PORT_GET(val)) + + #define MT753X_MIRROR_PORT_SET(id, val) ((id == ID_MT7531 || \ + id == ID_MT7988 || \ +- id == ID_EN7581) ? \ ++ id == ID_EN7581 || \ ++ id == ID_AN7583) ? \ + MT7531_MIRROR_PORT_SET(val) : \ + MT7530_MIRROR_PORT_SET(val)) + +@@ -619,6 +624,11 @@ enum mt7531_xtal_fsel { + #define CPORT_SW2FE_STAG_EN BIT(1) + #define CPORT_FE2SW_STAG_EN BIT(0) + ++#define AN7583_GEPHY_CONN_CFG 0x7c14 ++#define AN7583_CSR_DPHY_CKIN_SEL BIT(31) ++#define AN7583_CSR_PHY_CORE_REG_CLK_SEL BIT(30) ++#define AN7583_CSR_ETHER_AFE_PWD GENMASK(28, 24) ++ + /* Registers for LED GPIO control (MT7530 only) + * All registers follow this pattern: + * [ 2: 0] port 0 diff --git a/target/linux/airoha/patches-6.12/402-01-thermal-airoha-convert-to-regmap-API.patch b/target/linux/airoha/patches-6.12/402-01-thermal-airoha-convert-to-regmap-API.patch new file mode 100644 index 00000000000..9414bef9cbc --- /dev/null +++ b/target/linux/airoha/patches-6.12/402-01-thermal-airoha-convert-to-regmap-API.patch @@ -0,0 +1,194 @@ +From 7d55e75edc87022a4c1820588f70a80cebb13c5f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 23 May 2025 19:34:54 +0200 +Subject: [PATCH 1/5] thermal: airoha: convert to regmap API + +In preparation for support of Airoha AN7583, convert the driver to +regmap API. This is needed as Airoha AN7583 will be based on syscon +regmap. + +Signed-off-by: Christian Marangi +--- + drivers/thermal/airoha_thermal.c | 72 +++++++++++++++++++------------- + 1 file changed, 42 insertions(+), 30 deletions(-) + +--- a/drivers/thermal/airoha_thermal.c ++++ b/drivers/thermal/airoha_thermal.c +@@ -194,7 +194,7 @@ + #define AIROHA_MAX_SAMPLES 6 + + struct airoha_thermal_priv { +- void __iomem *base; ++ struct regmap *map; + struct regmap *chip_scu; + struct resource scu_adc_res; + +@@ -265,8 +265,8 @@ static int airoha_thermal_set_trips(stru + RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK))); + + /* We offset the high temp of 1°C to trigger correct event */ +- writel(TEMP_TO_RAW(priv, high) >> 4, +- priv->base + EN7581_TEMPOFFSETH); ++ regmap_write(priv->map, EN7581_TEMPOFFSETH, ++ TEMP_TO_RAW(priv, high) >> 4); + + enable_monitor = true; + } +@@ -277,15 +277,15 @@ static int airoha_thermal_set_trips(stru + RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK))); + + /* We offset the low temp of 1°C to trigger correct event */ +- writel(TEMP_TO_RAW(priv, low) >> 4, +- priv->base + EN7581_TEMPOFFSETL); ++ regmap_write(priv->map, EN7581_TEMPOFFSETL, ++ TEMP_TO_RAW(priv, high) >> 4); + + enable_monitor = true; + } + + /* Enable sensor 0 monitor after trip are set */ + if (enable_monitor) +- writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0); ++ regmap_write(priv->map, EN7581_TEMPMONCTL0, EN7581_SENSE0_EN); + + return 0; + } +@@ -302,7 +302,7 @@ static irqreturn_t airoha_thermal_irq(in + bool update = false; + u32 status; + +- status = readl(priv->base + EN7581_TEMPMONINTSTS); ++ regmap_read(priv->map, EN7581_TEMPMONINTSTS, &status); + switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) { + case EN7581_HOFSINTSTS0: + event = THERMAL_TRIP_VIOLATED; +@@ -318,7 +318,7 @@ static irqreturn_t airoha_thermal_irq(in + } + + /* Reset Interrupt */ +- writel(status, priv->base + EN7581_TEMPMONINTSTS); ++ regmap_write(priv->map, EN7581_TEMPMONINTSTS, status); + + if (update) + thermal_zone_device_update(priv->tz, event); +@@ -336,11 +336,11 @@ static void airoha_thermal_setup_adc_val + /* sleep 10 ms for ADC to enable */ + usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); + +- efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG); ++ regmap_read(priv->map, EN7581_EFUSE_TEMP_OFFSET_REG, &efuse_calib_info); + if (efuse_calib_info) { + priv->default_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info); + /* Different slope are applied if the sensor is used for CPU or for package */ +- cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG); ++ regmap_read(priv->map, EN7581_EFUSE_TEMP_CPU_SENSOR_REG, &cpu_sensor); + if (cpu_sensor) { + priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT; + priv->init_temp = EN7581_INIT_TEMP_FTK_X10; +@@ -359,8 +359,8 @@ static void airoha_thermal_setup_adc_val + static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv) + { + /* Set measure mode */ +- writel(FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4), +- priv->base + EN7581_TEMPMSRCTL0); ++ regmap_write(priv->map, EN7581_TEMPMSRCTL0, ++ FIELD_PREP(EN7581_MSRCTL0, EN7581_MSRCTL_6SAMPLE_MAX_MIX_AVG4)); + + /* + * Configure ADC valid reading addr +@@ -375,15 +375,15 @@ static void airoha_thermal_setup_monitor + * We set valid instead of volt as we don't enable valid/volt + * split reading and AHB read valid addr in such case. + */ +- writel(priv->scu_adc_res.start + EN7581_DOUT_TADC, +- priv->base + EN7581_TEMPADCVALIDADDR); ++ regmap_write(priv->map, EN7581_TEMPADCVALIDADDR, ++ priv->scu_adc_res.start + EN7581_DOUT_TADC); + + /* + * Configure valid bit on a fake value of bit 16. The ADC outputs + * max of 2 bytes for voltage. + */ +- writel(FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16), +- priv->base + EN7581_TEMPADCVALIDMASK); ++ regmap_write(priv->map, EN7581_TEMPADCVALIDMASK, ++ FIELD_PREP(EN7581_ADV_RD_VALID_POS, 16)); + + /* + * AHB supports max 12 bytes for ADC voltage. Shift the read +@@ -391,40 +391,52 @@ static void airoha_thermal_setup_monitor + * in the order of half a °C and is acceptable in the context + * of triggering interrupt in critical condition. + */ +- writel(FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4), +- priv->base + EN7581_TEMPADCVOLTAGESHIFT); ++ regmap_write(priv->map, EN7581_TEMPADCVOLTAGESHIFT, ++ FIELD_PREP(EN7581_ADC_VOLTAGE_SHIFT, 4)); + + /* BUS clock is 300MHz counting unit is 3 * 68.64 * 256 = 52.715us */ +- writel(FIELD_PREP(EN7581_PERIOD_UNIT, 3), +- priv->base + EN7581_TEMPMONCTL1); ++ regmap_write(priv->map, EN7581_TEMPMONCTL1, ++ FIELD_PREP(EN7581_PERIOD_UNIT, 3)); + + /* + * filt interval is 1 * 52.715us = 52.715us, + * sen interval is 379 * 52.715us = 19.97ms + */ +- writel(FIELD_PREP(EN7581_FILT_INTERVAL, 1) | +- FIELD_PREP(EN7581_FILT_INTERVAL, 379), +- priv->base + EN7581_TEMPMONCTL2); ++ regmap_write(priv->map, EN7581_TEMPMONCTL2, ++ FIELD_PREP(EN7581_FILT_INTERVAL, 1) | ++ FIELD_PREP(EN7581_FILT_INTERVAL, 379)); + + /* AHB poll is set to 146 * 68.64 = 10.02us */ +- writel(FIELD_PREP(EN7581_ADC_POLL_INTVL, 146), +- priv->base + EN7581_TEMPAHBPOLL); ++ regmap_write(priv->map, EN7581_TEMPAHBPOLL, ++ FIELD_PREP(EN7581_ADC_POLL_INTVL, 146)); + } + ++static const struct regmap_config airoha_thermal_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++}; ++ + static int airoha_thermal_probe(struct platform_device *pdev) + { + struct airoha_thermal_priv *priv; + struct device_node *chip_scu_np; + struct device *dev = &pdev->dev; ++ void __iomem *base; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + +- priv->base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(priv->base)) +- return PTR_ERR(priv->base); ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ priv->map = devm_regmap_init_mmio(dev, base, ++ &airoha_thermal_regmap_config); ++ if (IS_ERR(priv->map)) ++ return PTR_ERR(priv->map); + + chip_scu_np = of_parse_phandle(dev->of_node, "airoha,chip-scu", 0); + if (!chip_scu_np) +@@ -462,8 +474,8 @@ static int airoha_thermal_probe(struct p + platform_set_drvdata(pdev, priv); + + /* Enable LOW and HIGH interrupt */ +- writel(EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0, +- priv->base + EN7581_TEMPMONINT); ++ regmap_write(priv->map, EN7581_TEMPMONINT, ++ EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0); + + return 0; + } diff --git a/target/linux/airoha/patches-6.12/402-02-thermal-drivers-airoha-Generalize-probe-function.patch b/target/linux/airoha/patches-6.12/402-02-thermal-drivers-airoha-Generalize-probe-function.patch new file mode 100644 index 00000000000..b306b2308b1 --- /dev/null +++ b/target/linux/airoha/patches-6.12/402-02-thermal-drivers-airoha-Generalize-probe-function.patch @@ -0,0 +1,226 @@ +From 6c0f01b16687dc582f0470a5d5b20084fb3a290f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 23 May 2025 19:48:32 +0200 +Subject: [PATCH 2/5] thermal/drivers: airoha: Generalize probe function + +In preparation for support of Airoha AN7583, generalize the probe +function to address for the 2 SoC differece. + +Implement a match_data struct where it's possible to define a more +specific probe and post_probe function and specific thermal ops and +pllrg protect value. + +Signed-off-by: Christian Marangi +--- + drivers/thermal/airoha_thermal.c | 102 +++++++++++++++++++++++-------- + 1 file changed, 75 insertions(+), 27 deletions(-) + +--- a/drivers/thermal/airoha_thermal.c ++++ b/drivers/thermal/airoha_thermal.c +@@ -198,12 +198,23 @@ struct airoha_thermal_priv { + struct regmap *chip_scu; + struct resource scu_adc_res; + ++ u32 pllrg_protect; ++ + struct thermal_zone_device *tz; + int init_temp; + int default_slope; + int default_offset; + }; + ++struct airoha_thermal_soc_data { ++ u32 pllrg_protect; ++ ++ const struct thermal_zone_device_ops *thdev_ops; ++ int (*probe)(struct platform_device *pdev, ++ struct airoha_thermal_priv *priv); ++ int (*post_probe)(struct platform_device *pdev); ++}; ++ + static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv) + { + u32 val; +@@ -220,7 +231,8 @@ static void airoha_init_thermal_ADC_mode + regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg); + + /* Give access to thermal regs */ +- regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, EN7581_SCU_THERMAL_PROTECT_KEY); ++ regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, ++ priv->pllrg_protect); + adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1); + regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux); + +@@ -228,7 +240,7 @@ static void airoha_init_thermal_ADC_mode + regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg); + } + +-static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp) ++static int en7581_thermal_get_temp(struct thermal_zone_device *tz, int *temp) + { + struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); + int min_value, max_value, avg_value, value; +@@ -253,7 +265,7 @@ static int airoha_thermal_get_temp(struc + return 0; + } + +-static int airoha_thermal_set_trips(struct thermal_zone_device *tz, int low, ++static int en7581_thermal_set_trips(struct thermal_zone_device *tz, int low, + int high) + { + struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); +@@ -290,12 +302,12 @@ static int airoha_thermal_set_trips(stru + return 0; + } + +-static const struct thermal_zone_device_ops thdev_ops = { +- .get_temp = airoha_thermal_get_temp, +- .set_trips = airoha_thermal_set_trips, ++static const struct thermal_zone_device_ops en7581_thdev_ops = { ++ .get_temp = en7581_thermal_get_temp, ++ .set_trips = en7581_thermal_set_trips, + }; + +-static irqreturn_t airoha_thermal_irq(int irq, void *data) ++static irqreturn_t en7581_thermal_irq(int irq, void *data) + { + struct airoha_thermal_priv *priv = data; + enum thermal_notify_event event; +@@ -326,7 +338,7 @@ static irqreturn_t airoha_thermal_irq(in + return IRQ_HANDLED; + } + +-static void airoha_thermal_setup_adc_val(struct device *dev, ++static void en7581_thermal_setup_adc_val(struct device *dev, + struct airoha_thermal_priv *priv) + { + u32 efuse_calib_info, cpu_sensor; +@@ -356,7 +368,7 @@ static void airoha_thermal_setup_adc_val + } + } + +-static void airoha_thermal_setup_monitor(struct airoha_thermal_priv *priv) ++static void en7581_thermal_setup_monitor(struct airoha_thermal_priv *priv) + { + /* Set measure mode */ + regmap_write(priv->map, EN7581_TEMPMSRCTL0, +@@ -411,30 +423,26 @@ static void airoha_thermal_setup_monitor + FIELD_PREP(EN7581_ADC_POLL_INTVL, 146)); + } + +-static const struct regmap_config airoha_thermal_regmap_config = { ++static const struct regmap_config en7581_thermal_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + }; + +-static int airoha_thermal_probe(struct platform_device *pdev) ++static int en7581_thermal_probe(struct platform_device *pdev, ++ struct airoha_thermal_priv *priv) + { +- struct airoha_thermal_priv *priv; + struct device_node *chip_scu_np; + struct device *dev = &pdev->dev; + void __iomem *base; + int irq, ret; + +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->map = devm_regmap_init_mmio(dev, base, +- &airoha_thermal_regmap_config); ++ &en7581_thermal_regmap_config); + if (IS_ERR(priv->map)) + return PTR_ERR(priv->map); + +@@ -454,18 +462,55 @@ static int airoha_thermal_probe(struct p + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, +- airoha_thermal_irq, IRQF_ONESHOT, ++ en7581_thermal_irq, IRQF_ONESHOT, + pdev->name, priv); + if (ret) { + dev_err(dev, "Can't get interrupt working.\n"); + return ret; + } + +- airoha_thermal_setup_monitor(priv); +- airoha_thermal_setup_adc_val(dev, priv); ++ en7581_thermal_setup_monitor(priv); ++ en7581_thermal_setup_adc_val(dev, priv); ++ ++ return 0; ++} ++ ++static int en7581_thermal_post_probe(struct platform_device *pdev) ++{ ++ struct airoha_thermal_priv *priv = platform_get_drvdata(pdev); ++ ++ /* Enable LOW and HIGH interrupt (if supported) */ ++ regmap_write(priv->map, EN7581_TEMPMONINT, ++ EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0); ++ ++ return 0; ++} ++ ++static int airoha_thermal_probe(struct platform_device *pdev) ++{ ++ const struct airoha_thermal_soc_data *soc_data; ++ struct airoha_thermal_priv *priv; ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ soc_data = device_get_match_data(dev); ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->pllrg_protect = soc_data->pllrg_protect; ++ ++ if (!soc_data->probe) ++ return -EINVAL; ++ ++ ret = soc_data->probe(pdev, priv); ++ if (ret) ++ return ret; + + /* register of thermal sensor and get info from DT */ +- priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &thdev_ops); ++ priv->tz = devm_thermal_of_zone_register(dev, 0, priv, ++ soc_data->thdev_ops); + if (IS_ERR(priv->tz)) { + dev_err(dev, "register thermal zone sensor failed\n"); + return PTR_ERR(priv->tz); +@@ -473,15 +518,18 @@ static int airoha_thermal_probe(struct p + + platform_set_drvdata(pdev, priv); + +- /* Enable LOW and HIGH interrupt */ +- regmap_write(priv->map, EN7581_TEMPMONINT, +- EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0); +- +- return 0; ++ return soc_data->post_probe ? soc_data->post_probe(pdev) : 0; + } + ++static const struct airoha_thermal_soc_data en7581_data = { ++ .pllrg_protect = EN7581_SCU_THERMAL_PROTECT_KEY, ++ .thdev_ops = &en7581_thdev_ops, ++ .probe = &en7581_thermal_probe, ++ .post_probe = &en7581_thermal_post_probe, ++}; ++ + static const struct of_device_id airoha_thermal_match[] = { +- { .compatible = "airoha,en7581-thermal" }, ++ { .compatible = "airoha,en7581-thermal", .data = &en7581_data }, + {}, + }; + MODULE_DEVICE_TABLE(of, airoha_thermal_match); diff --git a/target/linux/airoha/patches-6.12/402-03-thermal-drivers-airoha-generalize-get_thermal_ADC-an.patch b/target/linux/airoha/patches-6.12/402-03-thermal-drivers-airoha-generalize-get_thermal_ADC-an.patch new file mode 100644 index 00000000000..5f6ae2c08a8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/402-03-thermal-drivers-airoha-generalize-get_thermal_ADC-an.patch @@ -0,0 +1,129 @@ +From 1e623852d07759c3c076505193bd7f0bd3486774 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 23 May 2025 19:54:53 +0200 +Subject: [PATCH 3/5] thermal/drivers: airoha: generalize get_thermal_ADC and + set_mux function + +In preparation for support of Airoha AN7583, generalize +get_thermal_ADC() and set_thermal_mux() with the use of reg_field API. + +This is to account the same logic between the current supported SoC and +the new one but with different register address. + +While at it also further improve some comments and move sleep inside the +set_thermal_mux function. + +Signed-off-by: Christian Marangi +--- + drivers/thermal/airoha_thermal.c | 54 +++++++++++++++++++++++++------- + 1 file changed, 42 insertions(+), 12 deletions(-) + +--- a/drivers/thermal/airoha_thermal.c ++++ b/drivers/thermal/airoha_thermal.c +@@ -193,9 +193,18 @@ + + #define AIROHA_MAX_SAMPLES 6 + ++enum airoha_thermal_chip_scu_field { ++ AIROHA_THERMAL_DOUT_TADC, ++ AIROHA_THERMAL_MUX_TADC, ++ ++ /* keep last */ ++ AIROHA_THERMAL_FIELD_MAX, ++}; ++ + struct airoha_thermal_priv { + struct regmap *map; + struct regmap *chip_scu; ++ struct regmap_field *chip_scu_fields[AIROHA_THERMAL_FIELD_MAX]; + struct resource scu_adc_res; + + u32 pllrg_protect; +@@ -219,22 +228,29 @@ static int airoha_get_thermal_ADC(struct + { + u32 val; + +- regmap_read(priv->chip_scu, EN7581_DOUT_TADC, &val); +- return FIELD_GET(EN7581_DOUT_TADC_MASK, val); ++ regmap_field_read(priv->chip_scu_fields[AIROHA_THERMAL_DOUT_TADC], ++ &val); ++ return val; + } + +-static void airoha_init_thermal_ADC_mode(struct airoha_thermal_priv *priv) ++static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv, ++ int tdac_idx) + { +- u32 adc_mux, pllrg; ++ u32 pllrg; + + /* Save PLLRG current value */ + regmap_read(priv->chip_scu, EN7581_PLLRG_PROTECT, &pllrg); + +- /* Give access to thermal regs */ ++ /* Give access to Thermal regs */ + regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, + priv->pllrg_protect); +- adc_mux = FIELD_PREP(EN7581_MUX_TADC, EN7581_SCU_THERMAL_MUX_DIODE1); +- regmap_write(priv->chip_scu, EN7581_PWD_TADC, adc_mux); ++ ++ /* Configure Thermal ADC mux to tdac_idx */ ++ regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC], ++ tdac_idx); ++ ++ /* Sleep 10 ms for Thermal ADC to enable */ ++ usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); + + /* Restore PLLRG value on exit */ + regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg); +@@ -343,10 +359,8 @@ static void en7581_thermal_setup_adc_val + { + u32 efuse_calib_info, cpu_sensor; + +- /* Setup thermal sensor to ADC mode and setup the mux to DIODE1 */ +- airoha_init_thermal_ADC_mode(priv); +- /* sleep 10 ms for ADC to enable */ +- usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); ++ /* Setup Thermal Sensor to ADC mode and setup the mux to DIODE1 */ ++ airoha_set_thermal_mux(priv, EN7581_SCU_THERMAL_MUX_DIODE1); + + regmap_read(priv->map, EN7581_EFUSE_TEMP_OFFSET_REG, &efuse_calib_info); + if (efuse_calib_info) { +@@ -429,13 +443,18 @@ static const struct regmap_config en7581 + .val_bits = 32, + }; + ++static const struct reg_field en7581_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = { ++ [AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(EN7581_DOUT_TADC, 0, 15), ++ [AIROHA_THERMAL_MUX_TADC] = REG_FIELD(EN7581_PWD_TADC, 1, 3), ++}; ++ + static int en7581_thermal_probe(struct platform_device *pdev, + struct airoha_thermal_priv *priv) + { + struct device_node *chip_scu_np; + struct device *dev = &pdev->dev; + void __iomem *base; +- int irq, ret; ++ int i, irq, ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) +@@ -454,6 +473,17 @@ static int en7581_thermal_probe(struct p + if (IS_ERR(priv->chip_scu)) + return PTR_ERR(priv->chip_scu); + ++ for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) { ++ struct regmap_field *field; ++ ++ field = devm_regmap_field_alloc(dev, priv->chip_scu, ++ en7581_chip_scu_fields[i]); ++ if (IS_ERR(field)) ++ return PTR_ERR(field); ++ ++ priv->chip_scu_fields[i] = field; ++ } ++ + of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res); + of_node_put(chip_scu_np); + diff --git a/target/linux/airoha/patches-6.12/402-05-thermal-drivers-airoha-Add-support-for-AN7583.patch b/target/linux/airoha/patches-6.12/402-05-thermal-drivers-airoha-Add-support-for-AN7583.patch new file mode 100644 index 00000000000..51332350ef7 --- /dev/null +++ b/target/linux/airoha/patches-6.12/402-05-thermal-drivers-airoha-Add-support-for-AN7583.patch @@ -0,0 +1,277 @@ +From 5891a9e5fbdf9a305b5f81e2625455efb2a886f0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 23 May 2025 19:59:20 +0200 +Subject: [PATCH 5/5] thermal/drivers: airoha: Add support for AN7583 + +Add support for Airoha AN7583 Thermal driver. This apply similar logic +on how to read the temperature but totally drop support for the +PTP_THERMAL subsystem. PTP_THERMAL subsystem was a way to trigger trip +point from hardware by configuring how to read the temperature +internally. + +This subsystem has been totally removed from Airoha AN7583 permitting +only to read the temperature. + +The SoC support up to 3 sensor but the original driver always read the +BGA sensor hence it's currently implemented reading only this specific +sensor. Reference and values for the other 2 sensor are defined for +further implementation if confirmed working. + +set_thermal_mux() is extended to also address muxing the sensor as +AN7583 use a different way to read the temperature from 3 different +diode. The EN7581 code is updated to account for these changes. + +Signed-off-by: Christian Marangi +--- + drivers/thermal/airoha_thermal.c | 158 ++++++++++++++++++++++++++++++- + 1 file changed, 154 insertions(+), 4 deletions(-) + +--- a/drivers/thermal/airoha_thermal.c ++++ b/drivers/thermal/airoha_thermal.c +@@ -18,6 +18,12 @@ + #define EN7581_DOUT_TADC 0x2f8 + #define EN7581_DOUT_TADC_MASK GENMASK(15, 0) + ++#define AN7583_MUX_SENSOR 0x2a0 ++#define AN7583_LOAD_ADJ GENMASK(3, 2) ++#define AN7583_MUX_TADC 0x2e4 ++#define AN7583_MUX_TADC_MASK GENMASK(3, 1) ++#define AN7583_DOUT_TADC 0x2f0 ++ + /* PTP_THERMAL regs */ + #define EN7581_TEMPMONCTL0 0x800 + #define EN7581_SENSE3_EN BIT(3) +@@ -181,6 +187,11 @@ + #define EN7581_SCU_THERMAL_PROTECT_KEY 0x12 + #define EN7581_SCU_THERMAL_MUX_DIODE1 0x7 + ++#define AN7583_SCU_THERMAL_PROTECT_KEY 0x80 ++#define AN7583_NUM_SENSOR 3 ++ ++#define AIROHA_THERMAL_NO_MUX_SENSOR -1 ++ + /* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */ + #define TEMP_TO_RAW(priv, temp) ((((((temp) / 100) - (priv)->init_temp) * \ + (priv)->default_slope) / 1000) + \ +@@ -193,8 +204,39 @@ + + #define AIROHA_MAX_SAMPLES 6 + ++/* ++ * AN7583 supports all these ADC mux but the original driver ++ * always checked temp with the AN7583_BGP_TEMP_SENSOR. ++ * Assume using the other sensor temperature is invalid and ++ * always read from AN7583_BGP_TEMP_SENSOR. ++ * ++ * On top of this it's defined that AN7583 supports 3 ++ * sensor: AN7583_BGP_TEMP_SENSOR, AN7583_GBE_TEMP_SENSOR, ++ * AN7583_CPU_TEMP_SENSOR. ++ * ++ * Provide the ADC mux for reference. ++ */ ++enum an7583_thermal_adc_mux { ++ AN7583_BGP_TEMP_SENSOR, ++ AN7583_PAD_AVS, ++ AN7583_CORE_POWER, ++ AN7583_AVSDAC_OUT, ++ AN7583_VCM, ++ AN7583_GBE_TEMP_SENSOR, ++ AN7583_CPU_TEMP_SENSOR, ++ ++ AN7583_ADC_MUX_MAX, ++}; ++ ++enum an7583_thermal_diode_mux { ++ AN7583_D0_TADC, ++ AN7583_ZERO_TADC, ++ AN7583_D1_TADC, ++}; ++ + enum airoha_thermal_chip_scu_field { + AIROHA_THERMAL_DOUT_TADC, ++ AIROHA_THERMAL_MUX_SENSOR, + AIROHA_THERMAL_MUX_TADC, + + /* keep last */ +@@ -208,6 +250,7 @@ struct airoha_thermal_priv { + struct resource scu_adc_res; + + u32 pllrg_protect; ++ int current_adc; + + struct thermal_zone_device *tz; + int init_temp; +@@ -224,6 +267,24 @@ struct airoha_thermal_soc_data { + int (*post_probe)(struct platform_device *pdev); + }; + ++static const unsigned int an7583_thermal_coeff[AN7583_ADC_MUX_MAX] = { ++ [AN7583_BGP_TEMP_SENSOR] = 973, ++ [AN7583_GBE_TEMP_SENSOR] = 995, ++ [AN7583_CPU_TEMP_SENSOR] = 1035, ++}; ++ ++static const unsigned int an7583_thermal_slope[AN7583_ADC_MUX_MAX] = { ++ [AN7583_BGP_TEMP_SENSOR] = 7440, ++ [AN7583_GBE_TEMP_SENSOR] = 7620, ++ [AN7583_CPU_TEMP_SENSOR] = 8390, ++}; ++ ++static const unsigned int an7583_thermal_offset[AN7583_ADC_MUX_MAX] = { ++ [AN7583_BGP_TEMP_SENSOR] = 294, ++ [AN7583_GBE_TEMP_SENSOR] = 298, ++ [AN7583_CPU_TEMP_SENSOR] = 344, ++}; ++ + static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv) + { + u32 val; +@@ -234,7 +295,7 @@ static int airoha_get_thermal_ADC(struct + } + + static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv, +- int tdac_idx) ++ int tdac_idx, int sensor_idx) + { + u32 pllrg; + +@@ -245,9 +306,20 @@ static void airoha_set_thermal_mux(struc + regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, + priv->pllrg_protect); + ++ /* ++ * Configure Thermal Sensor mux to sensor_idx. ++ * (if not supported, sensor_idx is AIROHA_THERMAL_NO_MUX_SENSOR) ++ */ ++ if (sensor_idx != AIROHA_THERMAL_NO_MUX_SENSOR) ++ regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_SENSOR], ++ sensor_idx); ++ + /* Configure Thermal ADC mux to tdac_idx */ +- regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC], +- tdac_idx); ++ if (priv->current_adc != tdac_idx) { ++ regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC], ++ tdac_idx); ++ priv->current_adc = tdac_idx; ++ } + + /* Sleep 10 ms for Thermal ADC to enable */ + usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); +@@ -360,7 +432,8 @@ static void en7581_thermal_setup_adc_val + u32 efuse_calib_info, cpu_sensor; + + /* Setup Thermal Sensor to ADC mode and setup the mux to DIODE1 */ +- airoha_set_thermal_mux(priv, EN7581_SCU_THERMAL_MUX_DIODE1); ++ airoha_set_thermal_mux(priv, EN7581_SCU_THERMAL_MUX_DIODE1, ++ AIROHA_THERMAL_NO_MUX_SENSOR); + + regmap_read(priv->map, EN7581_EFUSE_TEMP_OFFSET_REG, &efuse_calib_info); + if (efuse_calib_info) { +@@ -476,6 +549,10 @@ static int en7581_thermal_probe(struct p + for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) { + struct regmap_field *field; + ++ /* Skip registering MUX_SENSOR field as not supported */ ++ if (i == AIROHA_THERMAL_MUX_SENSOR) ++ continue; ++ + field = devm_regmap_field_alloc(dev, priv->chip_scu, + en7581_chip_scu_fields[i]); + if (IS_ERR(field)) +@@ -516,6 +593,71 @@ static int en7581_thermal_post_probe(str + return 0; + } + ++static int an7583_thermal_get_temp(struct thermal_zone_device *tz, int *temp) ++{ ++ struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); ++ int sensor_idx; ++ int delta_diode, delta_gain; ++ int coeff, slope, offset; ++ ++ int diode_zero, diode_d0, diode_d1; ++ ++ /* Always read sensor AN7583_BGP_TEMP_SENSOR */ ++ sensor_idx = AN7583_BGP_TEMP_SENSOR; ++ ++ coeff = an7583_thermal_coeff[sensor_idx]; ++ slope = an7583_thermal_slope[sensor_idx]; ++ offset = an7583_thermal_offset[sensor_idx]; ++ ++ airoha_set_thermal_mux(priv, sensor_idx, AN7583_ZERO_TADC); ++ diode_zero = airoha_get_thermal_ADC(priv); ++ airoha_set_thermal_mux(priv, sensor_idx, AN7583_D0_TADC); ++ diode_d0 = airoha_get_thermal_ADC(priv); ++ airoha_set_thermal_mux(priv, sensor_idx, AN7583_D1_TADC); ++ diode_d1 = airoha_get_thermal_ADC(priv); ++ ++ delta_diode = diode_d1 - diode_d0; ++ delta_gain = (delta_diode * coeff) / 100 + (diode_zero - diode_d1); ++ *temp = (slope * delta_diode * 10) / delta_gain - offset * 10; ++ *temp *= 100; ++ ++ return 0; ++} ++ ++static const struct thermal_zone_device_ops an7583_tz_ops = { ++ .get_temp = an7583_thermal_get_temp, ++}; ++ ++static const struct reg_field an7583_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = { ++ [AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(AN7583_DOUT_TADC, 0, 31), ++ [AIROHA_THERMAL_MUX_TADC] = REG_FIELD(AN7583_MUX_TADC, 1, 3), ++ [AIROHA_THERMAL_MUX_SENSOR] = REG_FIELD(AN7583_MUX_SENSOR, 2, 3), ++}; ++ ++static int an7583_thermal_probe(struct platform_device *pdev, ++ struct airoha_thermal_priv *priv) ++{ ++ struct device *dev = &pdev->dev; ++ int i; ++ ++ priv->chip_scu = device_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(priv->map)) ++ return PTR_ERR(priv->map); ++ ++ for (i = 0; i < AIROHA_THERMAL_FIELD_MAX; i++) { ++ struct regmap_field *field; ++ ++ field = devm_regmap_field_alloc(dev, priv->chip_scu, ++ an7583_chip_scu_fields[i]); ++ if (IS_ERR(field)) ++ return PTR_ERR(field); ++ ++ priv->chip_scu_fields[i] = field; ++ } ++ ++ return 0; ++} ++ + static int airoha_thermal_probe(struct platform_device *pdev) + { + const struct airoha_thermal_soc_data *soc_data; +@@ -530,6 +672,7 @@ static int airoha_thermal_probe(struct p + return -ENOMEM; + + priv->pllrg_protect = soc_data->pllrg_protect; ++ priv->current_adc = -1; + + if (!soc_data->probe) + return -EINVAL; +@@ -558,8 +701,15 @@ static const struct airoha_thermal_soc_d + .post_probe = &en7581_thermal_post_probe, + }; + ++static const struct airoha_thermal_soc_data an7583_data = { ++ .pllrg_protect = AN7583_SCU_THERMAL_PROTECT_KEY, ++ .thdev_ops = &an7583_tz_ops, ++ .probe = &an7583_thermal_probe, ++}; ++ + static const struct of_device_id airoha_thermal_match[] = { + { .compatible = "airoha,en7581-thermal", .data = &en7581_data }, ++ { .compatible = "airoha,an7583-thermal", .data = &an7583_data }, + {}, + }; + MODULE_DEVICE_TABLE(of, airoha_thermal_match); diff --git a/target/linux/airoha/patches-6.12/403-v6.18-cpufreq-airoha-Add-support-for-AN7583-SoC.patch b/target/linux/airoha/patches-6.12/403-v6.18-cpufreq-airoha-Add-support-for-AN7583-SoC.patch new file mode 100644 index 00000000000..1b4e2c51ade --- /dev/null +++ b/target/linux/airoha/patches-6.12/403-v6.18-cpufreq-airoha-Add-support-for-AN7583-SoC.patch @@ -0,0 +1,36 @@ +From 8640689f17fd550c3e89d2b47ecb02536c58baf3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 9 Aug 2025 13:28:30 +0200 +Subject: [PATCH] cpufreq: airoha: Add support for AN7583 SoC + +New Airoha AN7583 SoC use the same exact logic to control the CPU +frequency. Add the Device compatible to the block list for +cpufreq-dt-plat and to the Airoha CPUFreq driver compatible list. + +Signed-off-by: Christian Marangi +Signed-off-by: Viresh Kumar +--- + drivers/cpufreq/airoha-cpufreq.c | 1 + + drivers/cpufreq/cpufreq-dt-platdev.c | 1 + + 2 files changed, 2 insertions(+) + +--- a/drivers/cpufreq/airoha-cpufreq.c ++++ b/drivers/cpufreq/airoha-cpufreq.c +@@ -121,6 +121,7 @@ static struct platform_driver airoha_cpu + }; + + static const struct of_device_id airoha_cpufreq_match_list[] __initconst = { ++ { .compatible = "airoha,an7583" }, + { .compatible = "airoha,en7581" }, + {}, + }; +--- a/drivers/cpufreq/cpufreq-dt-platdev.c ++++ b/drivers/cpufreq/cpufreq-dt-platdev.c +@@ -104,6 +104,7 @@ static const struct of_device_id allowli + * platforms using "operating-points-v2" property. + */ + static const struct of_device_id blocklist[] __initconst = { ++ { .compatible = "airoha,an7583", }, + { .compatible = "airoha,en7581", }, + + { .compatible = "allwinner,sun50i-h6", }, diff --git a/target/linux/airoha/patches-6.12/600-01-clk-en7523-convert-driver-to-regmap-API.patch b/target/linux/airoha/patches-6.12/600-01-clk-en7523-convert-driver-to-regmap-API.patch new file mode 100644 index 00000000000..85bd2c214e1 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-01-clk-en7523-convert-driver-to-regmap-API.patch @@ -0,0 +1,351 @@ +From 8d5a00b3b83f76d255bcffc91d5263f72b27547a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 7 Feb 2025 23:51:23 +0100 +Subject: [PATCH 01/10] clk: en7523: convert driver to regmap API + +Convert driver to regmap API, in preparation for support of Airoha +AN7523 as the SCU will be an MFD and the regmap will be provided in the +parent node. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 137 ++++++++++++++++++++++----------------- + 1 file changed, 76 insertions(+), 61 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0-only + ++#include + #include + #include + #include +@@ -34,6 +35,7 @@ + #define REG_RESET_CONTROL_PCIE2 BIT(26) + /* EN7581 */ + #define REG_NP_SCU_PCIC 0x88 ++#define REG_PCIE_CTRL GENMASK(7, 0) + #define REG_NP_SCU_SSTR 0x9c + #define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) + #define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) +@@ -63,14 +65,14 @@ struct en_clk_desc { + }; + + struct en_clk_gate { +- void __iomem *base; ++ struct regmap *map; + struct clk_hw hw; + }; + + struct en_rst_data { + const u16 *bank_ofs; + const u16 *idx_map; +- void __iomem *base; ++ struct regmap *map; + struct reset_controller_dev rcdev; + }; + +@@ -388,44 +390,44 @@ static u32 en7523_get_div(const struct e + static int en7523_pci_is_enabled(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); ++ u32 val; + +- return !!(readl(cg->base + REG_PCI_CONTROL) & REG_PCI_CONTROL_REFCLK_EN1); ++ regmap_read(cg->map, REG_PCI_CONTROL, &val); ++ return !!(val & REG_PCI_CONTROL_REFCLK_EN1); + } + + static int en7523_pci_prepare(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +- void __iomem *np_base = cg->base; +- u32 val, mask; ++ struct regmap *map = cg->map; ++ u32 mask; + + /* Need to pull device low before reset */ +- val = readl(np_base + REG_PCI_CONTROL); +- val &= ~(REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT); +- writel(val, np_base + REG_PCI_CONTROL); ++ regmap_clear_bits(map, REG_PCI_CONTROL, ++ REG_PCI_CONTROL_PERSTOUT1 | ++ REG_PCI_CONTROL_PERSTOUT); + usleep_range(1000, 2000); + + /* Enable PCIe port 1 */ +- val |= REG_PCI_CONTROL_REFCLK_EN1; +- writel(val, np_base + REG_PCI_CONTROL); ++ regmap_set_bits(map, REG_PCI_CONTROL, ++ REG_PCI_CONTROL_REFCLK_EN1); + usleep_range(1000, 2000); + + /* Reset to default */ +- val = readl(np_base + REG_RESET_CONTROL1); + mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | + REG_RESET_CONTROL_PCIEHB; +- writel(val & ~mask, np_base + REG_RESET_CONTROL1); ++ regmap_clear_bits(map, REG_RESET_CONTROL1, mask); + usleep_range(1000, 2000); +- writel(val | mask, np_base + REG_RESET_CONTROL1); ++ regmap_set_bits(map, REG_RESET_CONTROL1, mask); + msleep(100); +- writel(val & ~mask, np_base + REG_RESET_CONTROL1); ++ regmap_clear_bits(map, REG_RESET_CONTROL1, mask); + usleep_range(5000, 10000); + + /* Release device */ + mask = REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT; +- val = readl(np_base + REG_PCI_CONTROL); +- writel(val & ~mask, np_base + REG_PCI_CONTROL); ++ regmap_clear_bits(map, REG_PCI_CONTROL, mask); + usleep_range(1000, 2000); +- writel(val | mask, np_base + REG_PCI_CONTROL); ++ regmap_set_bits(map, REG_PCI_CONTROL, mask); + msleep(250); + + return 0; +@@ -434,16 +436,13 @@ static int en7523_pci_prepare(struct clk + static void en7523_pci_unprepare(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +- void __iomem *np_base = cg->base; +- u32 val; ++ struct regmap *map = cg->map; + +- val = readl(np_base + REG_PCI_CONTROL); +- val &= ~REG_PCI_CONTROL_REFCLK_EN1; +- writel(val, np_base + REG_PCI_CONTROL); ++ regmap_clear_bits(map, REG_PCI_CONTROL, REG_PCI_CONTROL_REFCLK_EN1); + } + + static struct clk_hw *en7523_register_pcie_clk(struct device *dev, +- void __iomem *np_base) ++ struct regmap *clk_map) + { + const struct en_clk_soc_data *soc_data = device_get_match_data(dev); + struct clk_init_data init = { +@@ -456,7 +455,7 @@ static struct clk_hw *en7523_register_pc + if (!cg) + return NULL; + +- cg->base = np_base; ++ cg->map = clk_map; + cg->hw.init = &init; + + if (init.ops->unprepare) +@@ -474,21 +473,20 @@ static int en7581_pci_is_enabled(struct + u32 val, mask; + + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; +- val = readl(cg->base + REG_PCI_CONTROL); ++ regmap_read(cg->map, REG_PCI_CONTROL, &val); + return (val & mask) == mask; + } + + static int en7581_pci_enable(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +- void __iomem *np_base = cg->base; +- u32 val, mask; ++ struct regmap *map = cg->map; ++ u32 mask; + + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | + REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | + REG_PCI_CONTROL_PERSTOUT; +- val = readl(np_base + REG_PCI_CONTROL); +- writel(val | mask, np_base + REG_PCI_CONTROL); ++ regmap_set_bits(map, REG_PCI_CONTROL, mask); + + return 0; + } +@@ -496,19 +494,18 @@ static int en7581_pci_enable(struct clk_ + static void en7581_pci_disable(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +- void __iomem *np_base = cg->base; +- u32 val, mask; ++ struct regmap *map = cg->map; ++ u32 mask; + + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | + REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | + REG_PCI_CONTROL_PERSTOUT; +- val = readl(np_base + REG_PCI_CONTROL); +- writel(val & ~mask, np_base + REG_PCI_CONTROL); ++ regmap_clear_bits(map, REG_PCI_CONTROL, mask); + usleep_range(1000, 2000); + } + + static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, +- void __iomem *base, void __iomem *np_base) ++ struct regmap *map, struct regmap *clk_map) + { + struct clk_hw *hw; + u32 rate; +@@ -517,10 +514,12 @@ static void en7523_register_clocks(struc + for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { + const struct en_clk_desc *desc = &en7523_base_clks[i]; + u32 reg = desc->div_reg ? desc->div_reg : desc->base_reg; +- u32 val = readl(base + desc->base_reg); ++ u32 val; ++ ++ regmap_read(map, desc->base_reg, &val); + + rate = en7523_get_base_rate(desc, val); +- val = readl(base + reg); ++ regmap_read(map, reg, &val); + rate /= en7523_get_div(desc, val); + + hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); +@@ -533,30 +532,47 @@ static void en7523_register_clocks(struc + clk_data->hws[desc->id] = hw; + } + +- hw = en7523_register_pcie_clk(dev, np_base); ++ hw = en7523_register_pcie_clk(dev, clk_map); + clk_data->hws[EN7523_CLK_PCIE] = hw; + } + ++static const struct regmap_config en7523_clk_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++}; ++ + static int en7523_clk_hw_init(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data) + { + void __iomem *base, *np_base; ++ struct regmap *map, *clk_map; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + ++ map = devm_regmap_init_mmio(&pdev->dev, base, ++ &en7523_clk_regmap_config); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); ++ + np_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(np_base)) + return PTR_ERR(np_base); + +- en7523_register_clocks(&pdev->dev, clk_data, base, np_base); ++ clk_map = devm_regmap_init_mmio(&pdev->dev, np_base, ++ &en7523_clk_regmap_config); ++ if (IS_ERR(clk_map)) ++ return PTR_ERR(clk_map); ++ ++ en7523_register_clocks(&pdev->dev, clk_data, map, clk_map); + + return 0; + } + + static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, +- struct regmap *map, void __iomem *base) ++ struct regmap *map, struct regmap *clk_map) + { + struct clk_hw *hw; + u32 rate; +@@ -593,7 +609,7 @@ static void en7581_register_clocks(struc + clk_data->hws[desc->id] = hw; + } + +- hw = en7523_register_pcie_clk(dev, base); ++ hw = en7523_register_pcie_clk(dev, clk_map); + clk_data->hws[EN7523_CLK_PCIE] = hw; + } + +@@ -601,15 +617,10 @@ static int en7523_reset_update(struct re + unsigned long id, bool assert) + { + struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); +- void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; +- u32 val; ++ u32 addr = rst_data->bank_ofs[id / RST_NR_PER_BANK]; + +- val = readl(addr); +- if (assert) +- val |= BIT(id % RST_NR_PER_BANK); +- else +- val &= ~BIT(id % RST_NR_PER_BANK); +- writel(val, addr); ++ regmap_update_bits(rst_data->map, addr, BIT(id % RST_NR_PER_BANK), ++ assert ? BIT(id % RST_NR_PER_BANK) : 0); + + return 0; + } +@@ -630,9 +641,11 @@ static int en7523_reset_status(struct re + unsigned long id) + { + struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); +- void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; ++ u32 addr = rst_data->bank_ofs[id / RST_NR_PER_BANK]; ++ u32 val; + +- return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); ++ regmap_read(rst_data->map, addr, &val); ++ return !!(val & BIT(id % RST_NR_PER_BANK)); + } + + static int en7523_reset_xlate(struct reset_controller_dev *rcdev, +@@ -652,7 +665,7 @@ static const struct reset_control_ops en + .status = en7523_reset_status, + }; + +-static int en7581_reset_register(struct device *dev, void __iomem *base) ++static int en7581_reset_register(struct device *dev, struct regmap *map) + { + struct en_rst_data *rst_data; + +@@ -662,7 +675,7 @@ static int en7581_reset_register(struct + + rst_data->bank_ofs = en7581_rst_ofs; + rst_data->idx_map = en7581_rst_map; +- rst_data->base = base; ++ rst_data->map = map; + + rst_data->rcdev.nr_resets = ARRAY_SIZE(en7581_rst_map); + rst_data->rcdev.of_xlate = en7523_reset_xlate; +@@ -678,9 +691,8 @@ static int en7581_reset_register(struct + static int en7581_clk_hw_init(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data) + { +- struct regmap *map; ++ struct regmap *map, *clk_map; + void __iomem *base; +- u32 val; + + map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) +@@ -690,15 +702,18 @@ static int en7581_clk_hw_init(struct pla + if (IS_ERR(base)) + return PTR_ERR(base); + +- en7581_register_clocks(&pdev->dev, clk_data, map, base); +- +- val = readl(base + REG_NP_SCU_SSTR); +- val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +- writel(val, base + REG_NP_SCU_SSTR); +- val = readl(base + REG_NP_SCU_PCIC); +- writel(val | 3, base + REG_NP_SCU_PCIC); ++ clk_map = devm_regmap_init_mmio(&pdev->dev, base, &en7523_clk_regmap_config); ++ if (IS_ERR(clk_map)) ++ return PTR_ERR(clk_map); ++ ++ en7581_register_clocks(&pdev->dev, clk_data, map, clk_map); ++ ++ regmap_clear_bits(clk_map, REG_NP_SCU_SSTR, ++ REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); ++ regmap_update_bits(clk_map, REG_NP_SCU_PCIC, REG_PCIE_CTRL, ++ FIELD_PREP(REG_PCIE_CTRL, 3)); + +- return en7581_reset_register(&pdev->dev, base); ++ return en7581_reset_register(&pdev->dev, clk_map); + } + + static int en7523_clk_probe(struct platform_device *pdev) diff --git a/target/linux/airoha/patches-6.12/600-02-clk-en7523-generalize-register-clocks-function.patch b/target/linux/airoha/patches-6.12/600-02-clk-en7523-generalize-register-clocks-function.patch new file mode 100644 index 00000000000..b8d892ffb49 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-02-clk-en7523-generalize-register-clocks-function.patch @@ -0,0 +1,258 @@ +From 36a3a919391dea2000f355125f0a161c453fcf78 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 8 Feb 2025 00:08:08 +0100 +Subject: [PATCH 02/10] clk: en7523: generalize register clocks function + +Generalize register clocks function for Airoha EN7523 and EN7581 clocks +driver. The same logic is applied for both clock hence code can be +reduced and simplified by putting the base_clocks struct in the soc_data +and passing that to a generic register clocks function. + +While at it rework some function to return error and use devm variant +for clk_hw_regiser. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 148 +++++++++++++++++---------------------- + 1 file changed, 66 insertions(+), 82 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -78,8 +78,10 @@ struct en_rst_data { + + struct en_clk_soc_data { + u32 num_clocks; ++ const struct en_clk_desc *base_clks; + const struct clk_ops pcie_ops; + int (*hw_init)(struct platform_device *pdev, ++ const struct en_clk_soc_data *soc_data, + struct clk_hw_onecell_data *clk_data); + }; + +@@ -450,10 +452,11 @@ static struct clk_hw *en7523_register_pc + .ops = &soc_data->pcie_ops, + }; + struct en_clk_gate *cg; ++ int err; + + cg = devm_kzalloc(dev, sizeof(*cg), GFP_KERNEL); + if (!cg) +- return NULL; ++ return ERR_PTR(-ENOMEM); + + cg->map = clk_map; + cg->hw.init = &init; +@@ -461,12 +464,62 @@ static struct clk_hw *en7523_register_pc + if (init.ops->unprepare) + init.ops->unprepare(&cg->hw); + +- if (clk_hw_register(dev, &cg->hw)) +- return NULL; ++ err = devm_clk_hw_register(dev, &cg->hw); ++ if (err) ++ return ERR_PTR(err); + + return &cg->hw; + } + ++static int en75xx_register_clocks(struct device *dev, ++ const struct en_clk_soc_data *soc_data, ++ struct clk_hw_onecell_data *clk_data, ++ struct regmap *map, struct regmap *clk_map) ++{ ++ struct clk_hw *hw; ++ u32 rate; ++ int i; ++ ++ for (i = 0; i < soc_data->num_clocks - 1; i++) { ++ const struct en_clk_desc *desc = &soc_data->base_clks[i]; ++ u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; ++ int err; ++ ++ err = regmap_read(map, desc->base_reg, &val); ++ if (err) { ++ pr_err("Failed reading fixed clk rate %s: %d\n", ++ desc->name, err); ++ return err; ++ } ++ rate = en7523_get_base_rate(desc, val); ++ ++ err = regmap_read(map, reg, &val); ++ if (err) { ++ pr_err("Failed reading fixed clk div %s: %d\n", ++ desc->name, err); ++ return err; ++ } ++ rate /= en7523_get_div(desc, val); ++ ++ hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); ++ if (IS_ERR(hw)) { ++ pr_err("Failed to register clk %s: %ld\n", ++ desc->name, PTR_ERR(hw)); ++ return PTR_ERR(hw); ++ } ++ ++ clk_data->hws[desc->id] = hw; ++ } ++ ++ hw = en7523_register_pcie_clk(dev, clk_map); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ ++ clk_data->hws[EN7523_CLK_PCIE] = hw; ++ ++ return 0; ++} ++ + static int en7581_pci_is_enabled(struct clk_hw *hw) + { + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); +@@ -504,38 +557,6 @@ static void en7581_pci_disable(struct cl + usleep_range(1000, 2000); + } + +-static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, +- struct regmap *map, struct regmap *clk_map) +-{ +- struct clk_hw *hw; +- u32 rate; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { +- const struct en_clk_desc *desc = &en7523_base_clks[i]; +- u32 reg = desc->div_reg ? desc->div_reg : desc->base_reg; +- u32 val; +- +- regmap_read(map, desc->base_reg, &val); +- +- rate = en7523_get_base_rate(desc, val); +- regmap_read(map, reg, &val); +- rate /= en7523_get_div(desc, val); +- +- hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); +- if (IS_ERR(hw)) { +- pr_err("Failed to register clk %s: %ld\n", +- desc->name, PTR_ERR(hw)); +- continue; +- } +- +- clk_data->hws[desc->id] = hw; +- } +- +- hw = en7523_register_pcie_clk(dev, clk_map); +- clk_data->hws[EN7523_CLK_PCIE] = hw; +-} +- + static const struct regmap_config en7523_clk_regmap_config = { + .reg_bits = 32, + .val_bits = 32, +@@ -543,6 +564,7 @@ static const struct regmap_config en7523 + }; + + static int en7523_clk_hw_init(struct platform_device *pdev, ++ const struct en_clk_soc_data *soc_data, + struct clk_hw_onecell_data *clk_data) + { + void __iomem *base, *np_base; +@@ -566,51 +588,7 @@ static int en7523_clk_hw_init(struct pla + if (IS_ERR(clk_map)) + return PTR_ERR(clk_map); + +- en7523_register_clocks(&pdev->dev, clk_data, map, clk_map); +- +- return 0; +-} +- +-static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, +- struct regmap *map, struct regmap *clk_map) +-{ +- struct clk_hw *hw; +- u32 rate; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(en7581_base_clks); i++) { +- const struct en_clk_desc *desc = &en7581_base_clks[i]; +- u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; +- int err; +- +- err = regmap_read(map, desc->base_reg, &val); +- if (err) { +- pr_err("Failed reading fixed clk rate %s: %d\n", +- desc->name, err); +- continue; +- } +- rate = en7523_get_base_rate(desc, val); +- +- err = regmap_read(map, reg, &val); +- if (err) { +- pr_err("Failed reading fixed clk div %s: %d\n", +- desc->name, err); +- continue; +- } +- rate /= en7523_get_div(desc, val); +- +- hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); +- if (IS_ERR(hw)) { +- pr_err("Failed to register clk %s: %ld\n", +- desc->name, PTR_ERR(hw)); +- continue; +- } +- +- clk_data->hws[desc->id] = hw; +- } +- +- hw = en7523_register_pcie_clk(dev, clk_map); +- clk_data->hws[EN7523_CLK_PCIE] = hw; ++ return en75xx_register_clocks(&pdev->dev, soc_data, clk_data, map, clk_map); + } + + static int en7523_reset_update(struct reset_controller_dev *rcdev, +@@ -689,10 +667,12 @@ static int en7581_reset_register(struct + } + + static int en7581_clk_hw_init(struct platform_device *pdev, ++ const struct en_clk_soc_data *soc_data, + struct clk_hw_onecell_data *clk_data) + { + struct regmap *map, *clk_map; + void __iomem *base; ++ int ret; + + map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) +@@ -706,7 +686,9 @@ static int en7581_clk_hw_init(struct pla + if (IS_ERR(clk_map)) + return PTR_ERR(clk_map); + +- en7581_register_clocks(&pdev->dev, clk_data, map, clk_map); ++ ret = en75xx_register_clocks(&pdev->dev, soc_data, clk_data, map, clk_map); ++ if (ret) ++ return ret; + + regmap_clear_bits(clk_map, REG_NP_SCU_SSTR, + REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); +@@ -732,7 +714,7 @@ static int en7523_clk_probe(struct platf + return -ENOMEM; + + clk_data->num = soc_data->num_clocks; +- r = soc_data->hw_init(pdev, clk_data); ++ r = soc_data->hw_init(pdev, soc_data, clk_data); + if (r) + return r; + +@@ -740,6 +722,7 @@ static int en7523_clk_probe(struct platf + } + + static const struct en_clk_soc_data en7523_data = { ++ .base_clks = en7523_base_clks, + .num_clocks = ARRAY_SIZE(en7523_base_clks) + 1, + .pcie_ops = { + .is_enabled = en7523_pci_is_enabled, +@@ -750,6 +733,7 @@ static const struct en_clk_soc_data en75 + }; + + static const struct en_clk_soc_data en7581_data = { ++ .base_clks = en7581_base_clks, + /* We increment num_clocks by 1 to account for additional PCIe clock */ + .num_clocks = ARRAY_SIZE(en7581_base_clks) + 1, + .pcie_ops = { diff --git a/target/linux/airoha/patches-6.12/600-03-clk-en7523-convert-to-full-clk_hw-implementation.patch b/target/linux/airoha/patches-6.12/600-03-clk-en7523-convert-to-full-clk_hw-implementation.patch new file mode 100644 index 00000000000..31b5bce6369 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-03-clk-en7523-convert-to-full-clk_hw-implementation.patch @@ -0,0 +1,147 @@ +From 933030fd268ac111eb9db13b5a90b7c66cd9df41 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 17 Jun 2025 11:38:21 +0200 +Subject: [PATCH 03/10] clk: en7523: convert to full clk_hw implementation + +In preparation for support of .set_rate, convert the clock register +logic from fixed clock implementation to full clk_hw implementation with +dedicated OPs. + +This is just a rework and no behaviour change is expected. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 83 ++++++++++++++++++++++++++++------------ + 1 file changed, 59 insertions(+), 24 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -69,6 +69,12 @@ struct en_clk_gate { + struct clk_hw hw; + }; + ++struct en_clk { ++ struct regmap *map; ++ const struct en_clk_desc *desc; ++ struct clk_hw hw; ++}; ++ + struct en_rst_data { + const u16 *bank_ofs; + const u16 *idx_map; +@@ -471,44 +477,73 @@ static struct clk_hw *en7523_register_pc + return &cg->hw; + } + ++static unsigned long en75xx_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct en_clk *c = container_of(hw, struct en_clk, hw); ++ const struct en_clk_desc *desc = c->desc; ++ struct regmap *map = c->map; ++ u32 val, reg; ++ u32 rate; ++ int err; ++ ++ err = regmap_read(map, desc->base_reg, &val); ++ if (err) { ++ pr_err("Failed reading fixed clk rate %s: %d\n", ++ desc->name, err); ++ return err; ++ } ++ rate = en7523_get_base_rate(desc, val); ++ ++ reg = desc->div_reg ? desc->div_reg : desc->base_reg; ++ err = regmap_read(map, reg, &val); ++ if (err) { ++ pr_err("Failed reading fixed clk div %s: %d\n", ++ desc->name, err); ++ return err; ++ } ++ ++ return rate / en7523_get_div(desc, val); ++} ++ ++static const struct clk_ops en75xx_clk_ops = { ++ .recalc_rate = en75xx_recalc_rate, ++}; ++ + static int en75xx_register_clocks(struct device *dev, + const struct en_clk_soc_data *soc_data, + struct clk_hw_onecell_data *clk_data, + struct regmap *map, struct regmap *clk_map) + { + struct clk_hw *hw; +- u32 rate; + int i; + + for (i = 0; i < soc_data->num_clocks - 1; i++) { + const struct en_clk_desc *desc = &soc_data->base_clks[i]; +- u32 val, reg = desc->div_reg ? desc->div_reg : desc->base_reg; ++ struct clk_init_data init = { ++ .ops = &en75xx_clk_ops, ++ }; ++ struct en_clk *en_clk; + int err; + +- err = regmap_read(map, desc->base_reg, &val); +- if (err) { +- pr_err("Failed reading fixed clk rate %s: %d\n", +- desc->name, err); +- return err; +- } +- rate = en7523_get_base_rate(desc, val); ++ en_clk = devm_kzalloc(dev, sizeof(*en_clk), GFP_KERNEL); ++ if (!en_clk) ++ return -ENOMEM; + +- err = regmap_read(map, reg, &val); ++ init.name = desc->name; ++ ++ en_clk->map = map; ++ en_clk->desc = desc; ++ en_clk->hw.init = &init; ++ ++ err = devm_clk_hw_register(dev, &en_clk->hw); + if (err) { +- pr_err("Failed reading fixed clk div %s: %d\n", ++ pr_err("Failed to register clk %s: %d\n", + desc->name, err); + return err; + } +- rate /= en7523_get_div(desc, val); +- +- hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); +- if (IS_ERR(hw)) { +- pr_err("Failed to register clk %s: %ld\n", +- desc->name, PTR_ERR(hw)); +- return PTR_ERR(hw); +- } + +- clk_data->hws[desc->id] = hw; ++ clk_data->hws[desc->id] = &en_clk->hw; + } + + hw = en7523_register_pcie_clk(dev, clk_map); +@@ -672,7 +707,7 @@ static int en7581_clk_hw_init(struct pla + { + struct regmap *map, *clk_map; + void __iomem *base; +- int ret; ++ int err; + + map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) +@@ -686,9 +721,9 @@ static int en7581_clk_hw_init(struct pla + if (IS_ERR(clk_map)) + return PTR_ERR(clk_map); + +- ret = en75xx_register_clocks(&pdev->dev, soc_data, clk_data, map, clk_map); +- if (ret) +- return ret; ++ err = en75xx_register_clocks(&pdev->dev, soc_data, clk_data, map, clk_map); ++ if (err) ++ return err; + + regmap_clear_bits(clk_map, REG_NP_SCU_SSTR, + REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); diff --git a/target/linux/airoha/patches-6.12/600-04-clk-en7523-add-support-for-.set_rate.patch b/target/linux/airoha/patches-6.12/600-04-clk-en7523-add-support-for-.set_rate.patch new file mode 100644 index 00000000000..fc2656aeb72 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-04-clk-en7523-add-support-for-.set_rate.patch @@ -0,0 +1,173 @@ +From fe71e8f734a5c9b808a68b8abaa0156de605df4f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 17 Jun 2025 12:28:41 +0200 +Subject: [PATCH 04/10] clk: en7523: add support for .set_rate + +Add support for EN7523 driver to configure rate. The SoC expose both +base clock selector and clock divisor hence it's possible to change the +rate. + +This will be especially needed for new SoC AN7583 that require changes +for the MDIO and the eMMC. + +The clock were assumed correctly configured by the bootloader but this +goes against the rule of "kernel should not depend on external +configuration". + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 141 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 141 insertions(+) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -506,8 +506,149 @@ static unsigned long en75xx_recalc_rate( + return rate / en7523_get_div(desc, val); + } + ++static int en75xx_get_base_val_for_rate(const struct en_clk_desc *desc, ++ int div, unsigned long rate) ++{ ++ int i; ++ ++ /* Single base rate */ ++ if (!desc->base_bits) { ++ if (rate != desc->base_value / div) ++ goto err; ++ ++ return 0; ++ } ++ ++ /* Check every base rate with provided divisor */ ++ for (i = 0; i < desc->n_base_values; i++) ++ if (rate == desc->base_values[i] / div) ++ return i; ++ ++err: ++ return -EINVAL; ++} ++ ++static int en75xx_get_vals_for_rate(const struct en_clk_desc *desc, ++ unsigned long rate, ++ u32 *base_val, u32 *div_val) ++{ ++ int tmp_base_val = 0; ++ int tmp_div_val = 0; ++ ++ if (!desc->base_bits && !desc->div_bits) ++ return -EINVAL; ++ ++ /* Divisor not supported, just search in base rate */ ++ if (!desc->div_bits) { ++ tmp_base_val = en75xx_get_base_val_for_rate(desc, 1, rate); ++ if (tmp_base_val < 0) { ++ pr_err("Invalid rate for clock %s\n", ++ desc->name); ++ return -EINVAL; ++ } ++ ++ goto exit; ++ } ++ ++ /* Check if div0 satisfy the request */ ++ if (desc->div_val0) { ++ tmp_base_val = en75xx_get_base_val_for_rate(desc, ++ desc->div_val0, ++ rate); ++ if (tmp_base_val >= 0) ++ goto exit; ++ ++ /* Skip checking first divisor val */ ++ tmp_div_val = 1; ++ } ++ ++ /* Simulate rate with every divisor supported */ ++ for (; tmp_div_val < BIT(desc->div_bits); tmp_div_val++) { ++ int div = (tmp_div_val + desc->div_offset) * desc->div_step; ++ ++ tmp_base_val = en75xx_get_base_val_for_rate(desc, div, ++ rate); ++ if (tmp_base_val >= 0) ++ goto exit; ++ } ++ ++ if (tmp_div_val == BIT(desc->div_bits)) { ++ pr_err("Invalid rate for clock %s\n", ++ desc->name); ++ return -EINVAL; ++ } ++ ++exit: ++ *base_val = tmp_base_val; ++ *div_val = tmp_div_val; ++ ++ return 0; ++} ++ ++static long en75xx_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct en_clk *en_clk = container_of(hw, struct en_clk, hw); ++ u32 div_val, base_val; ++ int err; ++ ++ /* Just check if the rate is possible */ ++ err = en75xx_get_vals_for_rate(en_clk->desc, rate, ++ &base_val, &div_val); ++ if (err) ++ return err; ++ ++ return rate; ++} ++ ++static int en75xx_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct en_clk *en_clk = container_of(hw, struct en_clk, hw); ++ const struct en_clk_desc *desc = en_clk->desc; ++ struct regmap *map = en_clk->map; ++ u32 base_val, div_val; ++ u32 reg, val, mask; ++ int err; ++ ++ err = en75xx_get_vals_for_rate(en_clk->desc, rate, ++ &base_val, &div_val); ++ if (err) ++ return err; ++ ++ if (desc->div_bits) { ++ reg = desc->div_reg ? desc->div_reg : desc->base_reg; ++ ++ mask = (BIT(desc->div_bits) - 1) << desc->div_shift; ++ val = div_val << desc->div_shift; ++ ++ err = regmap_update_bits(map, reg, mask, val); ++ if (err) { ++ pr_err("Failed to update div reg for clock %s\n", ++ desc->name); ++ return -EINVAL; ++ } ++ } ++ ++ if (desc->base_bits) { ++ mask = (BIT(desc->base_bits) - 1) << desc->base_shift; ++ val = base_val << desc->base_shift; ++ ++ err = regmap_update_bits(map, desc->base_reg, mask, val); ++ if (err) { ++ pr_err("Failed to update reg for clock %s\n", ++ desc->name); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ + static const struct clk_ops en75xx_clk_ops = { + .recalc_rate = en75xx_recalc_rate, ++ .round_rate = en75xx_round_rate, ++ .set_rate = en75xx_set_rate, + }; + + static int en75xx_register_clocks(struct device *dev, diff --git a/target/linux/airoha/patches-6.12/600-05-clk-en7523-permit-to-reference-Chip-SCU-from-phandle.patch b/target/linux/airoha/patches-6.12/600-05-clk-en7523-permit-to-reference-Chip-SCU-from-phandle.patch new file mode 100644 index 00000000000..059d8083cfa --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-05-clk-en7523-permit-to-reference-Chip-SCU-from-phandle.patch @@ -0,0 +1,37 @@ +From 397a132fb8173a9d728bc7c7a31ff5c0590d076f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 17 Jun 2025 12:48:35 +0200 +Subject: [PATCH 05/10] clk: en7523: permit to reference Chip SCU from phandle + +In preparation for support of AN7583 and to make Chip SCU reference more +robust, permit to reference the Chip SCU syscon regmap also with the +"airoha,chip-scu" property in DT. + +Legacy implementation is kept by fallbacking in the absence of +"airoha,chip-scu" property. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -846,11 +846,16 @@ static int en7581_clk_hw_init(struct pla + const struct en_clk_soc_data *soc_data, + struct clk_hw_onecell_data *clk_data) + { ++ struct device *dev = &pdev->dev; + struct regmap *map, *clk_map; + void __iomem *base; + int err; + +- map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); ++ if (of_property_present(dev->of_node, "airoha,chip-scu")) ++ map = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "airoha,chip-scu"); ++ else ++ map = syscon_regmap_lookup_by_compatible("airoha,en7581-chip-scu"); + if (IS_ERR(map)) + return PTR_ERR(map); + diff --git a/target/linux/airoha/patches-6.12/600-07-clk-en7523-reword-and-clean-clk_probe-variables.patch b/target/linux/airoha/patches-6.12/600-07-clk-en7523-reword-and-clean-clk_probe-variables.patch new file mode 100644 index 00000000000..5de1768f1d0 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-07-clk-en7523-reword-and-clean-clk_probe-variables.patch @@ -0,0 +1,53 @@ +From d05fc5c8a9ab7bbda80e4fc728902f8d48d3e8aa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 17 Jun 2025 14:53:56 +0200 +Subject: [PATCH 07/10] clk: en7523: reword and clean clk_probe variables + +Rework and clean en7523_clk_probe variables to make them consistent with +the rest of the source. Also apply some minor cleanup for pdev +variables. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -881,25 +881,27 @@ static int en7581_clk_hw_init(struct pla + + static int en7523_clk_probe(struct platform_device *pdev) + { +- struct device_node *node = pdev->dev.of_node; + const struct en_clk_soc_data *soc_data; + struct clk_hw_onecell_data *clk_data; +- int r; ++ struct device *dev = &pdev->dev; ++ int err; + +- soc_data = device_get_match_data(&pdev->dev); ++ soc_data = device_get_match_data(dev); + +- clk_data = devm_kzalloc(&pdev->dev, +- struct_size(clk_data, hws, soc_data->num_clocks), ++ clk_data = devm_kzalloc(dev, ++ struct_size(clk_data, hws, ++ soc_data->num_clocks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = soc_data->num_clocks; +- r = soc_data->hw_init(pdev, soc_data, clk_data); +- if (r) +- return r; ++ err = soc_data->hw_init(pdev, soc_data, clk_data); ++ if (err) ++ return err; + +- return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); ++ return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, ++ clk_data); + } + + static const struct en_clk_soc_data en7523_data = { diff --git a/target/linux/airoha/patches-6.12/600-08-clk-en7523-add-support-for-probing-SCU-child.patch b/target/linux/airoha/patches-6.12/600-08-clk-en7523-add-support-for-probing-SCU-child.patch new file mode 100644 index 00000000000..4d57996a071 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-08-clk-en7523-add-support-for-probing-SCU-child.patch @@ -0,0 +1,57 @@ +From 8f1aea6f4aa61e09eb29b41ff9fffeedd5b2fc0d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 17 Jun 2025 13:15:19 +0200 +Subject: [PATCH 08/10] clk: en7523: add support for probing SCU child + +On new Airoha SoC in the SCU register space additional pheriperal might +be present aside from the clock/reset. The Airoha AN7583 SoC is an +example of this where 2 MDIO controller are present. + +Introduce a bool "probe_child" to trigger probe of child node of the SCU +node. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -83,6 +84,7 @@ struct en_rst_data { + }; + + struct en_clk_soc_data { ++ bool probe_child; + u32 num_clocks; + const struct en_clk_desc *base_clks; + const struct clk_ops pcie_ops; +@@ -900,8 +902,19 @@ static int en7523_clk_probe(struct platf + if (err) + return err; + +- return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, +- clk_data); ++ err = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, ++ clk_data); ++ if (err) ++ return err; ++ ++ if (soc_data->probe_child) { ++ err = of_platform_populate(dev->of_node, NULL, NULL, ++ dev); ++ if (err) ++ return err; ++ } ++ ++ return 0; + } + + static const struct en_clk_soc_data en7523_data = { diff --git a/target/linux/airoha/patches-6.12/600-09-dt-bindings-clock-airoha-Document-support-for-AN7583.patch b/target/linux/airoha/patches-6.12/600-09-dt-bindings-clock-airoha-Document-support-for-AN7583.patch new file mode 100644 index 00000000000..7c3430bb959 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-09-dt-bindings-clock-airoha-Document-support-for-AN7583.patch @@ -0,0 +1,122 @@ +From 12838dd20851a6eae67061c5f195f31981a4d8c1 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 28 May 2025 02:39:35 +0200 +Subject: [PATCH 09/10] dt-bindings: clock: airoha: Document support for AN7583 + clock + +Document support for Airoha AN7583 clock. This is based on the EN7523 +clock schema with the new requirement of the "airoha,chip-scu" +(previously optional for EN7581). + +Add additional binding for additional clock and reset lines. + +Signed-off-by: Christian Marangi +--- + .../bindings/clock/airoha,en7523-scu.yaml | 9 +++ + include/dt-bindings/clock/en7523-clk.h | 3 + + .../dt-bindings/reset/airoha,an7583-reset.h | 61 +++++++++++++++++++ + 3 files changed, 73 insertions(+) + create mode 100644 include/dt-bindings/reset/airoha,an7583-reset.h + +# diff --git a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml +# index bce77a14c938..be9759b86fdc 100644 +# --- a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml +# +++ b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml +# @@ -32,6 +32,7 @@ properties: +# - enum: +# - airoha,en7523-scu +# - airoha,en7581-scu +# + - airoha,an7583-scu + +# reg: +# items: +# @@ -82,6 +83,14 @@ allOf: +# reg: +# maxItems: 1 + +# + - if: +# + properties: +# + compatible: +# + const: airoha,an7583-scu +# + then: +# + required: +# + - airoha,chip-scu +# + +# additionalProperties: false + +# examples: +--- a/include/dt-bindings/clock/en7523-clk.h ++++ b/include/dt-bindings/clock/en7523-clk.h +@@ -14,4 +14,7 @@ + + #define EN7581_CLK_EMMC 8 + ++#define AN7583_CLK_MDIO0 9 ++#define AN7583_CLK_MDIO1 10 ++ + #endif /* _DT_BINDINGS_CLOCK_AIROHA_EN7523_H_ */ +--- /dev/null ++++ b/include/dt-bindings/reset/airoha,an7583-reset.h +@@ -0,0 +1,62 @@ ++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Christian Marangi ++ */ ++ ++#ifndef __DT_BINDINGS_RESET_CONTROLLER_AIROHA_AN7583_H_ ++#define __DT_BINDINGS_RESET_CONTROLLER_AIROHA_AN7583_H_ ++ ++/* RST_CTRL2 */ ++#define AN7583_XPON_PHY_RST 0 ++#define AN7583_GPON_OLT_RST 1 ++#define AN7583_CPU_TIMER2_RST 2 ++#define AN7583_HSUART_RST 3 ++#define AN7583_UART4_RST 4 ++#define AN7583_UART5_RST 5 ++#define AN7583_I2C2_RST 6 ++#define AN7583_XSI_MAC_RST 7 ++#define AN7583_XSI_PHY_RST 8 ++#define AN7583_NPU_RST 9 ++#define AN7583_TRNG_MSTART_RST 10 ++#define AN7583_DUAL_HSI0_RST 11 ++#define AN7583_DUAL_HSI1_RST 12 ++#define AN7583_DUAL_HSI0_MAC_RST 13 ++#define AN7583_DUAL_HSI1_MAC_RST 14 ++#define AN7583_XPON_XFI_RST 15 ++#define AN7583_WDMA_RST 16 ++#define AN7583_WOE0_RST 17 ++#define AN7583_HSDMA_RST 18 ++#define AN7583_TDMA_RST 19 ++#define AN7583_EMMC_RST 20 ++#define AN7583_SOE_RST 21 ++#define AN7583_XFP_MAC_RST 22 ++#define AN7583_MDIO0 23 ++#define AN7583_MDIO1 24 ++/* RST_CTRL1 */ ++#define AN7583_PCM1_ZSI_ISI_RST 25 ++#define AN7583_FE_PDMA_RST 26 ++#define AN7583_FE_QDMA_RST 27 ++#define AN7583_PCM_SPIWP_RST 28 ++#define AN7583_CRYPTO_RST 29 ++#define AN7583_TIMER_RST 30 ++#define AN7583_PCM1_RST 31 ++#define AN7583_UART_RST 32 ++#define AN7583_GPIO_RST 33 ++#define AN7583_GDMA_RST 34 ++#define AN7583_I2C_MASTER_RST 35 ++#define AN7583_PCM2_ZSI_ISI_RST 36 ++#define AN7583_SFC_RST 37 ++#define AN7583_UART2_RST 38 ++#define AN7583_GDMP_RST 39 ++#define AN7583_FE_RST 40 ++#define AN7583_USB_HOST_P0_RST 41 ++#define AN7583_GSW_RST 42 ++#define AN7583_SFC2_PCM_RST 43 ++#define AN7583_PCIE0_RST 44 ++#define AN7583_PCIE1_RST 45 ++#define AN7583_CPU_TIMER_RST 46 ++#define AN7583_PCIE_HB_RST 47 ++#define AN7583_XPON_MAC_RST 48 ++ ++#endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_AN7583_H_ */ diff --git a/target/linux/airoha/patches-6.12/600-10-clk-en7523-add-support-for-Airoha-AN7583-clock.patch b/target/linux/airoha/patches-6.12/600-10-clk-en7523-add-support-for-Airoha-AN7583-clock.patch new file mode 100644 index 00000000000..66f5cdb3f8f --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-10-clk-en7523-add-support-for-Airoha-AN7583-clock.patch @@ -0,0 +1,328 @@ +From 3c5cd99f894c23650accf19fef18b5b9bbe83941 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 8 Feb 2025 00:43:27 +0100 +Subject: [PATCH 10/10] clk: en7523: add support for Airoha AN7583 clock + +Add support for Airoha AN7583 clock and reset. + +Airoha AN7583 SoC have the same register address of EN7581 but implement +different bits and additional base clocks. Also reset are different with +the introduction of 2 dedicated MDIO line and drop of some reset lines. + +Signed-off-by: Christian Marangi +--- + drivers/clk/clk-en7523.c | 264 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 264 insertions(+) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #define RST_NR_PER_BANK 32 + +@@ -104,6 +105,14 @@ static const u32 bus7581_base[] = { 6000 + static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; + static const u32 crypto_base[] = { 540000000, 480000000 }; + static const u32 emmc7581_base[] = { 200000000, 150000000 }; ++/* AN7583 */ ++static const u32 gsw7583_base[] = { 540672000, 270336000, 400000000, 200000000 }; ++static const u32 emi7583_base[] = { 540672000, 480000000, 400000000, 300000000 }; ++static const u32 bus7583_base[] = { 600000000, 540672000, 480000000, 400000000 }; ++static const u32 spi7583_base[] = { 400000000, 12500000 }; ++static const u32 npu7583_base[] = { 666000000, 800000000, 720000000, 600000000 }; ++static const u32 crypto7583_base[] = { 540672000, 400000000 }; ++static const u32 emmc7583_base[] = { 150000000, 200000000 }; + + static const struct en_clk_desc en7523_base_clks[] = { + { +@@ -306,6 +315,138 @@ static const struct en_clk_desc en7581_b + } + }; + ++static const struct en_clk_desc an7583_base_clks[] = { ++ { ++ .id = EN7523_CLK_GSW, ++ .name = "gsw", ++ ++ .base_reg = REG_GSW_CLK_DIV_SEL, ++ .base_bits = 2, ++ .base_shift = 8, ++ .base_values = gsw7583_base, ++ .n_base_values = ARRAY_SIZE(gsw7583_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_EMI, ++ .name = "emi", ++ ++ .base_reg = REG_EMI_CLK_DIV_SEL, ++ .base_bits = 2, ++ .base_shift = 8, ++ .base_values = emi7583_base, ++ .n_base_values = ARRAY_SIZE(emi7583_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_BUS, ++ .name = "bus", ++ ++ .base_reg = REG_BUS_CLK_DIV_SEL, ++ .base_bits = 2, ++ .base_shift = 8, ++ .base_values = bus7583_base, ++ .n_base_values = ARRAY_SIZE(bus7583_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_SLIC, ++ .name = "slic", ++ ++ .base_reg = REG_SPI_CLK_FREQ_SEL, ++ .base_bits = 1, ++ .base_shift = 1, ++ .base_values = slic_base, ++ .n_base_values = ARRAY_SIZE(slic_base), ++ ++ .div_reg = REG_SPI_CLK_DIV_SEL, ++ .div_bits = 5, ++ .div_shift = 24, ++ .div_val0 = 20, ++ .div_step = 2, ++ }, { ++ .id = EN7523_CLK_SPI, ++ .name = "spi", ++ ++ .base_reg = REG_SPI_CLK_FREQ_SEL, ++ .base_bits = 1, ++ .base_shift = 0, ++ .base_values = spi7583_base, ++ .n_base_values = ARRAY_SIZE(spi7583_base), ++ ++ .div_reg = REG_SPI_CLK_DIV_SEL, ++ .div_bits = 5, ++ .div_shift = 8, ++ .div_val0 = 40, ++ .div_step = 2, ++ }, { ++ .id = EN7523_CLK_NPU, ++ .name = "npu", ++ ++ .base_reg = REG_NPU_CLK_DIV_SEL, ++ .base_bits = 2, ++ .base_shift = 9, ++ .base_values = npu7583_base, ++ .n_base_values = ARRAY_SIZE(npu7583_base), ++ ++ .div_bits = 3, ++ .div_shift = 0, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = EN7523_CLK_CRYPTO, ++ .name = "crypto", ++ ++ .base_reg = REG_CRYPTO_CLKSRC2, ++ .base_bits = 1, ++ .base_shift = 0, ++ .base_values = crypto7583_base, ++ .n_base_values = ARRAY_SIZE(crypto7583_base), ++ }, { ++ .id = EN7581_CLK_EMMC, ++ .name = "emmc", ++ ++ .base_reg = REG_CRYPTO_CLKSRC2, ++ .base_bits = 1, ++ .base_shift = 13, ++ .base_values = emmc7583_base, ++ .n_base_values = ARRAY_SIZE(emmc7583_base), ++ }, { ++ .id = AN7583_CLK_MDIO0, ++ .name = "mdio0", ++ ++ .base_reg = REG_CRYPTO_CLKSRC2, ++ ++ .base_value = 25000000, ++ ++ .div_bits = 4, ++ .div_shift = 15, ++ .div_step = 1, ++ .div_offset = 1, ++ }, { ++ .id = AN7583_CLK_MDIO1, ++ .name = "mdio1", ++ ++ .base_reg = REG_CRYPTO_CLKSRC2, ++ ++ .base_value = 25000000, ++ ++ .div_bits = 4, ++ .div_shift = 19, ++ .div_step = 1, ++ .div_offset = 1, ++ } ++}; ++ + static const u16 en7581_rst_ofs[] = { + REG_RST_CTRL2, + REG_RST_CTRL1, +@@ -369,6 +510,60 @@ static const u16 en7581_rst_map[] = { + [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, + }; + ++static const u16 an7583_rst_map[] = { ++ /* RST_CTRL2 */ ++ [AN7583_XPON_PHY_RST] = 0, ++ [AN7583_GPON_OLT_RST] = 1, ++ [AN7583_CPU_TIMER2_RST] = 2, ++ [AN7583_HSUART_RST] = 3, ++ [AN7583_UART4_RST] = 4, ++ [AN7583_UART5_RST] = 5, ++ [AN7583_I2C2_RST] = 6, ++ [AN7583_XSI_MAC_RST] = 7, ++ [AN7583_XSI_PHY_RST] = 8, ++ [AN7583_NPU_RST] = 9, ++ [AN7583_TRNG_MSTART_RST] = 12, ++ [AN7583_DUAL_HSI0_RST] = 13, ++ [AN7583_DUAL_HSI1_RST] = 14, ++ [AN7583_DUAL_HSI0_MAC_RST] = 16, ++ [AN7583_DUAL_HSI1_MAC_RST] = 17, ++ [AN7583_XPON_XFI_RST] = 18, ++ [AN7583_WDMA_RST] = 19, ++ [AN7583_WOE0_RST] = 20, ++ [AN7583_HSDMA_RST] = 22, ++ [AN7583_TDMA_RST] = 24, ++ [AN7583_EMMC_RST] = 25, ++ [AN7583_SOE_RST] = 26, ++ [AN7583_XFP_MAC_RST] = 28, ++ [AN7583_MDIO0] = 30, ++ [AN7583_MDIO1] = 31, ++ /* RST_CTRL1 */ ++ [AN7583_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, ++ [AN7583_FE_PDMA_RST] = RST_NR_PER_BANK + 1, ++ [AN7583_FE_QDMA_RST] = RST_NR_PER_BANK + 2, ++ [AN7583_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, ++ [AN7583_CRYPTO_RST] = RST_NR_PER_BANK + 6, ++ [AN7583_TIMER_RST] = RST_NR_PER_BANK + 8, ++ [AN7583_PCM1_RST] = RST_NR_PER_BANK + 11, ++ [AN7583_UART_RST] = RST_NR_PER_BANK + 12, ++ [AN7583_GPIO_RST] = RST_NR_PER_BANK + 13, ++ [AN7583_GDMA_RST] = RST_NR_PER_BANK + 14, ++ [AN7583_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, ++ [AN7583_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, ++ [AN7583_SFC_RST] = RST_NR_PER_BANK + 18, ++ [AN7583_UART2_RST] = RST_NR_PER_BANK + 19, ++ [AN7583_GDMP_RST] = RST_NR_PER_BANK + 20, ++ [AN7583_FE_RST] = RST_NR_PER_BANK + 21, ++ [AN7583_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, ++ [AN7583_GSW_RST] = RST_NR_PER_BANK + 23, ++ [AN7583_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, ++ [AN7583_PCIE0_RST] = RST_NR_PER_BANK + 26, ++ [AN7583_PCIE1_RST] = RST_NR_PER_BANK + 27, ++ [AN7583_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, ++ [AN7583_PCIE_HB_RST] = RST_NR_PER_BANK + 29, ++ [AN7583_XPON_MAC_RST] = RST_NR_PER_BANK + 31, ++}; ++ + static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val) + { + if (!desc->base_bits) +@@ -881,6 +1076,62 @@ static int en7581_clk_hw_init(struct pla + return en7581_reset_register(&pdev->dev, clk_map); + } + ++static int an7583_reset_register(struct device *dev, struct regmap *map) ++{ ++ struct en_rst_data *rst_data; ++ ++ rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL); ++ if (!rst_data) ++ return -ENOMEM; ++ ++ rst_data->bank_ofs = en7581_rst_ofs; ++ rst_data->idx_map = an7583_rst_map; ++ rst_data->map = map; ++ ++ rst_data->rcdev.nr_resets = ARRAY_SIZE(an7583_rst_map); ++ rst_data->rcdev.of_xlate = en7523_reset_xlate; ++ rst_data->rcdev.ops = &en7581_reset_ops; ++ rst_data->rcdev.of_node = dev->of_node; ++ rst_data->rcdev.of_reset_n_cells = 1; ++ rst_data->rcdev.owner = THIS_MODULE; ++ rst_data->rcdev.dev = dev; ++ ++ return devm_reset_controller_register(dev, &rst_data->rcdev); ++} ++ ++static int an7583_clk_hw_init(struct platform_device *pdev, ++ const struct en_clk_soc_data *soc_data, ++ struct clk_hw_onecell_data *clk_data) ++{ ++ struct device *dev = &pdev->dev; ++ struct regmap *map, *clk_map; ++ void __iomem *base; ++ int err; ++ ++ map = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,chip-scu"); ++ if (IS_ERR(map)) ++ return PTR_ERR(map); ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ clk_map = devm_regmap_init_mmio(&pdev->dev, base, &en7523_clk_regmap_config); ++ if (IS_ERR(clk_map)) ++ return PTR_ERR(clk_map); ++ ++ err = en75xx_register_clocks(dev, soc_data, clk_data, map, clk_map); ++ if (err) ++ return err; ++ ++ regmap_clear_bits(clk_map, REG_NP_SCU_SSTR, ++ REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK); ++ regmap_update_bits(clk_map, REG_NP_SCU_PCIC, REG_PCIE_CTRL, ++ FIELD_PREP(REG_PCIE_CTRL, 3)); ++ ++ return an7583_reset_register(dev, clk_map); ++} ++ + static int en7523_clk_probe(struct platform_device *pdev) + { + const struct en_clk_soc_data *soc_data; +@@ -940,9 +1191,23 @@ static const struct en_clk_soc_data en75 + .hw_init = en7581_clk_hw_init, + }; + ++static const struct en_clk_soc_data an7583_data = { ++ .probe_child = true, ++ .base_clks = an7583_base_clks, ++ /* We increment num_clocks by 1 to account for additional PCIe clock */ ++ .num_clocks = ARRAY_SIZE(an7583_base_clks) + 1, ++ .pcie_ops = { ++ .is_enabled = en7581_pci_is_enabled, ++ .enable = en7581_pci_enable, ++ .disable = en7581_pci_disable, ++ }, ++ .hw_init = an7583_clk_hw_init, ++}; ++ + static const struct of_device_id of_match_clk_en7523[] = { + { .compatible = "airoha,en7523-scu", .data = &en7523_data }, + { .compatible = "airoha,en7581-scu", .data = &en7581_data }, ++ { .compatible = "airoha,an7583-scu", .data = &an7583_data }, + { /* sentinel */ } + }; + diff --git a/target/linux/airoha/patches-6.12/600-11-dt-bindings-clock-airoha-Add-reset-support-to-EN7523.patch b/target/linux/airoha/patches-6.12/600-11-dt-bindings-clock-airoha-Add-reset-support-to-EN7523.patch new file mode 100644 index 00000000000..0c391ebce99 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-11-dt-bindings-clock-airoha-Add-reset-support-to-EN7523.patch @@ -0,0 +1,100 @@ +From 38195ddfcea372924a68b64f7a6f9235488160be Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sat, 20 Sep 2025 03:57:25 +0300 +Subject: [PATCH 1/3] dt-bindings: clock: airoha: Add reset support to EN7523 + clock binding + +Introduce reset capability to EN7523 device-tree clock binding +documentation. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: Rob Herring (Arm) +--- + .../bindings/clock/airoha,en7523-scu.yaml | 3 +- + .../dt-bindings/reset/airoha,en7523-reset.h | 61 +++++++++++++++++++ + 2 files changed, 62 insertions(+), 2 deletions(-) + create mode 100644 include/dt-bindings/reset/airoha,en7523-reset.h + +--- a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml ++++ b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml +@@ -64,8 +64,6 @@ allOf: + - description: scu base address + - description: misc scu base address + +- '#reset-cells': false +- + - if: + properties: + compatible: +@@ -89,6 +87,7 @@ examples: + reg = <0x1fa20000 0x400>, + <0x1fb00000 0x1000>; + #clock-cells = <1>; ++ #reset-cells = <1>; + }; + + - | +--- /dev/null ++++ b/include/dt-bindings/reset/airoha,en7523-reset.h +@@ -0,0 +1,61 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) 2024 iopsys Software Solutions AB. ++ * Copyright (C) 2025 Genexis AB. ++ * ++ * Author: Mikhail Kshevetskiy ++ * ++ * based on ++ * include/dt-bindings/reset/airoha,en7581-reset.h ++ * by Lorenzo Bianconi ++ */ ++ ++#ifndef __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7523_H_ ++#define __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7523_H_ ++ ++/* RST_CTRL2 */ ++#define EN7523_XPON_PHY_RST 0 ++#define EN7523_XSI_MAC_RST 1 ++#define EN7523_XSI_PHY_RST 2 ++#define EN7523_NPU_RST 3 ++#define EN7523_I2S_RST 4 ++#define EN7523_TRNG_RST 5 ++#define EN7523_TRNG_MSTART_RST 6 ++#define EN7523_DUAL_HSI0_RST 7 ++#define EN7523_DUAL_HSI1_RST 8 ++#define EN7523_HSI_RST 9 ++#define EN7523_DUAL_HSI0_MAC_RST 10 ++#define EN7523_DUAL_HSI1_MAC_RST 11 ++#define EN7523_HSI_MAC_RST 12 ++#define EN7523_WDMA_RST 13 ++#define EN7523_WOE0_RST 14 ++#define EN7523_WOE1_RST 15 ++#define EN7523_HSDMA_RST 16 ++#define EN7523_I2C2RBUS_RST 17 ++#define EN7523_TDMA_RST 18 ++/* RST_CTRL1 */ ++#define EN7523_PCM1_ZSI_ISI_RST 19 ++#define EN7523_FE_PDMA_RST 20 ++#define EN7523_FE_QDMA_RST 21 ++#define EN7523_PCM_SPIWP_RST 22 ++#define EN7523_CRYPTO_RST 23 ++#define EN7523_TIMER_RST 24 ++#define EN7523_PCM1_RST 25 ++#define EN7523_UART_RST 26 ++#define EN7523_GPIO_RST 27 ++#define EN7523_GDMA_RST 28 ++#define EN7523_I2C_MASTER_RST 29 ++#define EN7523_PCM2_ZSI_ISI_RST 30 ++#define EN7523_SFC_RST 31 ++#define EN7523_UART2_RST 32 ++#define EN7523_GDMP_RST 33 ++#define EN7523_FE_RST 34 ++#define EN7523_USB_HOST_P0_RST 35 ++#define EN7523_GSW_RST 36 ++#define EN7523_SFC2_PCM_RST 37 ++#define EN7523_PCIE0_RST 38 ++#define EN7523_PCIE1_RST 39 ++#define EN7523_PCIE_HB_RST 40 ++#define EN7523_XPON_MAC_RST 41 ++ ++#endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7523_H_ */ diff --git a/target/linux/airoha/patches-6.12/600-12-clk-en7523-Add-reset-controller-support-for-EN7523-S.patch b/target/linux/airoha/patches-6.12/600-12-clk-en7523-Add-reset-controller-support-for-EN7523-S.patch new file mode 100644 index 00000000000..c658e2f7757 --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-12-clk-en7523-Add-reset-controller-support-for-EN7523-S.patch @@ -0,0 +1,177 @@ +From 37de26f9d2f55cd74af55cb29c2860b5989bb728 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sat, 20 Sep 2025 03:57:25 +0300 +Subject: [PATCH 2/3] clk: en7523: Add reset-controller support for EN7523 SoC + +Introduce reset API support to EN7523 clock driver. EN7523 uses the +same reset logic as EN7581, so just reuse existing code. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +--- + drivers/clk/clk-en7523.c | 96 ++++++++++++++++++++++++++++------------ + 1 file changed, 67 insertions(+), 29 deletions(-) + +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -452,6 +453,53 @@ static const u16 en7581_rst_ofs[] = { + REG_RST_CTRL1, + }; + ++static const u16 en7523_rst_map[] = { ++ /* RST_CTRL2 */ ++ [EN7523_XPON_PHY_RST] = 0, ++ [EN7523_XSI_MAC_RST] = 7, ++ [EN7523_XSI_PHY_RST] = 8, ++ [EN7523_NPU_RST] = 9, ++ [EN7523_I2S_RST] = 10, ++ [EN7523_TRNG_RST] = 11, ++ [EN7523_TRNG_MSTART_RST] = 12, ++ [EN7523_DUAL_HSI0_RST] = 13, ++ [EN7523_DUAL_HSI1_RST] = 14, ++ [EN7523_HSI_RST] = 15, ++ [EN7523_DUAL_HSI0_MAC_RST] = 16, ++ [EN7523_DUAL_HSI1_MAC_RST] = 17, ++ [EN7523_HSI_MAC_RST] = 18, ++ [EN7523_WDMA_RST] = 19, ++ [EN7523_WOE0_RST] = 20, ++ [EN7523_WOE1_RST] = 21, ++ [EN7523_HSDMA_RST] = 22, ++ [EN7523_I2C2RBUS_RST] = 23, ++ [EN7523_TDMA_RST] = 24, ++ /* RST_CTRL1 */ ++ [EN7523_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, ++ [EN7523_FE_PDMA_RST] = RST_NR_PER_BANK + 1, ++ [EN7523_FE_QDMA_RST] = RST_NR_PER_BANK + 2, ++ [EN7523_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, ++ [EN7523_CRYPTO_RST] = RST_NR_PER_BANK + 6, ++ [EN7523_TIMER_RST] = RST_NR_PER_BANK + 8, ++ [EN7523_PCM1_RST] = RST_NR_PER_BANK + 11, ++ [EN7523_UART_RST] = RST_NR_PER_BANK + 12, ++ [EN7523_GPIO_RST] = RST_NR_PER_BANK + 13, ++ [EN7523_GDMA_RST] = RST_NR_PER_BANK + 14, ++ [EN7523_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, ++ [EN7523_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, ++ [EN7523_SFC_RST] = RST_NR_PER_BANK + 18, ++ [EN7523_UART2_RST] = RST_NR_PER_BANK + 19, ++ [EN7523_GDMP_RST] = RST_NR_PER_BANK + 20, ++ [EN7523_FE_RST] = RST_NR_PER_BANK + 21, ++ [EN7523_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, ++ [EN7523_GSW_RST] = RST_NR_PER_BANK + 23, ++ [EN7523_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, ++ [EN7523_PCIE0_RST] = RST_NR_PER_BANK + 26, ++ [EN7523_PCIE1_RST] = RST_NR_PER_BANK + 27, ++ [EN7523_PCIE_HB_RST] = RST_NR_PER_BANK + 29, ++ [EN7523_XPON_MAC_RST] = RST_NR_PER_BANK + 31, ++}; ++ + static const u16 en7581_rst_map[] = { + /* RST_CTRL2 */ + [EN7581_XPON_PHY_RST] = 0, +@@ -564,6 +612,9 @@ static const u16 an7583_rst_map[] = { + [AN7583_XPON_MAC_RST] = RST_NR_PER_BANK + 31, + }; + ++static int en7581_reset_register(struct device *dev, struct regmap *map, ++ const u16 *rst_map, int nr_resets); ++ + static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val) + { + if (!desc->base_bits) +@@ -942,6 +993,7 @@ static int en7523_clk_hw_init(struct pla + { + void __iomem *base, *np_base; + struct regmap *map, *clk_map; ++ int err; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) +@@ -961,7 +1013,13 @@ static int en7523_clk_hw_init(struct pla + if (IS_ERR(clk_map)) + return PTR_ERR(clk_map); + +- return en75xx_register_clocks(&pdev->dev, soc_data, clk_data, map, clk_map); ++ ++ err = en75xx_register_clocks(&pdev->dev, soc_data, clk_data, map, clk_map); ++ if (err) ++ return err; ++ ++ return en7581_reset_register(&pdev->dev, clk_map, en7523_rst_map, ++ ARRAY_SIZE(en7523_rst_map)); + } + + static int en7523_reset_update(struct reset_controller_dev *rcdev, +@@ -1016,7 +1074,8 @@ static const struct reset_control_ops en + .status = en7523_reset_status, + }; + +-static int en7581_reset_register(struct device *dev, struct regmap *map) ++static int en7581_reset_register(struct device *dev, struct regmap *map, ++ const u16 *rst_map, int nr_resets) + { + struct en_rst_data *rst_data; + +@@ -1025,10 +1084,10 @@ static int en7581_reset_register(struct + return -ENOMEM; + + rst_data->bank_ofs = en7581_rst_ofs; +- rst_data->idx_map = en7581_rst_map; ++ rst_data->idx_map = rst_map; + rst_data->map = map; + +- rst_data->rcdev.nr_resets = ARRAY_SIZE(en7581_rst_map); ++ rst_data->rcdev.nr_resets = nr_resets; + rst_data->rcdev.of_xlate = en7523_reset_xlate; + rst_data->rcdev.ops = &en7581_reset_ops; + rst_data->rcdev.of_node = dev->of_node; +@@ -1073,30 +1132,8 @@ static int en7581_clk_hw_init(struct pla + regmap_update_bits(clk_map, REG_NP_SCU_PCIC, REG_PCIE_CTRL, + FIELD_PREP(REG_PCIE_CTRL, 3)); + +- return en7581_reset_register(&pdev->dev, clk_map); +-} +- +-static int an7583_reset_register(struct device *dev, struct regmap *map) +-{ +- struct en_rst_data *rst_data; +- +- rst_data = devm_kzalloc(dev, sizeof(*rst_data), GFP_KERNEL); +- if (!rst_data) +- return -ENOMEM; +- +- rst_data->bank_ofs = en7581_rst_ofs; +- rst_data->idx_map = an7583_rst_map; +- rst_data->map = map; +- +- rst_data->rcdev.nr_resets = ARRAY_SIZE(an7583_rst_map); +- rst_data->rcdev.of_xlate = en7523_reset_xlate; +- rst_data->rcdev.ops = &en7581_reset_ops; +- rst_data->rcdev.of_node = dev->of_node; +- rst_data->rcdev.of_reset_n_cells = 1; +- rst_data->rcdev.owner = THIS_MODULE; +- rst_data->rcdev.dev = dev; +- +- return devm_reset_controller_register(dev, &rst_data->rcdev); ++ return en7581_reset_register(&pdev->dev, clk_map, en7581_rst_map, ++ ARRAY_SIZE(en7581_rst_map)); + } + + static int an7583_clk_hw_init(struct platform_device *pdev, +@@ -1129,7 +1166,8 @@ static int an7583_clk_hw_init(struct pla + regmap_update_bits(clk_map, REG_NP_SCU_PCIC, REG_PCIE_CTRL, + FIELD_PREP(REG_PCIE_CTRL, 3)); + +- return an7583_reset_register(dev, clk_map); ++ return en7581_reset_register(dev, clk_map, an7583_rst_map, ++ ARRAY_SIZE(an7583_rst_map)); + } + + static int en7523_clk_probe(struct platform_device *pdev) diff --git a/target/linux/airoha/patches-6.12/600-13-ARM-dts-airoha-update-EN7523-dtsi-to-support-resets.patch b/target/linux/airoha/patches-6.12/600-13-ARM-dts-airoha-update-EN7523-dtsi-to-support-resets.patch new file mode 100644 index 00000000000..3ed155aaf0a --- /dev/null +++ b/target/linux/airoha/patches-6.12/600-13-ARM-dts-airoha-update-EN7523-dtsi-to-support-resets.patch @@ -0,0 +1,32 @@ +From 6e5e3a8e20d12f60a1b902160b92337b392b1c18 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Sat, 20 Sep 2025 03:57:25 +0300 +Subject: [PATCH 3/3] ARM: dts: airoha: update EN7523 dtsi to support resets + +This patch updates EN7523 dtsi to reflect the reset-controller +support for EN7523 SoC. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: AngeloGioacchino Del Regno +--- + arch/arm/boot/dts/airoha/en7523.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/airoha/en7523.dtsi ++++ b/arch/arm/boot/dts/airoha/en7523.dtsi +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + + / { + interrupt-parent = <&gic>; +@@ -91,6 +92,7 @@ + reg = <0x1fa20000 0x400>, + <0x1fb00000 0x1000>; + #clock-cells = <1>; ++ #reset-cells = <1>; + }; + + gic: interrupt-controller@9000000 { diff --git a/target/linux/airoha/patches-6.12/604-01-net-pcs-airoha-add-support-for-AN7583.patch b/target/linux/airoha/patches-6.12/604-01-net-pcs-airoha-add-support-for-AN7583.patch new file mode 100644 index 00000000000..0801c2bec17 --- /dev/null +++ b/target/linux/airoha/patches-6.12/604-01-net-pcs-airoha-add-support-for-AN7583.patch @@ -0,0 +1,3386 @@ +From e4c0769d72f34035f4ac415ad155bbdf5a8a4f90 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 25 Jun 2025 00:00:59 +0200 +Subject: [PATCH 18/20] net: pcs: airoha: add support for Airoha AN7583 SoC + +Add support for Airoha AN7583 PCS. This use a new analog PHY +implementation that doesn't require manual calibration but makes use of +internal algo to lock to the center of the band EYE. + +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/airoha/Kconfig | 7 + + drivers/net/pcs/airoha/Makefile | 3 + + drivers/net/pcs/airoha/pcs-airoha-common.c | 169 +- + drivers/net/pcs/airoha/pcs-airoha.h | 452 ++++ + drivers/net/pcs/airoha/pcs-an7583.c | 2349 ++++++++++++++++++++ + 5 files changed, 2970 insertions(+), 10 deletions(-) + create mode 100644 drivers/net/pcs/airoha/pcs-an7583.c + +--- a/drivers/net/pcs/airoha/Kconfig ++++ b/drivers/net/pcs/airoha/Kconfig +@@ -10,3 +10,10 @@ config PCS_AIROHA_AN7581 + help + This module provides helper to phylink for managing the Airoha + AN7581 PCS for SoC Ethernet and PON SERDES. ++ ++config PCS_AIROHA_AN7583 ++ tristate "Airoha AN7583 PCS driver" ++ select PCS_AIROHA ++ help ++ This module provides helper to phylink for managing the Airoha ++ AN7583 PCS for SoC Ethernet and PON SERDES. +--- a/drivers/net/pcs/airoha/Makefile ++++ b/drivers/net/pcs/airoha/Makefile +@@ -5,3 +5,6 @@ pcs-airoha-objs := pcs-airoha-common.o + ifdef CONFIG_PCS_AIROHA_AN7581 + pcs-airoha-objs += pcs-an7581.o + endif ++ifdef CONFIG_PCS_AIROHA_AN7583 ++pcs-airoha-objs += pcs-an7583.o ++endif +--- a/drivers/net/pcs/airoha/pcs-airoha-common.c ++++ b/drivers/net/pcs/airoha/pcs-airoha-common.c +@@ -21,6 +21,7 @@ + static void airoha_pcs_setup_scu_eth(struct airoha_pcs_priv *priv, + phy_interface_t interface) + { ++ struct device *dev = priv->dev; + u32 xsi_sel; + + switch (interface) { +@@ -38,6 +39,12 @@ static void airoha_pcs_setup_scu_eth(str + regmap_update_bits(priv->scu, AIROHA_SCU_SSR3, + AIROHA_SCU_ETH_XSI_SEL, + xsi_sel); ++ ++ /* AN7583 require additional setting */ ++ if (device_is_compatible(dev, "airoha,an7583-pcs-eth")) ++ regmap_update_bits(priv->scu, AIROHA_SCU_WAN_CONF, ++ AIROHA_SCU_ETH_MAC_SEL, ++ AIROHA_SCU_ETH_MAC_SEL_XFI); + } + + static void airoha_pcs_setup_scu_pon(struct airoha_pcs_priv *priv, +@@ -71,7 +78,7 @@ static void airoha_pcs_setup_scu_pon(str + wan_sel); + } + +-static void airoha_pcs_setup_scu_pcie(struct airoha_pcs_priv *priv, ++static void an7581_pcs_setup_scu_pcie(struct airoha_pcs_priv *priv, + int index, phy_interface_t interface) + { + u32 xsi_sel; +@@ -111,6 +118,26 @@ static void airoha_pcs_setup_scu_pcie(st + } + } + ++static void an7583_pcs_setup_scu_pcie(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ u32 xsi_sel; ++ ++ if (priv->phy) ++ return; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ default: ++ xsi_sel = AIROHA_SCU_PCIE_XSI1_USXGMII; ++ } ++ ++ regmap_update_bits(priv->scu, AIROHA_SCU_SSTR, ++ AIROHA_SCU_PCIE_XSI1_SEL, ++ xsi_sel); ++} ++ + static int airoha_pcs_setup_scu(struct airoha_pcs_priv *priv, + int index, phy_interface_t interface) + { +@@ -125,7 +152,10 @@ static int airoha_pcs_setup_scu(struct a + airoha_pcs_setup_scu_pon(priv, interface); + break; + case AIROHA_PCS_PCIE: +- airoha_pcs_setup_scu_pcie(priv, index, interface); ++ if (device_is_compatible(priv->dev, "airoha,an7581-pcs-pcie")) ++ an7581_pcs_setup_scu_pcie(priv, index, interface); ++ else ++ an7583_pcs_setup_scu_pcie(priv, index, interface); + break; + case AIROHA_PCS_USB: + break; +@@ -147,18 +177,25 @@ static int airoha_pcs_setup_scu(struct a + + static void airoha_pcs_init_usxgmii(struct airoha_pcs_priv *priv, int index) + { ++ const struct airoha_pcs_match_data *data = priv->data; + struct airoha_pcs_maps *maps = &priv->maps[index]; + + regmap_set_bits(maps->multi_sgmii, AIROHA_PCS_MULTI_SGMII_MSG_RX_CTRL_0, + AIROHA_PCS_HSGMII_XFI_SEL); + + /* Disable Hibernation */ +- regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTROL_1, +- AIROHA_PCS_USXGMII_SPEED_SEL_H); ++ if (data->hibernation_workaround) ++ regmap_clear_bits(maps->usxgmii_pcs, AIROHA_PCS_USXGMII_PCS_CTROL_1, ++ AIROHA_PCS_USXGMII_SPEED_SEL_H); + + /* FIXME: wait Airoha */ + /* Avoid PCS sending garbage to MAC in some HW revision (E0) */ +- regmap_write(maps->usxgmii_pcs, AIROHA_PCS_USGMII_VENDOR_DEFINE_116, 0); ++ if (data->usxgmii_ber_time_fixup) ++ regmap_write(maps->usxgmii_pcs, AIROHA_PCS_USGMII_VENDOR_DEFINE_116, 0); ++ ++ if (data->usxgmii_rx_gb_out_vld_tweak) ++ regmap_clear_bits(maps->usxgmii_pcs, AN7583_PCS_USXGMII_RTL_MODIFIED, ++ AIROHA_PCS_USXGMII_MODIFIED_RX_GB_OUT_VLD); + } + + static void airoha_pcs_init_hsgmii(struct airoha_pcs_priv *priv, int index) +@@ -512,6 +549,13 @@ static int airoha_pcs_config(struct phyl + AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7, + AIROHA_PCS_USXGMII_RATE_UPDATE_MODE); + } ++ ++ if (data->usxgmii_xfi_mode_sel && ++ neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) ++ regmap_set_bits(maps->usxgmii_pcs, ++ AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7, ++ AIROHA_PCS_USXGMII_XFI_MODE_TX_SEL | ++ AIROHA_PCS_USXGMII_XFI_MODE_RX_SEL); + } + + /* Clear any force bit that my be set by bootloader */ +@@ -1015,10 +1059,14 @@ static int airoha_pcs_usb_alloc_maps(str + if (ret) + return ret; + ++ /* For AN7583 PCS ANA is controlled by PHY driver */ ++ if (device_is_compatible(&pdev->dev, "airoha,an7583-pcs-usb")) ++ return 0; ++ + return airoha_pcs_init_named_regmap(pdev, "pcs_ana", &priv->pcs_ana); + } + +-static int airoha_pcs_pcie_alloc_maps(struct platform_device *pdev, ++static int an7581_pcs_pcie_alloc_maps(struct platform_device *pdev, + struct airoha_pcs_priv *priv) + { + struct airoha_pcs_maps *maps = priv->maps; +@@ -1115,6 +1163,60 @@ static struct phylink_pcs *airoha_pcs_ge + return &priv->ports[index].pcs; + } + ++static int an7583_pcs_pcie_alloc_maps(struct platform_device *pdev, ++ struct airoha_pcs_priv *priv) ++{ ++ struct airoha_pcs_maps *maps = priv->maps; ++ int ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_mac", &maps[0].pcs_mac); ++ if (ret) ++ return ret; ++ ++ /* On AN7583 PCS MAC is shared by SGMII/HSGMII is provided by PCIe1 PHY ++ * and USXGMII is provided by PCIE0 ANA and PMA. ++ */ ++ if (priv->phy) { ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_an0", &maps[0].hsgmii_an); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_pcs0", &maps[0].hsgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_rate_adp0", &maps[0].hsgmii_rate_adp); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "multi_sgmii0", &maps[0].multi_sgmii); ++ if (ret) ++ return ret; ++ } else { ++ ret = airoha_pcs_init_named_regmap(pdev, "hsgmii_rate_adp1", &maps[0].hsgmii_rate_adp); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "multi_sgmii1", &maps[0].multi_sgmii); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "usxgmii1", &maps[0].usxgmii_pcs); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_pma", &priv->pcs_pma[0]); ++ if (ret) ++ return ret; ++ ++ ret = airoha_pcs_init_named_regmap(pdev, "pcs_ana", &priv->pcs_ana); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int airoha_pcs_probe(struct platform_device *pdev) + { + const struct airoha_pcs_match_data *data; +@@ -1136,14 +1238,19 @@ static int airoha_pcs_probe(struct platf + priv->dev = dev; + priv->data = data; + +- if (data->port_type == AIROHA_PCS_USB) { ++ if (data->port_type == AIROHA_PCS_USB || ++ device_is_compatible(dev, "airoha,an7583-pcs-pcie")) { + struct phy *phy; + ++ /* For AN7583 PCIe PCS PHY is optional */ + phy = devm_phy_get(dev, NULL); +- if (IS_ERR(phy)) ++ if (IS_ERR(phy) && ++ (data->port_type == AIROHA_PCS_USB || ++ PTR_ERR(phy) != -ENODEV)) + return dev_err_probe(dev, PTR_ERR(phy), "failed to get phy\n"); + +- priv->phy = phy; ++ if (!IS_ERR(phy)) ++ priv->phy = phy; + } + + switch (data->port_type) { +@@ -1155,7 +1262,10 @@ static int airoha_pcs_probe(struct platf + + break; + case AIROHA_PCS_PCIE: +- ret = airoha_pcs_pcie_alloc_maps(pdev, priv); ++ if (device_is_compatible(dev, "airoha,an7581-pcs-pcie")) ++ ret = an7581_pcs_pcie_alloc_maps(pdev, priv); ++ else ++ ret = an7583_pcs_pcie_alloc_maps(pdev, priv); + if (ret) + return ret; + +@@ -1260,6 +1370,8 @@ static void airoha_pcs_remove(struct pla + static const struct airoha_pcs_match_data an7581_pcs_eth = { + .num_port = 1, + .port_type = AIROHA_PCS_ETH, ++ .hibernation_workaround = true, ++ .usxgmii_ber_time_fixup = true, + .alloc_regmap_fields = an7581_pcs_alloc_regmap_fields, + .bringup = an7581_pcs_bringup, + .link_up = an7581_pcs_phya_link_up, +@@ -1269,6 +1381,8 @@ static const struct airoha_pcs_match_dat + static const struct airoha_pcs_match_data an7581_pcs_pon = { + .num_port = 1, + .port_type = AIROHA_PCS_PON, ++ .hibernation_workaround = true, ++ .usxgmii_ber_time_fixup = true, + .alloc_regmap_fields = an7581_pcs_alloc_regmap_fields, + .bringup = an7581_pcs_bringup, + .link_up = an7581_pcs_phya_link_up, +@@ -1277,6 +1391,8 @@ static const struct airoha_pcs_match_dat + static const struct airoha_pcs_match_data an7581_pcs_pcie = { + .num_port = 2, + .port_type = AIROHA_PCS_PCIE, ++ .hibernation_workaround = true, ++ .usxgmii_ber_time_fixup = true, + .alloc_regmap_fields = an7581_pcs_pcie_alloc_regmap_fields, + .bringup = an7581_pcs_bringup, + .link_up = an7581_pcs_phya_link_up, +@@ -1288,11 +1404,44 @@ static const struct airoha_pcs_match_dat + .bringup = an7581_pcs_usb_bringup, + }; + ++static const struct airoha_pcs_match_data an7583_pcs_eth = { ++ .port_type = AIROHA_PCS_ETH, ++ .usxgmii_rx_gb_out_vld_tweak = true, ++ .usxgmii_xfi_mode_sel = true, ++ .bringup = an7583_pcs_common_phya_bringup, ++ .link_up = an7583_pcs_common_phya_link_up, ++}; ++ ++static const struct airoha_pcs_match_data an7583_pcs_pon = { ++ .port_type = AIROHA_PCS_PON, ++ .usxgmii_rx_gb_out_vld_tweak = true, ++ .usxgmii_xfi_mode_sel = true, ++ .bringup = an7583_pcs_common_phya_bringup, ++ .link_up = an7583_pcs_common_phya_link_up, ++}; ++ ++static const struct airoha_pcs_match_data an7583_pcs_pcie = { ++ .port_type = AIROHA_PCS_PCIE, ++ .usxgmii_rx_gb_out_vld_tweak = true, ++ .usxgmii_xfi_mode_sel = true, ++ .bringup = an7583_pcs_common_phya_bringup, ++ .link_up = an7583_pcs_common_phya_link_up, ++}; ++ ++static const struct airoha_pcs_match_data an7583_pcs_usb = { ++ .port_type = AIROHA_PCS_USB, ++ .bringup = an7583_pcs_usb_phya_bringup, ++}; ++ + static const struct of_device_id airoha_pcs_of_table[] = { + { .compatible = "airoha,an7581-pcs-eth", .data = &an7581_pcs_eth }, + { .compatible = "airoha,an7581-pcs-pon", .data = &an7581_pcs_pon }, + { .compatible = "airoha,an7581-pcs-pcie", .data = &an7581_pcs_pcie }, + { .compatible = "airoha,an7581-pcs-usb", .data = &an7581_pcs_usb }, ++ { .compatible = "airoha,an7583-pcs-eth", .data = &an7583_pcs_eth }, ++ { .compatible = "airoha,an7583-pcs-pon", .data = &an7583_pcs_pon }, ++ { .compatible = "airoha,an7583-pcs-pcie", .data = &an7583_pcs_pcie }, ++ { .compatible = "airoha,an7583-pcs-usb", .data = &an7583_pcs_usb }, + { /* sentinel */ }, + }; + MODULE_DEVICE_TABLE(of, airoha_pcs_of_table); +--- a/drivers/net/pcs/airoha/pcs-airoha.h ++++ b/drivers/net/pcs/airoha/pcs-airoha.h +@@ -14,6 +14,9 @@ + #define AIROHA_SCU_PDIDR 0x5c + #define AIROHA_SCU_PRODUCT_ID GENMASK(15, 0) + #define AIROHA_SCU_WAN_CONF 0x70 ++#define AIROHA_SCU_ETH_MAC_SEL BIT(24) ++#define AIROHA_SCU_ETH_MAC_SEL_XFI FIELD_PREP_CONST(AIROHA_SCU_ETH_MAC_SEL, 0x0) ++#define AIROHA_SCU_ETH_MAC_SEL_PON FIELD_PREP_CONST(AIROHA_SCU_ETH_MAC_SEL, 0x1) + #define AIROHA_SCU_WAN_SEL GENMASK(7, 0) + #define AIROHA_SCU_WAN_SEL_SGMII FIELD_PREP_CONST(AIROHA_SCU_WAN_SEL, 0x10) + #define AIROHA_SCU_WAN_SEL_HSGMII FIELD_PREP_CONST(AIROHA_SCU_WAN_SEL, 0x11) +@@ -87,8 +90,14 @@ + /* HSGMII_PCS */ + #define AIROHA_PCS_HSGMII_PCS_CTROL_1 0x0 + #define AIROHA_PCS_TBI_10B_MODE BIT(30) ++#define AIROHA_PCS_SGMIII_ENA BIT(27) ++#define AIROHA_PCS_SD_SIG_DET BIT(26) + #define AIROHA_PCS_SGMII_SEND_AN_ERR_EN BIT(24) ++#define AIROHA_PCS_SGMII_1US_TIMER GENMASK(23, 16) ++#define AIROHA_PCS_RX_CLK_ENA BIT(15) ++#define AIROHA_PCS_GMII_TXCLK_ENA BIT(14) + #define AIROHA_PCS_REMOTE_FAULT_DIS BIT(12) ++#define AIROHA_PCS_AN_ENABLE BIT(10) + #define AIROHA_PCS_HSGMII_PCS_CTROL_3 0x8 + #define AIROHA_PCS_HSGMII_PCS_LINK_STSTIME GENMASK(19, 0) + #define AIROHA_PCS_HSGMII_PCS_CTROL_6 0x14 +@@ -192,6 +201,9 @@ + #define AIROHA_PCS_P0_SGMII_IS_10 BIT(2) + #define AIROHA_PCS_P0_SGMII_IS_100 BIT(1) + #define AIROHA_PCS_P0_SGMII_IS_1000 BIT(0) ++#define AIROHA_PCS_MULTI_SGMII_MSG_RX_LIK_STS_1 0x518 ++#define AIROHA_PCS_PAUSE_STS_P2 BIT(6) ++#define AIROHA_PCS_PAUSE_STS_P1 BIT(5) + + /* HSGMII_RATE_ADP */ + #define AIROHA_PCS_HSGMII_RATE_ADAPT_CTRL_0 0x0 +@@ -287,6 +299,8 @@ + #define AIROHA_PCS_USXGMII_PCS_AN_CONTROL_6 0x31c + #define AIROHA_PCS_USXGMII_TOG_PCS_AUTONEG_STS BIT(0) + #define AIROHA_PCS_USXGMII_PCS_AN_CONTROL_7 0x320 ++#define AIROHA_PCS_USXGMII_XFI_MODE_TX_SEL BIT(20) ++#define AIROHA_PCS_USXGMII_XFI_MODE_RX_SEL BIT(16) + #define AIROHA_PCS_USXGMII_RATE_UPDATE_MODE BIT(12) + #define AIROHA_PCS_USXGMII_MODE GENMASK(10, 8) + #define AIROHA_PCS_USXGMII_MODE_10000 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x0) +@@ -294,9 +308,27 @@ + #define AIROHA_PCS_USXGMII_MODE_2500 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x2) + #define AIROHA_PCS_USXGMII_MODE_1000 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x3) + #define AIROHA_PCS_USXGMII_MODE_100 FIELD_PREP_CONST(AIROHA_PCS_USXGMII_MODE, 0x4) ++#define AN7583_PCS_USXGMII_RTL_MODIFIED 0x334 ++#define AIROHA_PCS_USXGMII_MODIFIED_RX_GB_OUT_VLD BIT(25) + + /* PMA_PHYA */ + #define AIROHA_PCS_ANA_PXP_CMN_EN 0x0 ++#define AIROHA_PCS_ANA_CMN_VREFSEL GENMASK(18, 16) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_8V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x0) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_8_25V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x1) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_8_5V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x2) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_8_75V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x3) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_9V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x4) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_9_25V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x5) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_9_5V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x6) ++#define AIROHA_PCS_ANA_CMN_VREFSEL_9_75V FIELD_PREP_CONST(AIROHA_PCS_ANA_CMN_VREFSEL, 0x7) ++#define AIROHA_PCS_ANA_CMN_VREFSEL GENMASK(18, 16) ++/* GENMASK(2, 0) input selection from 0 to 7 ++ * BIT(3) OPAMP and path EN ++ * BIT(4) Current path measurement ++ * BIT(5) voltage/current path to PAD ++ */ ++#define AIROHA_PCS_ANA_CMN_MPXSELTOP_DC GENMASK(13, 8) + #define AIROHA_PCS_ANA_CMN_EN BIT(0) + #define AIROHA_PCS_ANA_PXP_JCPLL_IB_EXT_EN 0x4 + #define AIROHA_PCS_ANA_JCPLL_CHP_IOFST GENMASK(29, 24) +@@ -389,6 +421,8 @@ + #define AIROHA_PCS_ANA_JCPLL_TCL_KBAND_VREF GENMASK(20, 16) + #define AIROHA_PCS_ANA_JCPLL_SPARE_L GENMASK(15, 8) + #define AIROHA_PCS_ANA_JCPLL_SPARE_L_LDO BIT(5) ++#define AIROHA_PCS_ANA_PXP_JCPLL_FREQ_MEAS_EN 0x4c ++#define AIROHA_PCS_ANA_TXPLL_IB_EXT_EN BIT(24) + #define AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS 0x50 + #define AIROHA_PCS_ANA_TXPLL_LPF_BC GENMASK(28, 24) + #define AIROHA_PCS_ANA_TXPLL_LPF_BR GENMASK(20, 16) +@@ -412,6 +446,9 @@ + #define AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_1 0x3 + #define AIROHA_PCS_ANA_TXPLL_POSTDIV_EN BIT(8) + #define AIROHA_PCS_ANA_TXPLL_KBAND_KS GENMASK(1, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_PHY_CK1_EN 0x60 ++#define AIROHA_PCS_ANA_TXPLL_PHY_CK2_EN BIT(8) ++#define AIROHA_PCS_ANA_TXPLL_PHY_CK1_EN BIT(0) + #define AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL 0x64 + #define AIROHA_PCS_ANA_TXPLL_PLL_RSTB BIT(24) + #define AIROHA_PCS_ANA_TXPLL_RST_DLY GENMASK(18, 16) +@@ -477,16 +514,41 @@ + #define AIROHA_PCS_ANA_TXPLL_LDO_VCO_OUT GENMASK(25, 24) + #define AIROHA_PCS_ANA_TXPLL_LDO_OUT GENMASK(17, 16) + #define AIROHA_PCS_ANA_TXPLL_SSC_PERIOD GENMASK(15, 0) ++#define AIROHA_PCS_ANA_PXP_TXPLL_VTP_EN 0x88 ++#define AIROHA_PCS_ANA_TXPLL_VTP GENMASK(10, 8) ++#define AIROHA_PCS_ANA_TXPLL_VTP_EN BIT(0) + #define AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF 0x94 ++#define AIROHA_PCS_ANA_TXPLL_POSTDIV_D256_EN BIT(25) /* 0: 128 1: 256 */ ++#define AIROHA_PCS_ANA_TXPLL_VCO_KBAND_MEAS_EN BIT(24) ++#define AIROHA_PCS_ANA_TXPLL_FREQ_MEAS_EN BIT(16) ++#define AIROHA_PCS_ANA_TXPLL_VREF_SEL BIT(8) ++#define AIROHA_PCS_ANA_TXPLL_VREF_SEL_VBG FIELD_PREP_CONST(AIROHA_PCS_ANA_TXPLL_VREF_SEL, 0x0) ++#define AIROHA_PCS_ANA_TXPLL_VREF_SEL_AVDD FIELD_PREP_CONST(AIROHA_PCS_ANA_TXPLL_VREF_SEL, 0x1) + #define AIROHA_PCS_ANA_TXPLL_TCL_KBAND_VREF GENMASK(4, 0) ++#define AN7583_PCS_ANA_PXP_TXPLL_CHP_DOUBLE_EN 0x98 ++#define AIROHA_PCS_ANA_TXPLL_SPARE_L BIT(0) /* ICHP_DOUBLE */ ++#define AIROHA_PCS_ANA_PXP_PLL_MONCLK_SEL 0xa0 ++#define AIROHA_PCS_ANA_TDC_AUTOEN BIT(24) ++#define AIROHA_PCS_ANA_PXP_TDC_SYNC_CK_SEL 0xa8 ++#define AIROHA_PCS_ANA_PLL_LDO_CKDRV_VSEL GENMASK(17, 16) ++#define AIROHA_PCS_ANA_PLL_LDO_CKDRV_EN BIT(8) ++#define AIROHA_PCS_ANA_PXP_TX_TXLBRC_EN 0xc0 ++#define AIROHA_PCS_ANA_TX_TERMCAL_VREF_L GENMASK(26, 24) ++#define AIROHA_PCS_ANA_TX_TERMCAL_VREF_H GENMASK(18, 16) + #define AIROHA_PCS_ANA_PXP_TX_CKLDO_EN 0xc4 + #define AIROHA_PCS_ANA_TX_DMEDGEGEN_EN BIT(24) + #define AIROHA_PCS_ANA_TX_CKLDO_EN BIT(0) ++#define AIROHA_PCS_ANA_PXP_TX_TERMCAL_SELPN 0xc8 ++#define AIROHA_PCS_ANA_TX_TDC_CK_SEL GENMASK(17, 16) + #define AIROHA_PCS_ANA_PXP_RX_BUSBIT_SEL 0xcc + #define AIROHA_PCS_ANA_RX_PHY_CK_SEL_FORCE BIT(24) + #define AIROHA_PCS_ANA_RX_PHY_CK_SEL BIT(16) + #define AIROHA_PCS_ANA_RX_PHY_CK_SEL_FROM_PR 0x0 + #define AIROHA_PCS_ANA_RX_PHY_CK_SEL_FROM_DES 0x1 ++#define AIROHA_PCS_ANA_RX_BUSBIT_SEL_FORCE BIT(8) ++#define AIROHA_PCS_ANA_RX_BUSBIT_SEL BIT(0) ++#define AIROHA_PCS_ANA_RX_BUSBIT_SEL_8BIT FIELD_PREP_CONST(AIROHA_PCS_ANA_RX_BUSBIT_SEL, 0x0) ++#define AIROHA_PCS_ANA_RX_BUSBIT_SEL_16BIT FIELD_PREP_CONST(AIROHA_PCS_ANA_RX_BUSBIT_SEL, 0x1) + #define AIROHA_PCS_ANA_PXP_RX_REV_0 0xd4 + #define AIROHA_PCS_ANA_RX_REV_1 GENMASK(31, 16) + #define AIROHA_PCS_ANA_REV_1_FE_EQ_BIAS_CTRL GENMASK(30, 28) +@@ -494,6 +556,16 @@ + #define AIROHA_PCS_ANA_REV_1_FE_BUF2_BIAS_CTRL GENMASK(22, 20) + #define AIROHA_PCS_ANA_REV_1_SIGDET_ILEAK GENMASK(19, 18) + #define AIROHA_PCS_ANA_REV_1_FECUR_PWDB BIT(16) ++#define AIROHA_PCS_ANA_RX_REV_0 GENMASK(15, 0) ++#define AIROHA_PCS_ANA_REV_0_FE_BUF2_BIAS_TYPE GENMASK(13, 12) ++#define AIROHA_PCS_ANA_REV_0_OSCAL_FE_MODE_SET_SEL BIT(11) ++#define AIROHA_PCS_ANA_REV_0_FE_EQ_GAIN_MODE_TRAINING BIT(10) ++#define AIROHA_PCS_ANA_REV_0_FE_BUF_GAIN_MODE_TRAINING GENMASK(9, 8) ++#define AIROHA_PCS_ANA_REV_0_FE_EQ_GAIN_MODE_NORMAL BIT(6) ++#define AIROHA_PCS_ANA_REV_0_FE_BUF_GAIN_MODE_NORMAL GENMASK(5, 4) ++#define AIROHA_PCS_ANA_REV_0_VOS_PNINV GENMASK(3, 2) ++#define AIROHA_PCS_ANA_REV_0_PLEYEBD4 BIT(1) ++#define AIROHA_PCS_ANA_REV_0_PLEYE_XOR_MON_EN BIT(0) + #define AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV 0xd8 + #define AIROHA_PCS_ANA_RX_TDC_CK_SEL BIT(24) + #define AIROHA_PCS_ANA_RX_PHYCK_RSTB BIT(16) +@@ -502,6 +574,8 @@ + #define AIROHA_PCS_ANA_PXP_CDR_PD_PICAL_CKD8_INV 0xdc + #define AIROHA_PCS_ANA_CDR_PD_EDGE_DIS BIT(8) + #define AIROHA_PCS_ANA_CDR_PD_PICAL_CKD8_INV BIT(0) ++#define AIROHA_PCS_ANA_PXP_CDR_LPF_BOT_LIM 0xe0 ++#define AIROHA_PCS_ANA_CDR_LPF_BOT_LIM GENMASK(18, 0) + #define AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO 0xe8 + #define AIROHA_PCS_ANA_CDR_LPF_TOP_LIM GENMASK(26, 8) + #define AIROHA_PCS_ANA_CDR_LPF_RATIO GENMASK(1, 0) +@@ -517,6 +591,19 @@ + #define AIROHA_PCS_ANA_CDR_PR_DAC_BAND GENMASK(20, 16) + #define AIROHA_PCS_ANA_CDR_PR_VREG_CKBUF_VAL GENMASK(10, 8) + #define AIROHA_PCS_ANA_CDR_PR_VREG_IBAND_VAL GENMASK(2, 0) ++#define AIROHA_PCS_ANA_PXP_CDR_PR_CKREF_DIV 0x100 ++#define AIROHA_PCS_ANA_CDR_PR_RSTB_BYPASS BIT(16) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV GENMASK(1, 0) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV_1 FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV, 0x0) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV_2 FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV, 0x1) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV_4 FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV, 0x2) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV_X FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV, 0x3) ++#define AIROHA_PCS_ANA_PXP_CDR_PR_TDC_REF_SEL 0x108 ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1 GENMASK(25, 24) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1_1 FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1, 0x0) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1_2 FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1, 0x1) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1_4 FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1, 0x2) ++#define AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1_X FIELD_PREP_CONST(AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1, 0x3) + #define AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN 0x10c + #define AIROHA_PCS_ANA_RX_DAC_MON GENMASK(28, 24) + #define AIROHA_PCS_ANA_CDR_PR_CAP_EN BIT(19) +@@ -526,6 +613,7 @@ + #define AIROHA_PCS_ANA_CDR_PR_MONPR_EN BIT(0) + #define AIROHA_PCS_ANA_PXP_RX_DAC_RANGE 0x110 + #define AIROHA_PCS_ANA_RX_SIGDET_LPF_CTRL GENMASK(25, 24) ++#define AIROHA_PCS_ANA_RX_DAC_RANGE_EYE GENMASK(9, 8) + #define AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH 0x114 + #define AIROHA_PCS_ANA_RX_FE_50OHMS_SEL GENMASK(25, 24) + #define AIROHA_PCS_ANA_RX_SIGDET_VTH_SEL GENMASK(20, 16) +@@ -964,7 +1052,70 @@ + #define AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_0 0x0 + #define AIROHA_PCS_PMA_SW_LCPLL_EN BIT(24) + #define AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_1 0x4 ++#define AIROHA_PCS_PMA_LCPLL_CK_STB_TIMER GENMASK(31, 24) ++#define AIROHA_PCS_PMA_LCPLL_PCW_MAN_LOAD_TIMER GENMASK(23, 16) ++#define AIROHA_PCS_PMA_LCPLL_EN_TIMER GENMASK(15, 8) + #define AIROHA_PCS_PMA_LCPLL_MAN_PWDB BIT(0) ++#define AIROHA_PCS_PMA_LCPLL_TDC_PW_0 0x10 ++#define AIROHA_PCS_PMA_LCPLL_TDC_DIG_PWDB BIT(0) ++#define AIROHA_PCS_PMA_LCPLL_TDC_PW_5 0x24 ++#define AIROHA_PCS_PMA_LCPLL_TDC_SYNC_IN_MODE BIT(24) ++#define AIROHA_PCS_PMA_LCPLL_AUTOK_TDC BIT(16) ++#define AIROHA_PCS_PMA_LCPLL_TDC_FLT_0 0x28 ++#define AIROHA_PCS_PMA_LCPLL_KI GENMASK(10, 8) ++#define AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC GENMASK(1, 0) ++#define AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC_32 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC, 0x0) ++#define AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC_16 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC, 0x1) ++#define AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC_8 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC, 0x2) ++#define AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC_4 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_PON_RX_CDR_DIVTDC, 0x3) ++#define AIROHA_PCS_PMA_LCPLL_TDC_FLT_1 0x2c ++#define AIROHA_PCS_PMA_LCPLL_A_TDC GENMASK(11, 8) ++#define AIROHA_PCS_PMA_LCPLL_GPON_SEL BIT(0) ++#define AIROHA_PCS_PMA_LCPLL_GPON_SEL_FROM_EPON FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_GPON_SEL, 0x0) ++#define AIROHA_PCS_PMA_LCPLL_GPON_SEL_FROM_GPON FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_GPON_SEL, 0x1) ++#define AIROHA_PCS_PMA_LCPLL_TDC_FLT_3 0x34 ++#define AIROHA_PCS_PMA_LCPLL_NCPO_LOAD BIT(8) ++#define AIROHA_PCS_PMA_LCPLL_NCPO_SHIFT GENMASK(1, 0) ++#define AIROHA_PCS_PMA_LCPLL_TDC_FLT_5 0x3c ++#define AIROHA_PCS_PMA_LCPLL_TDC_AUTOPW_NCPO BIT(16) ++#define AIROHA_PCS_PMA_LCPLL_TDC_FLT_6 0x40 ++#define AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY GENMASK(9, 8) ++#define AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY_SEL FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY, 0x0) ++#define AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY_SEL_D1 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY, 0x1) ++#define AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY_SEL_D2 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY, 0x2) ++#define AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY_SEL_D3 FIELD_PREP_CONST(AIROHA_PCS_PMA_LCPLL_NCPO_CHG_DELAY, 0x3) ++#define AIROHA_PCS_PMA_LCPLL_TDC_PCW_1 0x48 ++#define AIROHA_PCS_PMA_LCPLL_PON_HRDDS_PCW_NCPO_GPON GENMASK(30, 0) ++#define AIROHA_PCS_PMA_LCPLL_TDC_PCW_2 0x4c ++#define AIROHA_PCS_PMA_LCPLL_PON_HRDDS_PCW_NCPO_EPON GENMASK(30, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_0 0x68 ++#define AIROHA_PCS_PMA_X_MAX GENMASK(26, 16) ++#define AIROHA_PCS_PMA_X_MIN GENMASK(10, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_1 0x6c ++#define AIROHA_PCS_PMA_INDEX_MODE BIT(16) ++#define AIROHA_PCS_PMA_Y_MAX GENMASK(14, 8) ++#define AIROHA_PCS_PMA_Y_MIN GENMASK(6, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_2 0x70 ++#define AIROHA_PCS_PMA_EYEDUR GENMASK(19, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_3 0x74 ++#define AIROHA_PCS_PMA_EYE_NEXTPTS BIT(16) ++#define AIROHA_PCS_PMA_EYE_NEXTPTS_TOGGLE BIT(8) ++#define AIROHA_PCS_PMA_EYE_NEXTPTS_SEL BIT(0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYEOPENING_CTRL_0 0x78 ++#define AIROHA_PCS_PMA_EYECNT_VTH GENMASK(15, 8) ++#define AIROHA_PCS_PMA_EYECNT_HTH GENMASK(7, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYEOPENING_CTRL_1 0x7c ++#define AIROHA_PCS_PMA_EO_VTH GENMASK(23, 16) ++#define AIROHA_PCS_PMA_EO_HTH GENMASK(10, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_0 0x80 ++#define AIROHA_PCS_PMA_EYE_MASK GENMASK(31, 24) ++#define AIROHA_PCS_PMA_CNTFOREVER BIT(16) ++#define AIROHA_PCS_PMA_CNTLEN GENMASK(9, 0) ++#define AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1 0x84 ++#define AIROHA_PCS_PMA_FORCE_EYEDUR_INIT_B BIT(24) ++#define AIROHA_PCS_PMA_FORCE_EYEDUR_EN BIT(16) ++#define AIROHA_PCS_PMA_DISB_EYEDUR_INIT_B BIT(8) ++#define AIROHA_PCS_PMA_DISB_EYEDUR_EN BIT(0) + #define AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_2 0x88 + #define AIROHA_PCS_PMA_DATA_SHIFT BIT(8) + #define AIROHA_PCS_PMA_EYECNT_FAST BIT(0) +@@ -996,14 +1147,49 @@ + #define AIROHA_PCS_PMA_RX_BLWC_RDY_EN GENMASK(15, 0) + #define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_6 0x104 + #define AIROHA_PCS_PMA_RX_OS_END GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0 0x108 ++#define AIROHA_PCS_PMA_DISB_RX_FEOS_EN BIT(24) ++#define AIROHA_PCS_PMA_DISB_RX_PDOS_EN BIT(16) ++#define AIROHA_PCS_PMA_DISB_RX_PICAL_EN BIT(8) ++#define AIROHA_PCS_PMA_DISB_RX_OS_EN BIT(0) + #define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1 0x10c + #define AIROHA_PCS_PMA_DISB_RX_RDY BIT(24) ++#define AIROHA_PCS_PMA_DISB_RX_BLWC_EN BIT(16) ++#define AIROHA_PCS_PMA_DISB_RX_OS_RDY BIT(8) ++#define AIROHA_PCS_PMA_DISB_RX_SDCAL_EN BIT(0) ++#define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0 0x110 ++#define AIROHA_PCS_PMA_FORCE_RX_FEOS_EN BIT(24) ++#define AIROHA_PCS_PMA_FORCE_RX_PDOS_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_RX_PICAL_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_RX_OS_EN BIT(0) + #define AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1 0x114 + #define AIROHA_PCS_PMA_FORCE_RX_RDY BIT(24) ++#define AIROHA_PCS_PMA_FORCE_RX_BLWC_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_RX_OS_RDY BIT(8) ++#define AIROHA_PCS_PMA_FORCE_RX_SDCAL_EN BIT(0) ++#define AIROHA_PCS_PMA_PHY_EQ_CTRL_0 0x118 ++#define AIROHA_PCS_PMA_VEO_MASK GENMASK(31, 24) ++#define AIROHA_PCS_PMA_HEO_MASK GENMASK(18, 8) ++#define AIROHA_PCS_PMA_EQ_EN_DELAY GENMASK(7, 0) ++#define AIROHA_PCS_PMA_PHY_EQ_CTRL_1 0x11c ++#define AIROHA_PCS_PMA_B_ZERO_SEL BIT(24) ++#define AIROHA_PCS_PMA_HEO_EMPHASIS BIT(16) ++#define AIROHA_PCS_PMA_A_MGAIN BIT(8) ++#define AIROHA_PCS_PMA_A_LGAIN BIT(0) + #define AIROHA_PCS_PMA_PHY_EQ_CTRL_2 0x120 + #define AIROHA_PCS_PMA_EQ_DEBUG_SEL GENMASK(17, 16) + #define AIROHA_PCS_PMA_FOM_NUM_ORDER GENMASK(12, 8) + #define AIROHA_PCS_PMA_A_SEL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_SS_RX_FEOS 0x144 ++#define AIROHA_PCS_PMA_EQ_FORCE_BLWC_FREEZE BIT(8) ++#define AIROHA_PCS_PMA_LFSEL GENMASK(7, 0) ++#define AIROHA_PCS_PMA_SS_RX_BLWC 0x148 ++#define AIROHA_PCS_PMA_EQ_BLWC_CNT_BOT_LIM GENMASK(29, 23) ++#define AIROHA_PCS_PMA_EQ_BLWC_CNT_TOP_LIM GENMASK(22, 16) ++#define AIROHA_PCS_PMA_EQ_BLWC_GAIN GENMASK(11, 8) ++#define AIROHA_PCS_PMA_EQ_BLWC_POL BIT(0) ++#define AIROHA_PCS_PMA_EQ_BLWC_POL_NORMAL FIELD_PREP_CONST(AIROHA_PCS_PMA_EQ_BLWC_POL, 0x0) ++#define AIROHA_PCS_PMA_EQ_BLWC_POL_INVERSION FIELD_PREP_CONST(AIROHA_PCS_PMA_EQ_BLWC_POL, 0x1) + #define AIROHA_PCS_PMA_SS_RX_FREQ_DET_1 0x14c + #define AIROHA_PCS_PMA_UNLOCK_CYCLECNT GENMASK(31, 16) + #define AIROHA_PCS_PMA_LOCK_CYCLECNT GENMASK(15, 0) +@@ -1022,31 +1208,182 @@ + #define AIROHA_PCS_PMA_FREQLOCK_DET_EN_WAIT FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x2) + #define AIROHA_PCS_PMA_FREQLOCK_DET_EN_NORMAL FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x3) + #define AIROHA_PCS_PMA_FREQLOCK_DET_EN_RX_STATE FIELD_PREP_CONST(AIROHA_PCS_PMA_FREQLOCK_DET_EN, 0x7) ++#define AIROHA_PCS_PMA_RX_PI_CAL 0x15c ++#define AIROHA_PCS_PMA_KPGAIN GENMASK(10, 8) ++#define AIROHA_PCS_PMA_RX_CAL1 0x160 ++#define AIROHA_PCS_PMA_CAL_CYC GENMASK(25, 24) ++#define AIROHA_PCS_PMA_CAL_CYC_63 FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_CYC, 0x0) ++#define AIROHA_PCS_PMA_CAL_CYC_15 FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_CYC, 0x1) ++#define AIROHA_PCS_PMA_CAL_CYC_31 FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_CYC, 0x2) ++#define AIROHA_PCS_PMA_CAL_CYC_127 FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_CYC, 0x3) ++#define AIROHA_PCS_PMA_CAL_STB GENMASK(17, 16) ++#define AIROHA_PCS_PMA_CAL_STB_5US FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_STB, 0x0) ++#define AIROHA_PCS_PMA_CAL_STB_8US FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_STB, 0x1) ++#define AIROHA_PCS_PMA_CAL_STB_16US FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_STB, 0x2) ++#define AIROHA_PCS_PMA_CAL_STB_32US FIELD_PREP_CONST(AIROHA_PCS_PMA_CAL_STB, 0x3) ++#define AIROHA_PCS_PMA_CAL_1US_SET GENMASK(15, 8) ++#define AIROHA_PCS_PMA_SIM_FAST_EN BIT(0) ++#define AIROHA_PCS_PMA_RX_CAL2 0x164 ++#define AIROHA_PCS_PMA_CAL_CYC_TIME GENMASK(17, 16) ++#define AIROHA_PCS_PMA_CAL_OUT_OS GENMASK(11, 8) ++#define AIROHA_PCS_PMA_CAL_OS_PULSE BIT(0) + #define AIROHA_PCS_PMA_SS_RX_SIGDET_1 0x16c + #define AIROHA_PCS_PMA_SIGDET_EN BIT(0) ++#define AIROHA_PCS_PMA_RX_FLL_0 0x170 ++#define AIROHA_PCS_PMA_KBAND_KFC GENMASK(25, 24) ++#define AIROHA_PCS_PMA_KBAND_KFC_8 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_KFC, 0x0) ++#define AIROHA_PCS_PMA_KBAND_KFC_16 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_KFC, 0x1) ++#define AIROHA_PCS_PMA_KBAND_KFC_32 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_KFC, 0x2) ++#define AIROHA_PCS_PMA_KBAND_KFC_64 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_KFC, 0x3) ++#define AIROHA_PCS_PMA_FPKDIV GENMASK(18, 8) ++#define AIROHA_PCS_PMA_KBAND_PREDIV GENMASK(2, 0) ++#define AIROHA_PCS_PMA_KBAND_PREDIV_1 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_PREDIV, 0x0) ++#define AIROHA_PCS_PMA_KBAND_PREDIV_2 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_PREDIV, 0x1) ++#define AIROHA_PCS_PMA_KBAND_PREDIV_4 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_PREDIV, 0x2) ++#define AIROHA_PCS_PMA_KBAND_PREDIV_8 FIELD_PREP_CONST(AIROHA_PCS_PMA_KBAND_PREDIV, 0x3) + #define AIROHA_PCS_PMA_RX_FLL_1 0x174 ++#define AIROHA_PCS_PMA_SYMBOL_WD GENMASK(26, 24) ++#define AIROHA_PCS_PMA_SETTLE_TIME_SEL GENMASK(18, 16) + #define AIROHA_PCS_PMA_LPATH_IDAC GENMASK(10, 0) + #define AIROHA_PCS_PMA_RX_FLL_2 0x178 + #define AIROHA_PCS_PMA_CK_RATE GENMASK(18, 16) + #define AIROHA_PCS_PMA_CK_RATE_20 FIELD_PREP_CONST(AIROHA_PCS_PMA_CK_RATE, 0x0) + #define AIROHA_PCS_PMA_CK_RATE_10 FIELD_PREP_CONST(AIROHA_PCS_PMA_CK_RATE, 0x1) + #define AIROHA_PCS_PMA_CK_RATE_5 FIELD_PREP_CONST(AIROHA_PCS_PMA_CK_RATE, 0x2) ++#define AIROHA_PCS_PMA_AMP GENMASK(10, 8) ++#define AIROHA_PCS_PMA_PRBS_SEL GENMASK(2, 0) + #define AIROHA_PCS_PMA_RX_FLL_5 0x184 + #define AIROHA_PCS_PMA_FLL_IDAC_MIN GENMASK(26, 16) + #define AIROHA_PCS_PMA_FLL_IDAC_MAX GENMASK(10, 0) ++#define AIROHA_PCS_PMA_RX_FLL_6 0x188 ++#define AIROHA_PCS_PMA_LNX_SW_FLL_4_LATCH_EN BIT(24) ++#define AIROHA_PCS_PMA_LNX_SW_FLL_3_LATCH_EN BIT(16) ++#define AIROHA_PCS_PMA_LNX_SW_FLL_2_LATCH_EN BIT(8) ++#define AIROHA_PCS_PMA_LNX_SW_FLL_1_LATCH_EN BIT(0) + #define AIROHA_PCS_PMA_RX_FLL_B 0x19c + #define AIROHA_PCS_PMA_LOAD_EN BIT(0) ++#define AIROHA_PCS_PMA_RX_PDOS_CTRL_0 0x200 ++#define AIROHA_PCS_PMA_SAP_SEL GENMASK(18, 16) ++#define AIROHA_PCS_PMA_SAP_SEL_SHIFT_6 FIELD_PREP_CONST(AIROHA_PCS_PMA_SAP_SEL, 0x0) ++#define AIROHA_PCS_PMA_SAP_SEL_SHIFT_7 FIELD_PREP_CONST(AIROHA_PCS_PMA_SAP_SEL, 0x1) ++#define AIROHA_PCS_PMA_SAP_SEL_SHIFT_8 FIELD_PREP_CONST(AIROHA_PCS_PMA_SAP_SEL, 0x2) ++#define AIROHA_PCS_PMA_SAP_SEL_SHIFT_9 FIELD_PREP_CONST(AIROHA_PCS_PMA_SAP_SEL, 0x3) ++#define AIROHA_PCS_PMA_SAP_SEL_SHIFT_10 FIELD_PREP_CONST(AIROHA_PCS_PMA_SAP_SEL, 0x4) ++#define AIROHA_PCS_PMA_EYE_BLWC_ADD BIT(8) ++#define AIROHA_PCS_PMA_DATA_BLWC_ADD BIT(0) ++#define AIROHA_PCS_PMA_RX_RESET_0 0x204 ++#define AIROHA_PCS_PMA_CAL_RST_B BIT(24) ++#define AIROHA_PCS_PMA_EQ_PI_CAL_RST_B BIT(16) ++#define AIROHA_PCS_PMA_FEOS_RST_B BIT(8) + #define AIROHA_PCS_PMA_RX_RESET_1 0x208 + #define AIROHA_PCS_PMA_SIGDET_RST_B BIT(8) ++#define AIROHA_PCS_PMA_PDOS_RST_B BIT(0) ++#define AIROHA_PCS_PMA_RX_DEBUG_0 0x20c ++#define AIROHA_PCS_PMA_RO_TOGGLE BIT(24) ++#define AIROHA_PCS_PMA_BISTCTL_CONTROL 0x210 ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL GENMASK(4, 0) ++/* AIROHA_PCS_PMA_BISTCTL_PAT_SEL_ALL_0 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x0) */ ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS7 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x1) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS9 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x2) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS15 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x3) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS23 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x4) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS31 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x5) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_HFTP FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x6) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_MFTP FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x7) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_4 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x8) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_5_LFTP FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x9) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_6 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0xa) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_7 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0xb) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_8_LFTP FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0xc) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_9 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0xd) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_10 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0xe) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_SQUARE_11 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0xf) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PROG_80 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x10) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_ALL_1 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x11) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_ALL_0 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x12) ++#define AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS11 FIELD_PREP_CONST(AIROHA_PCS_PMA_BISTCTL_PAT_SEL, 0x13) ++#define AIROHA_PCS_PMA_BISTCTL_ALIGN_PAT 0x214 ++#define AIROHA_PCS_PMA_BISTCTL_POLLUTION 0x220 ++#define AIROHA_PCS_PMA_BIST_TX_DATA_POLLUTION_LATCH BIT(16) ++#define AIROHA_PCS_PMA_BISTCTL_PRBS_INITIAL_SEED 0x224 ++#define AIROHA_PCS_PMA_BISTCTL_PRBS_FAIL_THRESHOLD 0x230 ++#define AIROHA_PCS_PMA_BISTCTL_PRBS_FAIL_THRESHOLD_MASK GENMASK(15, 0) ++#define AIROHA_PCS_PMA_RX_TORGS_DEBUG_2 0x23c ++#define AIROHA_PCS_PMA_PI_CAL_DATA_OUT GENMASK(22, 16) ++#define AIROHA_PCS_PMA_RX_TORGS_DEBUG_5 0x248 ++#define AIROHA_PCS_PMA_VEO_RDY BIT(24) ++#define AIROHA_PCS_PMA_HEO_RDY BIT(16) ++#define AIROHA_PCS_PMA_RX_TORGS_DEBUG_9 0x258 ++#define AIROHA_PCS_PMA_EO_Y_DONE BIT(24) ++#define AIROHA_PCS_PMA_EO_X_DONE BIT(16) ++#define AIROHA_PCS_PMA_RX_TORGS_DEBUG_10 0x25c ++#define AIROHA_PCS_PMA_EYE_EL GENMASK(26, 16) ++#define AIROHA_PCS_PMA_EYE_ER GENMASK(10, 0) + #define AIROHA_PCS_PMA_TX_RST_B 0x260 + #define AIROHA_PCS_PMA_TXCALIB_RST_B BIT(8) + #define AIROHA_PCS_PMA_TX_TOP_RST_B BIT(0) ++#define AIROHA_PCS_PMA_TX_CALIB_0 0x264 ++#define AIROHA_PCS_PMA_TXCALIB_FORCE_TERMP_SEL GENMASK(25, 24) ++#define AIROHA_PCS_PMA_TXCALIB_FORCE_TERMP_SEL_EN BIT(16) ++#define AIROHA_PCS_PMA_RX_TORGS_DEBUG_11 0x290 ++#define AIROHA_PCS_PMA_EYE_EB GENMASK(14, 8) ++#define AIROHA_PCS_PMA_EYE_EU GENMASK(6, 0) ++#define AIROHA_PCS_PMA_RX_FORCE_MODE_0 0x294 ++#define AIROHA_PCS_PMA_FORCE_DA_XPON_CDR_LPF_RSTB BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_XPON_RX_FE_GAIN_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_0 0x300 ++#define AIROHA_PCS_PMA_DISB_DA_XPON_CDR_LPF_RSTB BIT(24) ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_FE_GAIN_CTRL BIT(0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_1 0x304 ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_E0 BIT(24) ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_D1 BIT(16) ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_D0 BIT(8) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_2 0x308 ++#define AIROHA_PCS_PMA_DISB_DA_XPON_CDR_PR_PIEYE BIT(24) ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_FE_VOS BIT(16) ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_EYE BIT(8) ++#define AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_E1 BIT(0) ++#define AIROHA_PCS_PMA_RX_FORCE_MODE_3 0x30c ++#define AIROHA_PCS_PMA_FORCE_EQ_PI_CAL_RDY BIT(0) ++#define AIROHA_PCS_PMA_RX_FORCE_MODE_6 0x318 ++#define AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_EYECNT_RDY BIT(0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_3 0x31c ++#define AIROHA_PCS_PMA_DISB_RQ_PI_CAL_RDY BIT(0) + #define AIROHA_PCS_PMA_RX_DISB_MODE_4 0x320 + #define AIROHA_PCS_PMA_DISB_BLWC_OFFSET BIT(24) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_5 0x324 ++#define AIROHA_PCS_PMA_DISB_RX_OR_PICAL_EN BIT(24) ++#define AIROHA_PCS_PMA_DISB_EYECNT_RDY BIT(16) ++#define AIROHA_PCS_PMA_RX_FORCE_MODE_7 0x328 ++#define AIROHA_PCS_PMA_FORCE_PDOS_RX_RST_B BIT(16) ++#define AIROHA_PCS_PMA_FORCE_RX_AND_PICAL_RSTB BIT(8) ++#define AIROHA_PCS_PMA_FORCE_REF_AND_PICAL_RSTB BIT(0) ++#define AIROHA_PCS_PMA_RX_FORCE_MODE_8 0x32c ++#define AIROHA_PCS_PMA_FORCE_EYECNT_RX_RST_B BIT(24) ++#define AIROHA_PCS_PMA_FORCE_FEOS_RX_RST_B BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SDCAL_REF_RST_B BIT(8) ++#define AIROHA_PCS_PMA_FORCE_BLWC_RX_RST_B BIT(0) + #define AIROHA_PCS_PMA_RX_FORCE_MODE_9 0x330 ++#define AIROHA_PCS_PMA_FORCE_EYE_TOP_EN BIT(16) ++#define AIROHA_PCS_PMA_FORCE_EYE_RESET_PLU_O BIT(8) + #define AIROHA_PCS_PMA_FORCE_FBCK_LOCK BIT(0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_6 0x334 ++#define AIROHA_PCS_PMA_DISB_PDOS_RX_RST_B BIT(16) ++#define AIROHA_PCS_PMA_DISB_RX_AND_PICAL_RSTB BIT(8) ++#define AIROHA_PCS_PMA_DISB_REF_AND_PICAL_RSTB BIT(0) ++#define AIROHA_PCS_PMA_RX_DISB_MODE_7 0x338 ++#define AIROHA_PCS_PMA_DISB_EYECNT_RX_RST_B BIT(24) ++#define AIROHA_PCS_PMA_DISB_FEOS_RX_RST_B BIT(16) ++#define AIROHA_PCS_PMA_DISB_SDCAL_REF_RST_B BIT(8) ++#define AIROHA_PCS_PMA_DISB_BLWC_RX_RST_B BIT(0) + #define AIROHA_PCS_PMA_RX_DISB_MODE_8 0x33c ++#define AIROHA_PCS_PMA_DISB_EYE_TOP_EN BIT(16) ++#define AIROHA_PCS_PMA_DISB_EYE_RESET_PLU_O BIT(8) + #define AIROHA_PCS_PMA_DISB_FBCK_LOCK BIT(0) ++#define AIROHA_PCS_PMA_SS_BIST_1 0x344 ++#define AIROHA_PCS_PMA_LNX_BISTCTL_BIT_ERROR_RST_SEL BIT(24) ++#define AIROHA_PCS_PMA_ANLT_PX_LNX_LT_LOS BIT(0) + #define AIROHA_PCS_PMA_SS_DA_XPON_PWDB_0 0x34c + #define AIROHA_PCS_PMA_XPON_CDR_PD_PWDB BIT(24) + #define AIROHA_PCS_PMA_XPON_CDR_PR_PIEYE_PWDB BIT(16) +@@ -1069,7 +1406,11 @@ + #define AIROHA_PCS_PMA_PLL_LOCK_TARGET_BEG GENMASK(15, 0) + #define AIROHA_PCS_PMA_PLL_TDC_FREQDET_3 0x39c + #define AIROHA_PCS_PMA_PLL_LOCK_LOCKTH GENMASK(11, 8) ++#define AIROHA_PCS_PMA_ADD_CLKPATH_RST_0 0x410 ++#define AIROHA_PCS_PMA_CLKPATH_RSTB_CK BIT(8) ++#define AIROHA_PCS_PMA_CLKPATH_RST_EN BIT(0) + #define AIROHA_PCS_PMA_ADD_XPON_MODE_1 0x414 ++#define AIROHA_PCS_PMA_TX_BIST_GEN_EN BIT(16) + #define AIROHA_PCS_PMA_XFI_RX_MODE GENMASK(11, 9) + #define AIROHA_PCS_PMA_XFI_RX_MODE_10G3 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x0) + #define AIROHA_PCS_PMA_XFI_RX_MODE_5G15 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_RX_MODE, 0x1) +@@ -1085,7 +1426,26 @@ + #define AIROHA_PCS_PMA_XFI_TX_MODE_2G57 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x3) + #define AIROHA_PCS_PMA_XFI_TX_MODE_3G12 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x4) + #define AIROHA_PCS_PMA_XFI_TX_MODE_1G25 FIELD_PREP_CONST(AIROHA_PCS_PMA_XFI_TX_MODE, 0x5) ++#define AIROHA_PCS_PMA_ADD_RX2ANA_1 0x424 ++#define AIROHA_PCS_PMA_RX_DAC_E0 GENMASK(30, 24) ++#define AIROHA_PCS_PMA_RX_DAC_D1 GENMASK(22, 16) ++#define AIROHA_PCS_PMA_RX_DAC_D0 GENMASK(14, 8) ++#define AIROHA_PCS_PMA_RX_DAC_EYE GENMASK(6, 0) ++#define AIROHA_PCS_PMA_ADD_RX2ANA_2 0x428 ++#define AIROHA_PCS_PMA_RX_FEOS_OUT GENMASK(13, 8) ++#define AIROHA_PCS_PMA_RX_DAC_E1 GENMASK(6, 0) ++#define AIROHA_PCS_PMA_PON_TX_COUNTER_0 0x440 ++#define AIROHA_PCS_PMA_TXCALIB_5US GENMASK(31, 16) ++#define AIROHA_PCS_PMA_TXCALIB_50US GENMASK(15, 0) ++#define AIROHA_PCS_PMA_PON_TX_COUNTER_1 0x444 ++#define AIROHA_PCS_PMA_TX_HSDATA_EN_WAIT GENMASK(31, 16) ++#define AIROHA_PCS_PMA_TX_CK_EN_WAIT GENMASK(15, 0) ++#define AIROHA_PCS_PMA_PON_TX_COUNTER_2 0x448 ++#define AIROHA_PCS_PMA_TX_SERDES_RDY_WAIT GENMASK(31, 16) ++#define AIROHA_PCS_PMA_TX_POWER_ON_WAIT GENMASK(15, 0) + #define AIROHA_PCS_PMA_SW_RST_SET 0x460 ++#define AIROHA_PCS_PMA_SW_XFI_RXMAC_RST_N BIT(17) ++#define AIROHA_PCS_PMA_SW_XFI_TXMAC_RST_N BIT(16) + #define AIROHA_PCS_PMA_SW_HSG_RXPCS_RST_N BIT(11) + #define AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N BIT(10) + #define AIROHA_PCS_PMA_SW_XFI_RXPCS_BIST_RST_N BIT(9) +@@ -1098,17 +1458,32 @@ + #define AIROHA_PCS_PMA_SW_TX_RST_N BIT(2) + #define AIROHA_PCS_PMA_SW_RX_RST_N BIT(1) + #define AIROHA_PCS_PMA_SW_RX_FIFO_RST_N BIT(0) ++#define AIROHA_PCS_PMA_TX_DLY_CTRL 0x468 ++#define AIROHA_PCS_PMA_OUTBEN_DATA_MODE GENMASK(30, 28) ++#define AIROHA_PCS_PMA_TX_BEN_EXTEN_FTUNE GENMASK(23, 16) ++#define AIROHA_PCS_PMA_TX_DLY_BEN_FTUNE GENMASK(14, 8) ++#define AIROHA_PCS_PMA_TX_DLY_DATA_FTUNE GENMASK(6, 0) + #define AIROHA_PCS_PMA_XPON_INT_EN_3 0x474 + #define AIROHA_PCS_PMA_RX_SIGDET_INT_EN BIT(16) + #define AIROHA_PCS_PMA_XPON_INT_STA_3 0x47c + #define AIROHA_PCS_PMA_RX_SIGDET_INT BIT(16) + #define AIROHA_PCS_PMA_RX_EXTRAL_CTRL 0x48c ++/* 4ref_ck step: ++ * - 0x1 4ref_ck ++ * - 0x2 8ref_ck ++ * - 0x3 12ref_ck ++ * ... ++ */ ++#define AIROHA_PCS_PMA_L2D_TRIG_EQ_EN_TIME GENMASK(15, 8) ++#define AIROHA_PCS_PMA_OS_RDY_LATCH BIT(1) + #define AIROHA_PCS_PMA_DISB_LEQ BIT(0) + #define AIROHA_PCS_PMA_RX_FREQDET 0x530 + #define AIROHA_PCS_PMA_FL_OUT GENMASK(31, 16) + #define AIROHA_PCS_PMA_FBCK_LOCK BIT(0) + #define AIROHA_PCS_PMA_XPON_TX_RATE_CTRL 0x580 + #define AIROHA_PCS_PMA_PON_TX_RATE_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_MD32_MEM_CLK_CTRL 0x60c ++#define AIROHA_PCS_PMA_MD32PM_CK_SEL GENMASK(31, 0) + #define AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN 0x768 + #define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL BIT(24) + #define AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL GENMASK(19, 16) +@@ -1131,8 +1506,13 @@ + #define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C1 BIT(8) + #define AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C1 GENMASK(5, 0) + #define AIROHA_PCS_PMA_PXP_TX_RATE_CTRL 0x784 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE GENMASK(22, 16) + #define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_RATE_CTRL BIT(8) + #define AIROHA_PCS_PMA_FORCE_DA_TX_RATE_CTRL GENMASK(1, 0) ++#define AIROHA_PCS_PMA_PXP_CDR_PR_FLL_COR 0x790 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_DAC_EYE BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_DAC_EYE GENMASK(22, 16) + #define AIROHA_PCS_PMA_PXP_CDR_PR_IDAC 0x794 + #define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW BIT(24) + #define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_IDAC BIT(16) +@@ -1152,6 +1532,9 @@ + #define AIROHA_PCS_PMA_PXP_AEQ_RSTB 0x814 + #define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_INJCK_SEL BIT(24) + #define AIROHA_PCS_PMA_FORCE_DA_CDR_INJCK_SEL BIT(16) ++#define AIROHA_PCS_PMA_PXP_AEQ_EN 0x808 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_AEQ_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_AEQ_EN BIT(0) + #define AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA 0x818 + #define AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB BIT(24) + #define AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB BIT(16) +@@ -1177,6 +1560,14 @@ + #define AIROHA_PCS_PMA_FORCE_DA_JCPLL_EN BIT(16) + #define AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_CKOUT_EN BIT(8) + #define AIROHA_PCS_PMA_FORCE_DA_JCPLL_CKOUT_EN BIT(0) ++#define AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN_RSTB 0x83c ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_CKON BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_CKON BIT(16) ++#define AIROHA_PCS_PMA_PXP_RX_OSCAL_EN 0x840 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_RSTB BIT(24) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_RSTB BIT(16) ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_EN BIT(0) + #define AIROHA_PCS_PMA_PXP_RX_SCAN_RST_B 0x84c + #define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_PWDB BIT(24) + #define AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_PWDB BIT(16) +@@ -1187,6 +1578,12 @@ + #define AIROHA_PCS_PMA_FORCE_DA_TXPLL_EN BIT(16) + #define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_CKOUT_EN BIT(8) + #define AIROHA_PCS_PMA_FORCE_DA_TXPLL_CKOUT_EN BIT(0) ++#define AIROHA_PCS_PMA_PXP_TXPLL_KBAND_LOAD_EN 0x858 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_KBAND_LOAD_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TXPLL_KBAND_LOAD_EN BIT(0) ++#define AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW_CHG 0x864 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW_CHG BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW_CHG BIT(0) + #define AIROHA_PCS_PMA_PXP_TX_ACJTAG_EN 0x874 + #define AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_SEL BIT(24) + #define AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_SEL BIT(16) +@@ -1198,6 +1595,14 @@ + #define AIROHA_PCS_PMA_FORCE_DA_RX_PDOSCAL_EN BIT(16) + #define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PWDB BIT(8) + #define AIROHA_PCS_PMA_FORCE_DA_RX_FE_PWDB BIT(0) ++#define AIROHA_PCS_PMA_PXP_RX_SIGDET_CAL_EN 0x898 ++#define AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_CAL_EN BIT(8) ++#define AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_CAL_EN BIT(0) ++#define AIROHA_PCS_PMA_DIG_RESERVE_12 0x8b8 ++#define AIROHA_PCS_PMA_RESERVE_12_FEOS_0 BIT(0) ++#define AIROHA_PCS_PMA_DIG_RESERVE_24 0x8fc ++#define AIROHA_PCS_PMA_FORCE_RX_GEARBOX BIT(12) ++#define AIROHA_PCS_PMA_FORCE_SEL_RX_GEARBOX BIT(8) + #define AIROHA_PCS_PMA_DIG_RESERVE_29 0x910 + #define AIROHA_PCS_PMA_2L_TX_RATE_CTRL GENMASK(1, 0) + #define AIROHA_PCS_PMA_2L_RX_RATE_CTRL GENMASK(5, 4) +@@ -1205,6 +1610,19 @@ + #define AIROHA_PCS_MAX_CALIBRATION_TRY 50 + #define AIROHA_PCS_MAX_NUM_RSTS 2 + ++enum pon_eo_buf_vals { ++ EYE_EU, ++ EYE_EB, ++ DAC_D0, ++ DAC_D1, ++ DAC_E0, ++ DAC_E1, ++ DAC_EYE, ++ FEOS, ++ ++ EO_BUF_MAX, ++}; ++ + enum xfi_port_type { + AIROHA_PCS_ETH, + AIROHA_PCS_PON, +@@ -1254,6 +1672,11 @@ struct airoha_pcs_match_data { + int num_port; + enum xfi_port_type port_type; + ++ bool hibernation_workaround; ++ bool usxgmii_ber_time_fixup; ++ bool usxgmii_rx_gb_out_vld_tweak; ++ bool usxgmii_xfi_mode_sel; ++ + int (*alloc_regmap_fields)(struct airoha_pcs_priv *priv); + int (*bringup)(struct airoha_pcs_priv *priv, + int index, phy_interface_t interface); +@@ -1307,3 +1730,30 @@ static inline int an7581_pcs_rxlock_work + return 0; + } + #endif ++ ++#ifdef CONFIG_PCS_AIROHA_AN7583 ++int an7583_pcs_common_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface); ++ ++int an7583_pcs_usb_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface); ++ ++void an7583_pcs_common_phya_link_up(struct airoha_pcs_priv *priv, int index); ++#else ++static inline int an7583_pcs_common_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int an7583_pcs_usb_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void an7583_pcs_common_phya_link_up(struct airoha_pcs_priv *priv, ++ int index) ++{ ++} ++#endif +--- /dev/null ++++ b/drivers/net/pcs/airoha/pcs-an7583.c +@@ -0,0 +1,2349 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024 AIROHA Inc ++ * Author: Christian Marangi ++ */ ++#include ++#include ++#include ++ ++#include "pcs-airoha.h" ++ ++static void an7583_pcs_dig_reset_hold(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_RX_FIFO_RST_N | ++ AIROHA_PCS_PMA_SW_TX_FIFO_RST_N); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_ALLPCS_RST_N); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_TX_RST_N | ++ AIROHA_PCS_PMA_SW_RX_RST_N); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_PMA_RST_N); ++ ++ usleep_range(50, 100); ++} ++ ++static void an7583_pcs_dig_reset_release(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_TX_RST_N | ++ AIROHA_PCS_PMA_SW_RX_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_PMA_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_RX_FIFO_RST_N | ++ AIROHA_PCS_PMA_SW_TX_FIFO_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_ALLPCS_RST_N); ++ ++ usleep_range(100, 200); ++} ++ ++static void an7583_pcs_common_phya_txpll(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 pcw, tdc_pcw; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: /* DS(RX)_1.25G / US(TX)_1.25G*/ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ pcw = 0x32000000; ++ tdc_pcw = 0x64000000; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: /* DS(RX)_3.125G / US(TX)_3.125G */ ++ pcw = 0x3e800000; ++ tdc_pcw = 0x7d000000; ++ break; ++ case PHY_INTERFACE_MODE_5GBASER: /* DS(RX)_5.15625G / US(TX)_5.15625G */ ++ case PHY_INTERFACE_MODE_USXGMII: /* DS(RX)_10.31252G / US(TX)_10.3125G */ ++ case PHY_INTERFACE_MODE_10GBASER: ++ pcw = 0x33900000; ++ tdc_pcw = 0x67200000; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_IDAC, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_FLT_3, ++ AIROHA_PCS_PMA_LCPLL_NCPO_LOAD); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW, ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW, pcw)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_PCW_1, ++ AIROHA_PCS_PMA_LCPLL_PON_HRDDS_PCW_NCPO_GPON, ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_PON_HRDDS_PCW_NCPO_GPON, ++ tdc_pcw)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_PCW_2, ++ AIROHA_PCS_PMA_LCPLL_PON_HRDDS_PCW_NCPO_EPON, ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_PON_HRDDS_PCW_NCPO_EPON, ++ tdc_pcw)); ++} ++ ++static void an7583_pcs_common_phya_tx(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 fir_cn1, fir_c0b, fir_c1, fir_c2; ++ u32 tx_ben_exten_ftune; ++ u32 tx_dly_data_ftune; ++ u32 tx_dly_ben_ftune; ++ u32 ckin_divisor; ++ u32 tx_rate_ctrl; ++ ++ if (data->port_type == AIROHA_PCS_ETH || ++ data->port_type == AIROHA_PCS_PON || ++ data->port_type == AIROHA_PCS_PCIE) ++ tx_ben_exten_ftune = 0x2; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ ckin_divisor = BIT(1); ++ tx_rate_ctrl = BIT(0); ++ fir_cn1 = 0; ++ fir_c0b = 8; ++ fir_c1 = 0; ++ fir_c2 = 0; ++ ++ if (data->port_type == AIROHA_PCS_PON) { ++ tx_ben_exten_ftune = 0x7; ++ tx_dly_ben_ftune = 0x2; ++ tx_dly_data_ftune = 0x6; ++ } ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ ckin_divisor = BIT(2); ++ tx_rate_ctrl = BIT(0); ++ fir_cn1 = 0; ++ fir_c0b = 8; ++ fir_c1 = 1; ++ fir_c2 = 0; ++ if (data->port_type == AIROHA_PCS_PON) ++ tx_ben_exten_ftune = 0x2; ++ break; ++ case PHY_INTERFACE_MODE_5GBASER: ++ ckin_divisor = BIT(2); ++ tx_rate_ctrl = BIT(1); ++ fir_cn1 = 0; ++ fir_c0b = 14; ++ fir_c1 = 4; ++ fir_c2 = 0; ++ if (data->port_type == AIROHA_PCS_PON) ++ tx_ben_exten_ftune = 0x2; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ ckin_divisor = BIT(2) | BIT(0); ++ tx_rate_ctrl = BIT(1); ++ fir_cn1 = 0; ++ fir_c0b = 14; ++ fir_c1 = 4; ++ fir_c2 = 0; ++ ++ if (data->port_type == AIROHA_PCS_PON) { ++ tx_ben_exten_ftune = 0x16; ++ tx_dly_ben_ftune = 0xd; ++ tx_dly_data_ftune = 0x30; ++ } ++ ++ break; ++ default: ++ return; ++ } ++ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TX_CKLDO_EN, ++ AIROHA_PCS_ANA_TX_DMEDGEGEN_EN | ++ AIROHA_PCS_ANA_TX_CKLDO_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CMN_EN, ++ AIROHA_PCS_ANA_CMN_VREFSEL | ++ AIROHA_PCS_ANA_CMN_MPXSELTOP_DC | ++ AIROHA_PCS_ANA_CMN_EN, ++ AIROHA_PCS_ANA_CMN_VREFSEL_9V | ++ FIELD_PREP(AIROHA_PCS_ANA_CMN_MPXSELTOP_DC, 0x1) | ++ AIROHA_PCS_ANA_CMN_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_ACJTAG_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_SEL); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_FIR_C0B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_CN1 | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_CN1 | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C0B | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C0B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_CN1 | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_CN1, fir_cn1) | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C0B | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C0B, fir_c0b)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_FIR_C1, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C2 | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C2 | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C1 | ++ AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C1, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C2 | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C2, fir_c2) | ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_FIR_C1 | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_FIR_C1, fir_c1)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_TERM_SEL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_DIVISOR | ++ AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_DIVISOR, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_CKIN_DIVISOR | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_CKIN_DIVISOR, ++ ckin_divisor)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_RATE_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TX_RATE_CTRL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_TX_RATE_CTRL, ++ tx_rate_ctrl)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_XPON_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_PON_TX_RATE_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_PON_TX_RATE_CTRL, ++ tx_rate_ctrl)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_TX_DLY_CTRL, ++ AIROHA_PCS_PMA_TX_BEN_EXTEN_FTUNE, ++ FIELD_PREP(AIROHA_PCS_PMA_TX_BEN_EXTEN_FTUNE, tx_ben_exten_ftune)); ++ ++ if (data->port_type == AIROHA_PCS_PON) { ++ if (interface == PHY_INTERFACE_MODE_SGMII || interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_USXGMII || interface == PHY_INTERFACE_MODE_10GBASER) ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_TX_DLY_CTRL, ++ AIROHA_PCS_PMA_TX_DLY_BEN_FTUNE | ++ AIROHA_PCS_PMA_TX_DLY_DATA_FTUNE, ++ FIELD_PREP(AIROHA_PCS_PMA_TX_DLY_BEN_FTUNE, tx_dly_ben_ftune) | ++ FIELD_PREP(AIROHA_PCS_PMA_TX_DLY_DATA_FTUNE, tx_dly_data_ftune)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_MD32_MEM_CLK_CTRL, ++ AIROHA_PCS_PMA_MD32PM_CK_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_MD32PM_CK_SEL, 0x3)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_TX_DLY_CTRL, ++ AIROHA_PCS_PMA_OUTBEN_DATA_MODE, ++ FIELD_PREP(AIROHA_PCS_PMA_OUTBEN_DATA_MODE, 0x1)); ++ } ++} ++ ++static void an7583_pcs_common_phya_rx(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 cdr_pr_beta_dac; ++ u32 rx_force_mode_0; ++ u32 dig_reserve_0; ++ u32 rx_rate_ctrl; ++ u32 fe_gain_ctrl; ++ u32 busbit_sel; ++ u32 phyck_sel; ++ u32 phyck_div; ++ u32 lpf_ratio; ++ u32 rx_rev0; ++ u32 osr; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ dig_reserve_0 = 0x300; ++ cdr_pr_beta_dac = 0x8; ++ phyck_sel = 0x1; ++ phyck_div = 0x29; ++ lpf_ratio = 0x3; ++ osr = 0x3; ++ rx_rate_ctrl = 0x0; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ dig_reserve_0 = 0x300; ++ cdr_pr_beta_dac = 0x6; ++ phyck_sel = 0x1; ++ phyck_div = 0xb; ++ lpf_ratio = 0x1; ++ osr = 0x1; ++ rx_rate_ctrl = 0x0; ++ break; ++ case PHY_INTERFACE_MODE_5GBASER: ++ dig_reserve_0 = 0x400; ++ cdr_pr_beta_dac = 0x8; ++ phyck_sel = 0x2; ++ phyck_div = 0x42; ++ lpf_ratio = 0x1; ++ osr = 0x1; ++ rx_rate_ctrl = 0x2; ++ break; ++ case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ dig_reserve_0 = 0x100; ++ cdr_pr_beta_dac = 0x8; ++ phyck_sel = 0x2; ++ phyck_div = 0x42; ++ lpf_ratio = 0x0; ++ osr = 0x0; ++ rx_rate_ctrl = 0x2; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_REV_0, ++ AIROHA_PCS_ANA_REV_1_FE_BUF1_BIAS_CTRL | ++ AIROHA_PCS_ANA_REV_1_FE_BUF2_BIAS_CTRL | ++ AIROHA_PCS_ANA_REV_1_SIGDET_ILEAK, ++ FIELD_PREP(AIROHA_PCS_ANA_REV_1_FE_BUF1_BIAS_CTRL, BIT(2)) | ++ FIELD_PREP(AIROHA_PCS_ANA_REV_1_FE_BUF2_BIAS_CTRL, BIT(2)) | ++ FIELD_PREP(AIROHA_PCS_ANA_REV_1_SIGDET_ILEAK, 0x0)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_OSCAL_WATCH_WNDW, ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE, ++ FIELD_PREP(AIROHA_PCS_ANA_RX_OSCAL_FORCE, ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA2VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA2IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA1VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_VGA1IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE2VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE2IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE1VOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_CTLE1IOS | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_LVSH | ++ AIROHA_PCS_ANA_RX_OSCAL_FORCE_COMPOS)); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PD_PICAL_CKD8_INV, ++ AIROHA_PCS_ANA_CDR_PD_EDGE_DIS | ++ AIROHA_PCS_ANA_CDR_PD_PICAL_CKD8_INV); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_AEQ_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_INJCK_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_INJCK_SEL); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_FE_GAIN_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_GAIN_CTRL); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_DIG_RESERVE_12, ++ AIROHA_PCS_PMA_RESERVE_12_FEOS_0); ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GBASER) { ++ rx_rev0 = FIELD_PREP(AIROHA_PCS_ANA_REV_0_FE_BUF2_BIAS_TYPE, 0x1) | ++ FIELD_PREP(AIROHA_PCS_ANA_REV_0_FE_BUF_GAIN_MODE_NORMAL, 0x3); ++ fe_gain_ctrl = 0x1; ++ rx_force_mode_0 = 0x1; ++ } else { ++ rx_rev0 = FIELD_PREP(AIROHA_PCS_ANA_REV_0_FE_BUF2_BIAS_TYPE, 0x1) | ++ AIROHA_PCS_ANA_REV_0_OSCAL_FE_MODE_SET_SEL | ++ BIT(7) | /* FIXME: Missing documentation for this BIT */ ++ FIELD_PREP(AIROHA_PCS_ANA_REV_0_FE_BUF_GAIN_MODE_NORMAL, 0x3); ++ fe_gain_ctrl = 0x3; ++ rx_force_mode_0 = 0x3; ++ } ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_REV_0, ++ AIROHA_PCS_ANA_RX_REV_0, rx_rev0); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_FE_GAIN_CTRL, ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, ++ fe_gain_ctrl)); ++ ++ regmap_write(pcs_pma, AIROHA_PCS_PMA_DIG_RESERVE_0, ++ dig_reserve_0); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_0, ++ AIROHA_PCS_PMA_FORCE_DA_XPON_RX_FE_GAIN_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_XPON_RX_FE_GAIN_CTRL, ++ rx_force_mode_0)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_0, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_FE_GAIN_CTRL); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_BETA_DAC, ++ AIROHA_PCS_ANA_CDR_PR_BETA_DAC, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_PR_BETA_DAC, ++ cdr_pr_beta_dac)); ++ ++ if (data->port_type == AIROHA_PCS_ETH && ++ interface == PHY_INTERFACE_MODE_2500BASEX) ++ regmap_update_bits(priv->pcs_ana, ++ AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, ++ AIROHA_PCS_ANA_CDR_PR_DAC_BAND, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_PR_DAC_BAND, ++ 0x6)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, ++ AIROHA_PCS_ANA_RX_PHYCK_SEL, ++ FIELD_PREP(AIROHA_PCS_ANA_RX_PHYCK_SEL, phyck_sel)); ++ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, ++ AIROHA_PCS_ANA_CDR_PR_XFICK_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_BUSBIT_SEL, ++ AIROHA_PCS_ANA_RX_PHY_CK_SEL_FORCE | ++ AIROHA_PCS_ANA_RX_PHY_CK_SEL, ++ AIROHA_PCS_ANA_RX_PHY_CK_SEL_FORCE); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, ++ AIROHA_PCS_ANA_RX_PHYCK_RSTB | ++ AIROHA_PCS_ANA_RX_PHYCK_DIV, ++ AIROHA_PCS_ANA_RX_PHYCK_RSTB | ++ FIELD_PREP(AIROHA_PCS_ANA_RX_PHYCK_DIV, phyck_div)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO, ++ AIROHA_PCS_ANA_CDR_LPF_RATIO, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_LPF_RATIO, ++ lpf_ratio)); ++ ++ if (interface == PHY_INTERFACE_MODE_5GBASER) ++ busbit_sel = AIROHA_PCS_ANA_RX_BUSBIT_SEL_FORCE | ++ AIROHA_PCS_ANA_RX_BUSBIT_SEL_16BIT; ++ else ++ busbit_sel = 0; ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_BUSBIT_SEL, ++ AIROHA_PCS_ANA_RX_BUSBIT_SEL_FORCE | ++ AIROHA_PCS_ANA_RX_BUSBIT_SEL, ++ busbit_sel); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_AEQ_SPEED, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_OSR_SEL | ++ AIROHA_PCS_PMA_FORCE_DA_OSR_SEL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_OSR_SEL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_OSR_SEL, osr)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_XPON_RX_RESERVED_1, ++ AIROHA_PCS_PMA_XPON_RX_RATE_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_XPON_RX_RATE_CTRL, rx_rate_ctrl)); ++} ++ ++static void an7583_pcs_common_phya_ana(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 lpf_br, lpf_bwr; ++ u32 txpll_chp_ibias; ++ bool chp_double_en; ++ u32 tcl_amp_vref; ++ bool sdm_hren; ++ bool sdm_ifm; ++ u32 vco_cfix; ++ bool sdm_di; ++ bool vcodiv; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ if (data->port_type == AIROHA_PCS_PON) { ++ txpll_chp_ibias = 0x18; ++ lpf_br = 0xa; ++ lpf_bwr = 0x16; ++ } else { ++ txpll_chp_ibias = 0x31; ++ lpf_br = 0x5; ++ lpf_bwr = 0xb; ++ } ++ vco_cfix = 0x3; ++ tcl_amp_vref = 0xb; ++ vcodiv = false; ++ sdm_hren = data->port_type == AIROHA_PCS_PON; ++ sdm_ifm = data->port_type == AIROHA_PCS_PON; ++ sdm_di = data->port_type == AIROHA_PCS_PON; ++ chp_double_en = false; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ txpll_chp_ibias = 0x1e; ++ if (data->port_type == AIROHA_PCS_PCIE) { ++ lpf_br = 0xa; ++ lpf_bwr = 0x16; ++ } else { ++ lpf_br = 0x5; ++ lpf_bwr = 0xb; ++ } ++ vco_cfix = 0x0; ++ tcl_amp_vref = 0xe; ++ vcodiv = true; ++ sdm_hren = false; ++ sdm_ifm = false; ++ sdm_di = false; ++ chp_double_en = data->port_type == AIROHA_PCS_PON || ++ data->port_type == AIROHA_PCS_PCIE; ++ break; ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ txpll_chp_ibias = 0x18; ++ lpf_br = 0xa; ++ lpf_bwr = 0x16; ++ sdm_hren = true; ++ vco_cfix = 0x2; ++ tcl_amp_vref = 0xb; ++ vcodiv = false; ++ sdm_ifm = true; ++ sdm_di = true; ++ chp_double_en = false; ++ break; ++ default: ++ return; ++ } ++ ++ if (data->port_type == AIROHA_PCS_PON || ++ data->port_type == AIROHA_PCS_PCIE) ++ /* XPON TDC */ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_PLL_MONCLK_SEL, ++ AIROHA_PCS_ANA_TDC_AUTOEN); ++ ++ /* TXPLL VCO LDO Out */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SSC_PERIOD, ++ AIROHA_PCS_ANA_TXPLL_LDO_VCO_OUT | ++ AIROHA_PCS_ANA_TXPLL_LDO_OUT, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LDO_VCO_OUT, 0x1) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LDO_OUT, 0x1)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_VTP_EN, ++ AIROHA_PCS_ANA_TXPLL_VTP | ++ AIROHA_PCS_ANA_TXPLL_VTP_EN, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VTP, 0x0) | ++ AIROHA_PCS_ANA_TXPLL_VTP_EN); ++ ++ if (data->port_type == AIROHA_PCS_ETH || ++ data->port_type == AIROHA_PCS_PON) ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TDC_SYNC_CK_SEL, ++ AIROHA_PCS_ANA_PLL_LDO_CKDRV_VSEL | ++ AIROHA_PCS_ANA_PLL_LDO_CKDRV_EN, ++ FIELD_PREP(AIROHA_PCS_ANA_PLL_LDO_CKDRV_VSEL, 0x1) | ++ AIROHA_PCS_ANA_PLL_LDO_CKDRV_EN); ++ ++ /* Setup RSTB */ ++ /* FIXME: different order */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_REFIN_INTERNAL, ++ AIROHA_PCS_ANA_TXPLL_PLL_RSTB | ++ AIROHA_PCS_ANA_TXPLL_RST_DLY | ++ AIROHA_PCS_ANA_TXPLL_REFIN_DIV | ++ AIROHA_PCS_ANA_TXPLL_REFIN_INTERNAL, ++ AIROHA_PCS_ANA_TXPLL_PLL_RSTB | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_RST_DLY, 0x4) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_REFIN_DIV, ++ AIROHA_PCS_ANA_TXPLL_REFIN_DIV_1)); ++ ++ /* Setup SDM */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SDM_DI_EN, ++ AIROHA_PCS_ANA_TXPLL_SDM_MODE | ++ AIROHA_PCS_ANA_TXPLL_SDM_IFM | ++ AIROHA_PCS_ANA_TXPLL_SDM_DI_LS | ++ AIROHA_PCS_ANA_TXPLL_SDM_DI_EN, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_SDM_MODE, 0) | ++ (sdm_ifm ? AIROHA_PCS_ANA_TXPLL_SDM_IFM : 0) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_SDM_DI_LS, ++ AIROHA_PCS_ANA_TXPLL_SDM_DI_LS_2_23) | ++ (sdm_di ? AIROHA_PCS_ANA_TXPLL_SDM_DI_EN : 0)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD, ++ AIROHA_PCS_ANA_TXPLL_SDM_HREN | ++ AIROHA_PCS_ANA_TXPLL_SDM_OUT | ++ AIROHA_PCS_ANA_TXPLL_SDM_ORD, ++ (sdm_hren ? AIROHA_PCS_ANA_TXPLL_SDM_HREN : 0) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_SDM_ORD, ++ AIROHA_PCS_ANA_TXPLL_SDM_ORD_3SDM)); ++ ++ /* Setup SSC */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SSC_DELTA1, ++ AIROHA_PCS_ANA_TXPLL_SSC_DELTA | ++ AIROHA_PCS_ANA_TXPLL_SSC_DELTA1, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_SSC_DELTA, 0x0) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_SSC_DELTA1, 0x0)); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SSC_EN, ++ AIROHA_PCS_ANA_TXPLL_SSC_TRI_EN | ++ AIROHA_PCS_ANA_TXPLL_SSC_PHASE_INI | ++ AIROHA_PCS_ANA_TXPLL_SSC_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SSC_PERIOD, ++ AIROHA_PCS_ANA_TXPLL_SSC_PERIOD, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_SSC_PERIOD, 0x0)); ++ ++ regmap_update_bits(priv->pcs_ana, AN7583_PCS_ANA_PXP_TXPLL_CHP_DOUBLE_EN, ++ AIROHA_PCS_ANA_TXPLL_SPARE_L, ++ chp_double_en ? AIROHA_PCS_ANA_TXPLL_SPARE_L : 0); ++ ++ /* Setup LPF */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_CHP_IBIAS, ++ AIROHA_PCS_ANA_TXPLL_LPF_BC | ++ AIROHA_PCS_ANA_TXPLL_LPF_BR | ++ AIROHA_PCS_ANA_TXPLL_CHP_IOFST | ++ AIROHA_PCS_ANA_TXPLL_CHP_IBIAS, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LPF_BC, 0x1f) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LPF_BR, lpf_br) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_CHP_IOFST, 0x0) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_CHP_IBIAS, txpll_chp_ibias)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP, ++ AIROHA_PCS_ANA_TXPLL_LPF_BWC | ++ AIROHA_PCS_ANA_TXPLL_LPF_BWR | ++ AIROHA_PCS_ANA_TXPLL_LPF_BP, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LPF_BWC, 0x18) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LPF_BWR, lpf_bwr) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_LPF_BP, 0x2)); ++ ++ /* Setup VCO */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, ++ AIROHA_PCS_ANA_TXPLL_VCO_CFIX, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VCO_CFIX, vco_cfix)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_VCO_HALFLSB_EN, ++ AIROHA_PCS_ANA_TXPLL_VCO_VCOVAR_BIAS_L | ++ AIROHA_PCS_ANA_TXPLL_VCO_VCOVAR_BIAS_H | ++ AIROHA_PCS_ANA_TXPLL_VCO_TCLVAR | ++ AIROHA_PCS_ANA_TXPLL_VCO_SCAPWR | ++ AIROHA_PCS_ANA_TXPLL_VCO_HALFLSB_EN, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VCO_VCOVAR_BIAS_L, 0x0) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VCO_VCOVAR_BIAS_H, 0x4) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VCO_TCLVAR, 0x4) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VCO_SCAPWR, 0x7) | ++ AIROHA_PCS_ANA_TXPLL_VCO_HALFLSB_EN); ++ ++ /* Setup KBand */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_KBAND_CODE, ++ AIROHA_PCS_ANA_TXPLL_KBAND_KF | ++ AIROHA_PCS_ANA_TXPLL_KBAND_KFC | ++ AIROHA_PCS_ANA_TXPLL_KBAND_DIV | ++ AIROHA_PCS_ANA_TXPLL_KBAND_CODE, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_KBAND_KF, 0x3) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_KBAND_KFC, 0x0) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_KBAND_DIV, 0x2) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_KBAND_CODE, 0xe4)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_KBAND_KS, ++ AIROHA_PCS_ANA_TXPLL_KBAND_KS, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_KBAND_KS, 0x1)); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_LPF_BP, ++ AIROHA_PCS_ANA_TXPLL_KBAND_OPTION); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF, ++ AIROHA_PCS_ANA_TXPLL_VCO_KBAND_MEAS_EN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_KBAND_LOAD_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_KBAND_LOAD_EN | ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_KBAND_LOAD_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_KBAND_LOAD_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_KBAND_KS, ++ AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE | ++ AIROHA_PCS_ANA_TXPLL_POSTDIV_EN, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE, ++ AIROHA_PCS_ANA_TXPLL_MMD_PREDIV_MODE_2) | ++ AIROHA_PCS_ANA_TXPLL_POSTDIV_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_AMP_GAIN, ++ AIROHA_PCS_ANA_TXPLL_TCL_AMP_VREF | ++ AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_TCL_AMP_VREF, tcl_amp_vref) | ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN, ++ AIROHA_PCS_ANA_TXPLL_TCL_AMP_GAIN_4)); ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF, ++ AIROHA_PCS_ANA_TXPLL_POSTDIV_D256_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, ++ AIROHA_PCS_ANA_TXPLL_VCODIV, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_VCODIV, ++ vcodiv ? AIROHA_PCS_ANA_TXPLL_VCODIV_2 : ++ AIROHA_PCS_ANA_TXPLL_VCODIV_1)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF, ++ AIROHA_PCS_ANA_TXPLL_TCL_KBAND_VREF, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_TCL_KBAND_VREF, 0xf)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_LPF_EN, ++ AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW | ++ AIROHA_PCS_ANA_TXPLL_TCL_LPF_EN, ++ FIELD_PREP(AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW, ++ AIROHA_PCS_ANA_TXPLL_TCL_LPF_BW_0_5) | ++ AIROHA_PCS_ANA_TXPLL_TCL_LPF_EN); ++ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_SDM_ORD, ++ AIROHA_PCS_ANA_TXPLL_TCL_AMP_EN); ++ ++ /* Setup TX TermCal */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TX_TXLBRC_EN, ++ AIROHA_PCS_ANA_TX_TERMCAL_VREF_L | ++ AIROHA_PCS_ANA_TX_TERMCAL_VREF_H, ++ FIELD_PREP(AIROHA_PCS_ANA_TX_TERMCAL_VREF_L, 0x2) | ++ FIELD_PREP(AIROHA_PCS_ANA_TX_TERMCAL_VREF_H, 0x2)); ++ ++ /* Setup XPON RX */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_FE_EQ_HZEN, ++ AIROHA_PCS_ANA_RX_FE_VB_EQ3_EN | ++ AIROHA_PCS_ANA_RX_FE_VB_EQ2_EN | ++ AIROHA_PCS_ANA_RX_FE_VB_EQ1_EN | ++ AIROHA_PCS_ANA_RX_FE_EQ_HZEN, ++ AIROHA_PCS_ANA_RX_FE_VB_EQ3_EN | ++ AIROHA_PCS_ANA_RX_FE_VB_EQ2_EN | ++ AIROHA_PCS_ANA_RX_FE_VB_EQ1_EN); ++ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_FE_VCM_GEN_PWDB, ++ AIROHA_PCS_ANA_RX_FE_VCM_GEN_PWDB); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO, ++ AIROHA_PCS_ANA_CDR_LPF_TOP_LIM, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_LPF_TOP_LIM, 0x8000)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_LPF_BOT_LIM, ++ AIROHA_PCS_ANA_CDR_LPF_BOT_LIM, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_LPF_BOT_LIM, 0x78000)); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_CKREF_DIV, ++ AIROHA_PCS_ANA_CDR_PR_RSTB_BYPASS); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_DAC_RANGE, ++ AIROHA_PCS_ANA_RX_DAC_RANGE_EYE, ++ FIELD_PREP(AIROHA_PCS_ANA_RX_DAC_RANGE_EYE, 0x2)); ++} ++ ++static void an7583_pcs_cfg_phy_type(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Enable PLL force selection and Force Disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_EN); ++ ++ if (data->port_type == AIROHA_PCS_PON || ++ data->port_type == AIROHA_PCS_PCIE) { ++ /* TDC */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_FLT_3, ++ AIROHA_PCS_PMA_LCPLL_NCPO_SHIFT, ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_NCPO_SHIFT, 0x1)); ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_FLT_1, ++ AIROHA_PCS_PMA_LCPLL_A_TDC, ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_A_TDC, 0x5)); ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TX_TERMCAL_SELPN, ++ AIROHA_PCS_ANA_TX_TDC_CK_SEL, ++ FIELD_PREP(AIROHA_PCS_ANA_TX_TDC_CK_SEL, 0x0)); ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_PHYCK_DIV, ++ AIROHA_PCS_ANA_RX_TDC_CK_SEL); ++ } ++ ++ /* PLL EN HW Mode */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_1, ++ AIROHA_PCS_PMA_LCPLL_CK_STB_TIMER | ++ AIROHA_PCS_PMA_LCPLL_PCW_MAN_LOAD_TIMER | ++ AIROHA_PCS_PMA_LCPLL_EN_TIMER | ++ AIROHA_PCS_PMA_LCPLL_MAN_PWDB, ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_CK_STB_TIMER, 0x1) | ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_PCW_MAN_LOAD_TIMER, 0x10) | ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_EN_TIMER, 0xa) | ++ AIROHA_PCS_PMA_LCPLL_MAN_PWDB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PON_TX_COUNTER_1, ++ AIROHA_PCS_PMA_TX_HSDATA_EN_WAIT | ++ AIROHA_PCS_PMA_TX_CK_EN_WAIT, ++ FIELD_PREP(AIROHA_PCS_PMA_TX_HSDATA_EN_WAIT, 0x113) | ++ FIELD_PREP(AIROHA_PCS_PMA_TX_CK_EN_WAIT, 0xfa)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PON_TX_COUNTER_2, ++ AIROHA_PCS_PMA_TX_SERDES_RDY_WAIT | ++ AIROHA_PCS_PMA_TX_POWER_ON_WAIT, ++ FIELD_PREP(AIROHA_PCS_PMA_TX_SERDES_RDY_WAIT, 0x9b) | ++ FIELD_PREP(AIROHA_PCS_PMA_TX_POWER_ON_WAIT, 0x210)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PON_TX_COUNTER_0, ++ AIROHA_PCS_PMA_TXCALIB_5US | ++ AIROHA_PCS_PMA_TXCALIB_50US, ++ FIELD_PREP(AIROHA_PCS_PMA_TXCALIB_5US, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_TXCALIB_50US, 0x26)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_FLT_0, ++ AIROHA_PCS_PMA_LCPLL_KI, ++ FIELD_PREP(AIROHA_PCS_PMA_LCPLL_KI, 0x3)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_PW_5, ++ AIROHA_PCS_PMA_LCPLL_TDC_SYNC_IN_MODE); ++ ++ an7583_pcs_common_phya_txpll(priv, index, interface); ++ an7583_pcs_common_phya_tx(priv, index, interface); ++ ++ /* RX HW mode counter */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_0, ++ AIROHA_PCS_PMA_RX_OS_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_OS_START, 0x1)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_6, ++ AIROHA_PCS_PMA_RX_OS_END, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_OS_END, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_0, ++ AIROHA_PCS_PMA_OSC_SPEED_OPT, ++ AIROHA_PCS_PMA_OSC_SPEED_OPT_0_1); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_1, ++ AIROHA_PCS_PMA_RX_PICAL_END | ++ AIROHA_PCS_PMA_RX_PICAL_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PICAL_END, 0x3e8) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PICAL_START, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_4, ++ AIROHA_PCS_PMA_RX_SDCAL_END | ++ AIROHA_PCS_PMA_RX_SDCAL_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_SDCAL_END, 0x3e8) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_SDCAL_START, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_2, ++ AIROHA_PCS_PMA_RX_PDOS_END | ++ AIROHA_PCS_PMA_RX_PDOS_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PDOS_END, 0x3e8) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_PDOS_START, 0x2)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_3, ++ AIROHA_PCS_PMA_RX_FEOS_END | ++ AIROHA_PCS_PMA_RX_FEOS_START, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_FEOS_END, 0x3e8) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_FEOS_START, 0x2)); ++ ++ /* RX Settings */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_2, ++ AIROHA_PCS_PMA_FOM_NUM_ORDER | ++ AIROHA_PCS_PMA_A_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_FOM_NUM_ORDER, 0x1) | ++ FIELD_PREP(AIROHA_PCS_PMA_A_SEL, 0x3)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_0, ++ AIROHA_PCS_PMA_X_MAX | AIROHA_PCS_PMA_X_MIN, ++ FIELD_PREP(AIROHA_PCS_PMA_X_MAX, 0x240) | ++ FIELD_PREP(AIROHA_PCS_PMA_X_MIN, 0x1c0)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_2, ++ AIROHA_PCS_PMA_DATA_SHIFT); ++ ++ an7583_pcs_common_phya_rx(priv, index, interface); ++ an7583_pcs_common_phya_ana(priv, index, interface); ++ ++ /* Setup EYE */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_2, ++ AIROHA_PCS_PMA_EYECNT_FAST); ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_3, ++ AIROHA_PCS_PMA_EYE_NEXTPTS); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEOPENING_CTRL_0, ++ AIROHA_PCS_PMA_EYECNT_VTH | ++ AIROHA_PCS_PMA_EYECNT_HTH, ++ FIELD_PREP(AIROHA_PCS_PMA_EYECNT_VTH, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_EYECNT_HTH, 0x4)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEOPENING_CTRL_1, ++ AIROHA_PCS_PMA_EO_VTH | ++ AIROHA_PCS_PMA_EO_HTH, ++ FIELD_PREP(AIROHA_PCS_PMA_EO_VTH, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_EO_HTH, 0x4)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_0, ++ AIROHA_PCS_PMA_EYE_MASK | ++ AIROHA_PCS_PMA_CNTLEN, ++ FIELD_PREP(AIROHA_PCS_PMA_EYE_MASK, 0xff) | ++ FIELD_PREP(AIROHA_PCS_PMA_CNTLEN, 0xd0)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_0, ++ AIROHA_PCS_PMA_VEO_MASK | ++ AIROHA_PCS_PMA_HEO_MASK | ++ AIROHA_PCS_PMA_EQ_EN_DELAY, ++ FIELD_PREP(AIROHA_PCS_PMA_VEO_MASK, 0x0) | ++ FIELD_PREP(AIROHA_PCS_PMA_HEO_MASK, 0x0) | ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_EN_DELAY, 0x1)); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_1, ++ AIROHA_PCS_PMA_A_LGAIN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CAL1, ++ AIROHA_PCS_PMA_CAL_CYC | ++ AIROHA_PCS_PMA_CAL_STB | ++ AIROHA_PCS_PMA_CAL_1US_SET | ++ AIROHA_PCS_PMA_SIM_FAST_EN, ++ AIROHA_PCS_PMA_CAL_CYC_15 | ++ AIROHA_PCS_PMA_CAL_STB_8US | ++ FIELD_PREP(AIROHA_PCS_PMA_CAL_1US_SET, 0x2e) | ++ AIROHA_PCS_PMA_SIM_FAST_EN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CAL2, ++ AIROHA_PCS_PMA_CAL_CYC_TIME | ++ AIROHA_PCS_PMA_CAL_OUT_OS | ++ AIROHA_PCS_PMA_CAL_OS_PULSE, ++ FIELD_PREP(AIROHA_PCS_PMA_CAL_CYC_TIME, 0x0) | ++ FIELD_PREP(AIROHA_PCS_PMA_CAL_OUT_OS, 0x0)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_CTRL_5, ++ AIROHA_PCS_PMA_RX_RDY | ++ AIROHA_PCS_PMA_RX_BLWC_RDY_EN, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_RDY, 0xa) | ++ FIELD_PREP(AIROHA_PCS_PMA_RX_BLWC_RDY_EN, 0x5)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FEOS, ++ AIROHA_PCS_PMA_EQ_FORCE_BLWC_FREEZE | ++ AIROHA_PCS_PMA_LFSEL, ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_FORCE_BLWC_FREEZE, 0x0)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_1, ++ AIROHA_PCS_PMA_INDEX_MODE | ++ AIROHA_PCS_PMA_Y_MAX | ++ AIROHA_PCS_PMA_Y_MIN, ++ AIROHA_PCS_PMA_INDEX_MODE | ++ FIELD_PREP(AIROHA_PCS_PMA_Y_MAX, 0x3f) | ++ FIELD_PREP(AIROHA_PCS_PMA_Y_MIN, 0x40)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_2, ++ AIROHA_PCS_PMA_EYEDUR, ++ FIELD_PREP(AIROHA_PCS_PMA_EYEDUR, 0x18)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EXTRAL_CTRL, ++ AIROHA_PCS_PMA_L2D_TRIG_EQ_EN_TIME | ++ AIROHA_PCS_PMA_OS_RDY_LATCH | ++ AIROHA_PCS_PMA_DISB_LEQ, ++ FIELD_PREP(AIROHA_PCS_PMA_L2D_TRIG_EQ_EN_TIME, 0x2) | ++ AIROHA_PCS_PMA_OS_RDY_LATCH); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_0, ++ AIROHA_PCS_PMA_KBAND_KFC | ++ AIROHA_PCS_PMA_FPKDIV, ++ AIROHA_PCS_PMA_KBAND_KFC_8 | ++ FIELD_PREP(AIROHA_PCS_PMA_FPKDIV, 0xa5)); ++ ++ if (data->port_type == AIROHA_PCS_PCIE) ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_0, ++ AIROHA_PCS_PMA_KBAND_PREDIV, ++ AIROHA_PCS_PMA_KBAND_PREDIV_8); ++ else ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_0, ++ AIROHA_PCS_PMA_KBAND_PREDIV, ++ AIROHA_PCS_PMA_KBAND_PREDIV_4); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_1, ++ AIROHA_PCS_PMA_SYMBOL_WD | ++ AIROHA_PCS_PMA_SETTLE_TIME_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_SYMBOL_WD, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_SETTLE_TIME_SEL, 0x1)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_5, ++ AIROHA_PCS_PMA_FLL_IDAC_MIN | ++ AIROHA_PCS_PMA_FLL_IDAC_MAX, ++ FIELD_PREP(AIROHA_PCS_PMA_FLL_IDAC_MIN, 0x400) | ++ FIELD_PREP(AIROHA_PCS_PMA_FLL_IDAC_MAX, 0x1ff)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_2, ++ AIROHA_PCS_PMA_AMP | ++ AIROHA_PCS_PMA_PRBS_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_AMP, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_PRBS_SEL, 0x3)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_4, ++ AIROHA_PCS_PMA_DISB_BLWC_OFFSET); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_PDOS_CTRL_0, ++ AIROHA_PCS_PMA_EYE_BLWC_ADD | ++ AIROHA_PCS_PMA_DATA_BLWC_ADD, ++ AIROHA_PCS_PMA_DATA_BLWC_ADD); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_BLWC, ++ AIROHA_PCS_PMA_EQ_BLWC_CNT_BOT_LIM | ++ AIROHA_PCS_PMA_EQ_BLWC_CNT_TOP_LIM | ++ AIROHA_PCS_PMA_EQ_BLWC_GAIN | ++ AIROHA_PCS_PMA_EQ_BLWC_POL, ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_BLWC_CNT_BOT_LIM, 0x10) | ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_BLWC_CNT_TOP_LIM, 0x70) | ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_BLWC_GAIN, 0xa) | ++ AIROHA_PCS_PMA_EQ_BLWC_POL_INVERSION); ++} ++ ++static void an7583_pcs_common_phya_txpll_on(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ if (data->port_type == AIROHA_PCS_PCIE) { ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_JCPLL_SPARE_H, ++ AIROHA_PCS_ANA_JCPLL_SPARE_L, ++ FIELD_PREP(AIROHA_PCS_ANA_JCPLL_SPARE_L, ++ AIROHA_PCS_ANA_JCPLL_SPARE_L_LDO)); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_JCPLL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_JCPLL_EN); ++ } ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_LCPLL_PWCTL_SETTING_0, ++ AIROHA_PCS_PMA_SW_LCPLL_EN); ++ ++ udelay(6); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_CKOUT_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_CKOUT_EN | ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_CKOUT_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF, ++ AIROHA_PCS_ANA_TXPLL_FREQ_MEAS_EN | ++ AIROHA_PCS_ANA_TXPLL_VREF_SEL, ++ AIROHA_PCS_ANA_TXPLL_FREQ_MEAS_EN | ++ AIROHA_PCS_ANA_TXPLL_VREF_SEL_VBG); ++ ++ regmap_set_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_PHY_CK1_EN, ++ AIROHA_PCS_ANA_TXPLL_PHY_CK2_EN | ++ AIROHA_PCS_ANA_TXPLL_PHY_CK1_EN); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_TXPLL_TCL_KBAND_VREF, ++ AIROHA_PCS_ANA_TXPLL_FREQ_MEAS_EN); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_JCPLL_FREQ_MEAS_EN, ++ AIROHA_PCS_ANA_TXPLL_IB_EXT_EN); ++ ++ if (data->port_type == AIROHA_PCS_PCIE) ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FREQ_DET_1, ++ AIROHA_PCS_PMA_UNLOCK_CYCLECNT | ++ AIROHA_PCS_PMA_LOCK_CYCLECNT, ++ FIELD_PREP(AIROHA_PCS_PMA_UNLOCK_CYCLECNT, 0x7fff) | ++ FIELD_PREP(AIROHA_PCS_PMA_LOCK_CYCLECNT, 0x7fff)); ++ ++ usleep_range(500, 1000); ++} ++ ++static void an7583_pcs_common_phya_tx_on(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 xfi_tx_term_sel = 0x1; ++ // int efuse_valid; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_TX_RST_B, ++ AIROHA_PCS_PMA_TX_TOP_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_ADD_CLKPATH_RST_0, ++ AIROHA_PCS_PMA_CLKPATH_RSTB_CK | ++ AIROHA_PCS_PMA_CLKPATH_RST_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_TX_RST_B, ++ AIROHA_PCS_PMA_TXCALIB_RST_B | ++ AIROHA_PCS_PMA_TX_TOP_RST_B); ++ ++ usleep_range(100, 200); ++ ++ /* TODO handle efuse */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_TX_CALIB_0, ++ AIROHA_PCS_PMA_TXCALIB_FORCE_TERMP_SEL | ++ AIROHA_PCS_PMA_TXCALIB_FORCE_TERMP_SEL_EN, ++ FIELD_PREP(AIROHA_PCS_PMA_TXCALIB_FORCE_TERMP_SEL, ++ xfi_tx_term_sel) | ++ AIROHA_PCS_PMA_TXCALIB_FORCE_TERMP_SEL_EN); ++} ++ ++static void an7583_pcs_common_phya_rx_preset(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 cdr_pr_buf_in_sr; ++ bool cdr_pr_cap_en; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_2500BASEX: ++ if (data->port_type == AIROHA_PCS_PCIE) { ++ cdr_pr_cap_en = false; ++ cdr_pr_buf_in_sr = 0x7; ++ } else { ++ cdr_pr_cap_en = true; ++ cdr_pr_buf_in_sr = 0x6; ++ } ++ ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ cdr_pr_cap_en = false; ++ cdr_pr_buf_in_sr = 0x7; ++ break; ++ default: ++ return; ++ } ++ ++ /* Setup RX Precondition */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH, ++ AIROHA_PCS_ANA_RX_SIGDET_VTH_SEL | ++ AIROHA_PCS_ANA_RX_SIGDET_PEAK, ++ FIELD_PREP(AIROHA_PCS_ANA_RX_SIGDET_VTH_SEL, 0x2) | ++ FIELD_PREP(AIROHA_PCS_ANA_RX_SIGDET_PEAK, 0x2)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_DAC_RANGE, ++ AIROHA_PCS_ANA_RX_SIGDET_LPF_CTRL, ++ FIELD_PREP(AIROHA_PCS_ANA_RX_SIGDET_LPF_CTRL, 0x3)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_MONPR_EN, ++ AIROHA_PCS_ANA_CDR_PR_CAP_EN | ++ AIROHA_PCS_ANA_CDR_BUF_IN_SR, ++ (cdr_pr_cap_en ? AIROHA_PCS_ANA_CDR_PR_CAP_EN : 0) | ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_BUF_IN_SR, cdr_pr_buf_in_sr)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_OS_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_OS_RDY); ++ ++ /* Setup L2R */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA); ++ ++ /* Setup LEQ setting */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, 0x0)); ++ ++ /* Keep EYE reset */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_RESET_PLU_O); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_EYE_RESET_PLU_O); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_TOP_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_EYE_TOP_EN); ++ ++ /* Kepp BLWC reset */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_BLWC_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_BLWC_RX_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_BLWC_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_BLWC_EN); ++} ++ ++static void an7583_pcs_common_phya_rx_on(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE_PWDB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PD_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_KBAND_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_KBAND_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PD_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PD_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PD_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PWDB); ++ ++ /* RX SigDet Pwdb */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_SCAN_RST_B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_PWDB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_SCAN_RST_B, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SCAN_RST_B | ++ AIROHA_PCS_PMA_FORCE_DA_RX_SCAN_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_DA_XPON_PWDB_0, ++ AIROHA_PCS_PMA_XPON_CDR_PD_PWDB | ++ AIROHA_PCS_PMA_XPON_CDR_PR_PIEYE_PWDB | ++ AIROHA_PCS_PMA_XPON_CDR_PW_PWDB | ++ AIROHA_PCS_PMA_XPON_RX_FE_PWDB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SS_DA_XPON_PWDB_1, ++ AIROHA_PCS_PMA_RX_SIDGET_PWDB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_SYS_EN_SEL_0, ++ AIROHA_PCS_PMA_RX_SYS_EN_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_RX_SYS_EN_SEL, 0x1)); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, ++ AIROHA_PCS_ANA_CDR_PR_FBKSEL | ++ AIROHA_PCS_ANA_CDR_PR_VREG_CKBUF_VAL | ++ AIROHA_PCS_ANA_CDR_PR_VREG_IBAND_VAL, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_PR_FBKSEL, 0x0) | ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_PR_VREG_CKBUF_VAL, 0x5) | ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_PR_VREG_IBAND_VAL, 0x5)); ++ ++ if (data->port_type == AIROHA_PCS_PCIE) ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_VREG_IBAND_VAL, ++ AIROHA_PCS_ANA_CDR_PR_DAC_BAND, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_PR_DAC_BAND, 0x8)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_PDOS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_FEOS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_SDCAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_OS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_BLWC_EN); ++ ++ if (data->port_type == AIROHA_PCS_PCIE) ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_PMA_PXP_AEQ_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_AEQ_EN | ++ AIROHA_PCS_PMA_FORCE_DA_AEQ_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_AEQ_EN); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_CKREF_DIV, ++ AIROHA_PCS_ANA_CDR_PR_CKREF_DIV, ++ AIROHA_PCS_ANA_CDR_PR_CKREF_DIV_1); ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_PR_TDC_REF_SEL, ++ AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1, ++ AIROHA_PCS_ANA_CDR_PR_CKREF_DIV1_1); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_RX_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB); ++ ++ usleep_range(100, 200); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB); ++} ++ ++static void an7583_pcs_common_phya_l2d(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Setup LPF L2D force and disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA); ++ ++ usleep_range(200, 300); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB); ++} ++ ++static void an7583_pcs_common_phya_tdc_off(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_IDAC, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_FLT_3, ++ AIROHA_PCS_PMA_LCPLL_NCPO_LOAD); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW_CHG, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_TXPLL_SDM_PCW_CHG); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW_CHG, ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW_CHG); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TXPLL_SDM_PCW_CHG, ++ AIROHA_PCS_PMA_FORCE_DA_TXPLL_SDM_PCW_CHG); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_FLT_1, ++ AIROHA_PCS_PMA_LCPLL_GPON_SEL, ++ AIROHA_PCS_PMA_LCPLL_GPON_SEL_FROM_EPON); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_LCPLL_TDC_PW_0, ++ AIROHA_PCS_PMA_LCPLL_TDC_DIG_PWDB); ++ ++ usleep_range(100, 200); ++} ++ ++static void an7583_pcs_common_phya_rx_oscal(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_FBCK_LOCK); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_FBCK_LOCK); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_CKON | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_CKON); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_OSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_OSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_EN); ++ ++ usleep_range(200, 300); ++ ++ /* Set normal of force mode */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_OS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_OS_RDY); ++ ++ /* Disable force mode signal */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_OS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_OS_RDY); ++ ++ /* Release reset enable */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_OS_EN); ++} ++ ++static void an7583_pcs_common_phya_pical(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Pre Condition */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_2, ++ AIROHA_PCS_PMA_DISB_DA_XPON_CDR_PR_PIEYE); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_PI_CAL, ++ AIROHA_PCS_PMA_KPGAIN, ++ FIELD_PREP(AIROHA_PCS_PMA_KPGAIN, 0x4)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_0, ++ AIROHA_PCS_PMA_EQ_EN_DELAY, ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_EN_DELAY, 0x8)); ++ ++ /* Reset Block */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_EQ_PI_CAL_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_RX_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_6, ++ AIROHA_PCS_PMA_DISB_RX_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_REF_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_6, ++ AIROHA_PCS_PMA_DISB_REF_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_3, ++ AIROHA_PCS_PMA_DISB_RQ_PI_CAL_RDY); ++ ++ /* Enable */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_5, ++ AIROHA_PCS_PMA_DISB_RX_OR_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_3, ++ AIROHA_PCS_PMA_FORCE_EQ_PI_CAL_RDY); ++ ++ /* Release Reset and Enable */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_EQ_PI_CAL_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_RX_AND_PICAL_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_REF_AND_PICAL_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PICAL_EN); ++ ++ usleep_range(200, 300); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_3, ++ AIROHA_PCS_PMA_FORCE_EQ_PI_CAL_RDY); ++} ++ ++static void an7583_pcs_common_phya_pdos(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_PDOSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_PDOSCAL_EN); ++ ++ /* Pre Condition */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_OS_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_OS_RDY); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_1, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_E0); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_1, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_D1); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_1, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_D0); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_2, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_E1); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_2, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_EYE); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_BLWC_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_BLWC_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_EYEDUR_INIT_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_DISB_EYEDUR_INIT_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_EYECNT_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_EYECNT_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_EYEDUR_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_DISB_EYEDUR_EN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_PDOS_CTRL_0, ++ AIROHA_PCS_PMA_SAP_SEL, ++ AIROHA_PCS_PMA_SAP_SEL_SHIFT_8); ++ ++ /* Reset Block */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_PDOS_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_6, ++ AIROHA_PCS_PMA_DISB_PDOS_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_1, ++ AIROHA_PCS_PMA_PDOS_RST_B); ++ ++ /* Disable */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PDOS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_PDOS_EN); ++ ++ /* Release Reset and Enable */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_OS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_OS_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_PDOS_RX_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_1, ++ AIROHA_PCS_PMA_PDOS_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PDOS_EN); ++ ++ usleep_range(200, 300); ++ ++ /* Disable (again) */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PDOS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_OS_EN); ++ ++ /* Release EYE related */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_EYEDUR_INIT_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_DISB_EYEDUR_INIT_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_EYECNT_RX_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_EYECNT_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_EYEDUR_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_DISB_EYEDUR_EN); ++ ++ /* Disable PDOS */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_FE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_PDOSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_PDOSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_PDOSCAL_EN); ++} ++ ++static void an7583_pcs_common_phya_feos(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Pre Condition */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_OS_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_OS_RDY); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_2, ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_FE_VOS); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_BLWC_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_BLWC_RX_RST_B); ++ ++ /* Setting */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_SS_RX_FEOS, ++ AIROHA_PCS_PMA_LFSEL, ++ FIELD_PREP(AIROHA_PCS_PMA_LFSEL, 0x30)); ++ ++ /* Reset */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_FEOS_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_FEOS_RX_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_FEOS_RST_B); ++ ++ /* Disable */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_FEOS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_FEOS_EN); ++ ++ /* Release Reset and Enable */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_OS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_OS_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_FEOS_RX_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_FEOS_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_FEOS_EN); ++ ++ usleep_range(1000, 1500); ++ ++ /* Disable */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_FEOS_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_OS_EN); ++} ++ ++static void an7583_pcs_common_phya_sdcal(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ /* Pre Condition */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_SIGDET_CAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_CAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_CAL_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_OSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_EN); ++ ++ /* Reset */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_CAL_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_SDCAL_REF_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_SDCAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_SDCAL_REF_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_SDCAL_EN); ++ ++ /* Release Reset and Enable */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_CAL_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_8, ++ AIROHA_PCS_PMA_FORCE_SDCAL_REF_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_SDCAL_EN); ++ ++ usleep_range(200, 300); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_SDCAL_EN); ++ ++ /* SigDet Cal Disable */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_SIGDET_CAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_CAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_SIGDET_CAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_SIGDET_CAL_EN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_CKON | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_CKON, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_CKON); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_OSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_RSTB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_RX_OSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_EN | ++ AIROHA_PCS_PMA_FORCE_DA_RX_OSCAL_EN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_OSCAL_EN); ++} ++ ++static void an7583_pcs_common_phya_phy_status(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_OS_RDY); ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_OS_RDY); ++ udelay(1); ++} ++ ++static void an7583_pcs_common_phya_eye_setting(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 cdr_lpf_ratio; ++ u32 x_min, x_max; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ x_min = 0x0; ++ x_max = 0x400; ++ cdr_lpf_ratio = 0x3; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ x_min = 0x140; ++ x_max = 0x2c0; ++ cdr_lpf_ratio = 0x0; ++ break; ++ case PHY_INTERFACE_MODE_5GBASER: ++ x_min = 0x180; ++ x_max = 0x280; ++ cdr_lpf_ratio = 0x1; ++ break; ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ x_min = 0x1c0; ++ x_max = 0x234; ++ cdr_lpf_ratio = 0x0; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_CDR_LPF_RATIO, ++ AIROHA_PCS_ANA_CDR_LPF_RATIO, ++ FIELD_PREP(AIROHA_PCS_ANA_CDR_LPF_RATIO, ++ cdr_lpf_ratio)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_0, ++ AIROHA_PCS_PMA_EYE_MASK, ++ FIELD_PREP(AIROHA_PCS_PMA_EYE_MASK, 0xff)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_0, ++ AIROHA_PCS_PMA_X_MAX | AIROHA_PCS_PMA_X_MIN, ++ FIELD_PREP(AIROHA_PCS_PMA_X_MAX, x_max) | ++ FIELD_PREP(AIROHA_PCS_PMA_X_MIN, x_min)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_0, ++ AIROHA_PCS_PMA_CNTLEN, ++ FIELD_PREP(AIROHA_PCS_PMA_CNTLEN, 0xf8)); ++ ++ regmap_clear_bits(priv->pcs_ana, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_0, ++ AIROHA_PCS_PMA_CNTFOREVER); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_2, ++ AIROHA_PCS_PMA_DATA_SHIFT); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_1, ++ AIROHA_PCS_PMA_INDEX_MODE); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_2, ++ AIROHA_PCS_PMA_EYEDUR, ++ FIELD_PREP(AIROHA_PCS_PMA_EYEDUR, 0x44c)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEINDEX_CTRL_3, ++ AIROHA_PCS_PMA_EYE_NEXTPTS | ++ AIROHA_PCS_PMA_EYE_NEXTPTS_TOGGLE | ++ AIROHA_PCS_PMA_EYE_NEXTPTS_SEL, ++ AIROHA_PCS_PMA_EYE_NEXTPTS); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEOPENING_CTRL_0, ++ AIROHA_PCS_PMA_EYECNT_VTH | ++ AIROHA_PCS_PMA_EYECNT_HTH, ++ FIELD_PREP(AIROHA_PCS_PMA_EYECNT_VTH, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_EYECNT_HTH, 0x4)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYEOPENING_CTRL_1, ++ AIROHA_PCS_PMA_EO_VTH | ++ AIROHA_PCS_PMA_EO_HTH, ++ FIELD_PREP(AIROHA_PCS_PMA_EO_VTH, 0x4) | ++ FIELD_PREP(AIROHA_PCS_PMA_EO_HTH, 0x4)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_1, ++ AIROHA_PCS_PMA_B_ZERO_SEL | ++ AIROHA_PCS_PMA_HEO_EMPHASIS | ++ AIROHA_PCS_PMA_A_MGAIN | ++ AIROHA_PCS_PMA_A_LGAIN); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_2, ++ AIROHA_PCS_PMA_A_SEL, ++ FIELD_PREP(AIROHA_PCS_PMA_A_SEL, 0x1)); ++} ++ ++static void an7583_pcs_common_phya_eye_cal(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_TX_RATE_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE, 0x0)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_FLL_COR, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_DAC_EYE | ++ AIROHA_PCS_PMA_FORCE_DA_RX_DAC_EYE, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_DAC_EYE, 0x0)); ++ ++ /* Redo PICal and reset Block */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_0, ++ AIROHA_PCS_PMA_EQ_EN_DELAY, ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_EN_DELAY, 0x80)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_PI_CAL, ++ AIROHA_PCS_PMA_KPGAIN, ++ FIELD_PREP(AIROHA_PCS_PMA_KPGAIN, 0x1)); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_EQ_PI_CAL_RST_B); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_RX_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_6, ++ AIROHA_PCS_PMA_DISB_RX_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_REF_AND_PICAL_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_6, ++ AIROHA_PCS_PMA_DISB_REF_AND_PICAL_RSTB); ++ ++ /* Enable PICal */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_5, ++ AIROHA_PCS_PMA_DISB_RX_OR_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_0, ++ AIROHA_PCS_PMA_DISB_RX_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_0, ++ AIROHA_PCS_PMA_FORCE_RX_PICAL_EN); ++ ++ /* Release Reset */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_RESET_0, ++ AIROHA_PCS_PMA_EQ_PI_CAL_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_RX_AND_PICAL_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_7, ++ AIROHA_PCS_PMA_FORCE_REF_AND_PICAL_RSTB); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN); ++ ++ usleep_range(1000, 1500); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_RX_OR_PICAL_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_3, ++ AIROHA_PCS_PMA_DISB_RQ_PI_CAL_RDY); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_3, ++ AIROHA_PCS_PMA_FORCE_EQ_PI_CAL_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_5, ++ AIROHA_PCS_PMA_DISB_EYECNT_RDY); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_EYECNT_RDY); ++ ++ usleep_range(1000, 1500); ++} ++ ++static void an7583_pcs_common_phya_eye_eo_read(struct airoha_pcs_priv *priv, ++ int index, u32 *heo, u32 *veo) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 eo_buf[EO_BUF_MAX]; ++ u32 eye_el, eye_er; ++ u32 feos; ++ u32 val; ++ int i; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_6, ++ AIROHA_PCS_PMA_LNX_SW_FLL_4_LATCH_EN | ++ AIROHA_PCS_PMA_LNX_SW_FLL_3_LATCH_EN | ++ AIROHA_PCS_PMA_LNX_SW_FLL_2_LATCH_EN | ++ AIROHA_PCS_PMA_LNX_SW_FLL_1_LATCH_EN); ++ ++ usleep_range(50, 100); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FLL_6, ++ AIROHA_PCS_PMA_LNX_SW_FLL_4_LATCH_EN | ++ AIROHA_PCS_PMA_LNX_SW_FLL_3_LATCH_EN | ++ AIROHA_PCS_PMA_LNX_SW_FLL_2_LATCH_EN | ++ AIROHA_PCS_PMA_LNX_SW_FLL_1_LATCH_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DEBUG_0, ++ AIROHA_PCS_PMA_RO_TOGGLE); ++ ++ usleep_range(100, 200); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DEBUG_0, ++ AIROHA_PCS_PMA_RO_TOGGLE); ++ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_RX_TORGS_DEBUG_10, &val); ++ eye_el = FIELD_GET(AIROHA_PCS_PMA_EYE_EL, val); ++ eye_er = FIELD_GET(AIROHA_PCS_PMA_EYE_ER, val); ++ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_RX_TORGS_DEBUG_11, &val); ++ eo_buf[EYE_EU] = FIELD_GET(AIROHA_PCS_PMA_EYE_EU, val); ++ eo_buf[EYE_EB] = FIELD_GET(AIROHA_PCS_PMA_EYE_EB, val); ++ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_ADD_RX2ANA_1, &val); ++ eo_buf[DAC_EYE] = FIELD_GET(AIROHA_PCS_PMA_RX_DAC_EYE, val); ++ eo_buf[DAC_D0] = FIELD_GET(AIROHA_PCS_PMA_RX_DAC_D0, val); ++ eo_buf[DAC_D1] = FIELD_GET(AIROHA_PCS_PMA_RX_DAC_D1, val); ++ eo_buf[DAC_E0] = FIELD_GET(AIROHA_PCS_PMA_RX_DAC_E0, val); ++ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_ADD_RX2ANA_2, &val); ++ eo_buf[FEOS] = FIELD_GET(AIROHA_PCS_PMA_RX_FEOS_OUT, val); ++ eo_buf[DAC_E1] = FIELD_GET(AIROHA_PCS_PMA_RX_DAC_E1, val); ++ ++ feos = eo_buf[FEOS]; ++ ++ for (i = 0; i < ARRAY_SIZE(eo_buf); i++) { ++ if ((eo_buf[i] == feos) && (eo_buf[i] >= 32)) ++ eo_buf[i] = eo_buf[i] - 64; ++ else if (eo_buf[i] >= 64) ++ eo_buf[i] = eo_buf[i] - 128; ++ } ++ ++ /* Check if CLK unlocking happens (E0 result validity) */ ++ regmap_read(pcs_pma, AIROHA_PCS_PMA_RX_TORGS_DEBUG_5, &val); ++ if (!FIELD_GET(AIROHA_PCS_PMA_HEO_RDY, val)) { ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_0, ++ AIROHA_PCS_PMA_DISB_DA_XPON_CDR_LPF_RSTB); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_0, ++ AIROHA_PCS_PMA_FORCE_DA_XPON_CDR_LPF_RSTB); ++ ++ usleep_range(500, 700); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_0, ++ AIROHA_PCS_PMA_FORCE_DA_XPON_CDR_LPF_RSTB); ++ ++ usleep_range(500, 700); ++ } ++ ++ *heo = abs(eye_er - eye_el); ++ *veo = abs(eo_buf[EYE_EU] - eo_buf[EYE_EB]); ++} ++ ++static void an7583_pcs_common_phya_eye_eo(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface, ++ u32 *heo, u32 *veo) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_EYE_RESET_PLU_O); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_RESET_PLU_O); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_RESET_PLU_O); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_EYE_TOP_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_TOP_EN); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_TOP_EN); ++ ++ if (interface == PHY_INTERFACE_MODE_10GBASER || ++ interface == PHY_INTERFACE_MODE_USXGMII) ++ usleep_range(5500, 6000); ++ else ++ msleep(55); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_2, ++ AIROHA_PCS_PMA_DISB_DA_XPON_CDR_PR_PIEYE | ++ AIROHA_PCS_PMA_DISB_DA_XPON_RX_DAC_EYE); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_DISB_EYEDUR_INIT_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_7, ++ AIROHA_PCS_PMA_DISB_EYECNT_RX_RST_B); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_EYE_TOP_EYECNT_CTRL_1, ++ AIROHA_PCS_PMA_DISB_EYEDUR_EN); ++ ++ an7583_pcs_common_phya_eye_eo_read(priv, index, heo, veo); ++ ++ /* Clear Eye SW value */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_RESET_PLU_O); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_8, ++ AIROHA_PCS_PMA_DISB_EYE_TOP_EN); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_9, ++ AIROHA_PCS_PMA_FORCE_EYE_TOP_EN); ++ ++ /* Reset PICal Rdy */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_3, ++ AIROHA_PCS_PMA_DISB_RQ_PI_CAL_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_3, ++ AIROHA_PCS_PMA_FORCE_EQ_PI_CAL_RDY); ++ ++ /* Reset Eyecnt Rdy */ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_DISB_MODE_5, ++ AIROHA_PCS_PMA_DISB_EYECNT_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_FORCE_MODE_6, ++ AIROHA_PCS_PMA_FORCE_EYECNT_RDY); ++} ++ ++static void an7583_pcs_common_phya_eo_scan(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 best_heo = 0, best_veo = 0; ++ u32 leq_gain, best_leq_gain; ++ u32 best_leq_peacking = 0; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_5GBASER: ++ leq_gain = 3; ++ break; ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ leq_gain = 1; ++ break; ++ default: ++ return; ++ } ++ ++ best_leq_gain = leq_gain; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_PR_PIEYE_PWDB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_PR_PIEYE_PWDB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_PR_PIEYE_PWDB); ++ ++ an7583_pcs_common_phya_eye_setting(priv, index, interface); ++ ++ /* EYE Open */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PHY_EQ_CTRL_0, ++ AIROHA_PCS_PMA_EQ_EN_DELAY, ++ FIELD_PREP(AIROHA_PCS_PMA_EQ_EN_DELAY, 0x80)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_RX_PI_CAL, ++ AIROHA_PCS_PMA_KPGAIN, ++ FIELD_PREP(AIROHA_PCS_PMA_KPGAIN, 0x4)); ++ ++ for (; leq_gain <= FIELD_MAX(AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL); leq_gain++) { ++ u32 leq_peaking; ++ u32 heo, veo; ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_FE_GAIN_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_GAIN_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_GAIN_CTRL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, leq_gain)); ++ ++ for (leq_peaking = 0; leq_peaking <= FIELD_MAX(AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL); leq_peaking++) { ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL | ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_RX_FE_PEAKING_CTRL | ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, leq_peaking)); ++ ++ usleep_range(500, 700); ++ ++ an7583_pcs_common_phya_eye_cal(priv, index); ++ an7583_pcs_common_phya_eye_eo(priv, index, interface, &heo, &veo); ++ ++ if (veo > 53 && best_veo > 53) { ++ if (heo > best_heo) { ++ best_heo = heo; ++ best_veo = veo; ++ best_leq_peacking = leq_peaking; ++ best_leq_gain = leq_gain; ++ } else if (heo == best_heo && veo > best_veo) { ++ best_heo = heo; ++ best_veo = veo; ++ best_leq_peacking = leq_peaking; ++ best_leq_gain = leq_gain; ++ } ++ } else { ++ if (veo > best_veo) { ++ best_heo = heo; ++ best_veo = veo; ++ best_leq_peacking = leq_peaking; ++ best_leq_gain = leq_gain; ++ } ++ } ++ } ++ } ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_FE_GAIN_CTRL, ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_GAIN_CTRL, best_leq_gain)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_JCPLL_SDM_SCAN, ++ AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, ++ FIELD_PREP(AIROHA_PCS_PMA_FORCE_DA_RX_FE_PEAKING_CTRL, best_leq_peacking)); ++} ++ ++static void an7583_pcs_common_phya_rxrdy(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ u32 xfi_rx_term_sel = 0x1; ++ // int efuse_valid; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_FORCE_RX_RDY); ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_RX_FIFO_RST_N); ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_RX_FIFO_RST_N); ++ ++ /* TODO HANDLE EFUSE */ ++ regmap_update_bits(priv->pcs_ana, AIROHA_PCS_ANA_PXP_RX_SIGDET_NOVTH, ++ AIROHA_PCS_ANA_RX_FE_50OHMS_SEL, ++ FIELD_PREP(AIROHA_PCS_ANA_RX_FE_50OHMS_SEL, ++ xfi_rx_term_sel)); ++} ++ ++static void an7583_pcs_common_phya_bist_setting(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_write(pcs_pma, AIROHA_PCS_PMA_BISTCTL_ALIGN_PAT, ++ 0x8ff1fd53); ++ regmap_write(pcs_pma, AIROHA_PCS_PMA_BISTCTL_PRBS_INITIAL_SEED, ++ 0xFF1FD53); ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_BISTCTL_PRBS_FAIL_THRESHOLD, ++ AIROHA_PCS_PMA_BISTCTL_PRBS_FAIL_THRESHOLD_MASK, ++ FIELD_PREP(AIROHA_PCS_PMA_BISTCTL_PRBS_FAIL_THRESHOLD_MASK, 0x1)); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_BISTCTL_CONTROL, ++ AIROHA_PCS_PMA_BISTCTL_PAT_SEL, ++ AIROHA_PCS_PMA_BISTCTL_PAT_SEL_PRBS31); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_BISTCTL_POLLUTION, ++ AIROHA_PCS_PMA_BIST_TX_DATA_POLLUTION_LATCH); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_SS_BIST_1, ++ AIROHA_PCS_PMA_LNX_BISTCTL_BIT_ERROR_RST_SEL | ++ AIROHA_PCS_PMA_ANLT_PX_LNX_LT_LOS); ++} ++ ++static void an7583_pcs_first_plug_in(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ const struct airoha_pcs_match_data *data = priv->data; ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ an7583_pcs_common_phya_rx_preset(priv, index, interface); ++ if (data->port_type == AIROHA_PCS_PON || ++ data->port_type == AIROHA_PCS_PCIE) ++ an7583_pcs_common_phya_tdc_off(priv, index); ++ an7583_pcs_common_phya_rx_on(priv, index); ++ an7583_pcs_common_phya_l2d(priv, index); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_REF_RST_N); ++ ++ usleep_range(100, 200); ++ ++ an7583_pcs_common_phya_rx_oscal(priv, index); ++ an7583_pcs_common_phya_pical(priv, index); ++ an7583_pcs_common_phya_pdos(priv, index); ++ an7583_pcs_common_phya_feos(priv, index); ++ an7583_pcs_common_phya_sdcal(priv, index); ++ an7583_pcs_common_phya_phy_status(priv, index); ++ ++ an7583_pcs_dig_reset_release(priv, index); ++ ++ an7583_pcs_common_phya_l2d(priv, index); ++ ++ if (data->port_type == AIROHA_PCS_PON) ++ an7583_pcs_common_phya_eo_scan(priv, index, interface); ++ an7583_pcs_common_phya_rxrdy(priv, index); ++ if (data->port_type == AIROHA_PCS_PON || ++ data->port_type == AIROHA_PCS_PCIE) ++ an7583_pcs_common_phya_bist_setting(priv, index); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_ADD_XPON_MODE_1, ++ AIROHA_PCS_PMA_TX_BIST_GEN_EN | ++ AIROHA_PCS_PMA_R2T_MODE); ++} ++ ++static void an7583_pcs_ana_reset_release(struct airoha_pcs_priv *priv, ++ int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_XFI_RXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_TXPCS_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_XFI_RXPCS_BIST_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_HSG_RXPCS_RST_N | ++ AIROHA_PCS_PMA_SW_HSG_TXPCS_RST_N); ++ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_SW_RST_SET, ++ AIROHA_PCS_PMA_SW_XFI_RXMAC_RST_N | ++ AIROHA_PCS_PMA_SW_XFI_TXMAC_RST_N); ++} ++ ++int an7583_pcs_usb_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ struct airoha_pcs_maps *maps = &priv->maps[index]; ++ ++ regmap_set_bits(maps->multi_sgmii, ++ AIROHA_PCS_MULTI_SGMII_MSG_RX_LIK_STS_1, ++ AIROHA_PCS_PAUSE_STS_P2 | AIROHA_PCS_PAUSE_STS_P1); ++ ++ return phy_set_mode_ext(priv->phy, PHY_MODE_ETHERNET, interface); ++} ++ ++int an7583_pcs_common_phya_bringup(struct airoha_pcs_priv *priv, ++ int index, phy_interface_t interface) ++{ ++ if (priv->phy) ++ return an7583_pcs_usb_phya_bringup(priv, index, interface); ++ ++ an7583_pcs_dig_reset_hold(priv, index); ++ ++ an7583_pcs_cfg_phy_type(priv, index, interface); ++ ++ an7583_pcs_common_phya_txpll_on(priv, index); ++ ++ an7583_pcs_common_phya_tx_on(priv, index); ++ ++ an7583_pcs_first_plug_in(priv, index, interface); ++ ++ an7583_pcs_ana_reset_release(priv, index); ++ ++ return 0; ++} ++ ++void an7583_pcs_common_phya_link_up(struct airoha_pcs_priv *priv, int index) ++{ ++ struct regmap *pcs_pma = priv->pcs_pma[index]; ++ ++ if (priv->phy) ++ return; ++ ++ /* First CDR reset */ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB); ++ ++ usleep_range(700, 1000); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB); ++ ++ usleep_range(100, 200); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_RSTB | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_RSTB); ++ ++ regmap_update_bits(pcs_pma, AIROHA_PCS_PMA_PXP_CDR_LPF_LCK_2DATA, ++ AIROHA_PCS_PMA_FORCE_SEL_DA_CDR_LPF_LCK2DATA | ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA, ++ AIROHA_PCS_PMA_FORCE_DA_CDR_LPF_LCK2DATA); ++ ++ /* Then RX Rdy reset */ ++ regmap_set_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_DISB_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_RDY); ++ ++ regmap_clear_bits(pcs_pma, AIROHA_PCS_PMA_RX_CTRL_SEQUENCE_FORCE_CTRL_1, ++ AIROHA_PCS_PMA_DISB_RX_RDY); ++} diff --git a/target/linux/airoha/patches-6.12/604-02-net-ethernet-airoha-define-sport-value-for-GDM3.patch b/target/linux/airoha/patches-6.12/604-02-net-ethernet-airoha-define-sport-value-for-GDM3.patch new file mode 100644 index 00000000000..b78cc3455fe --- /dev/null +++ b/target/linux/airoha/patches-6.12/604-02-net-ethernet-airoha-define-sport-value-for-GDM3.patch @@ -0,0 +1,26 @@ +From 6548e580509397a622b7c504a79de93414771459 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 25 Jun 2025 00:04:36 +0200 +Subject: [PATCH 6/6] net: ethernet: airoha: define sport value for GDM3 + +On Airoha AN7583, the Serdes Ethernet goes through the GDM3 port. +To correctly receive packet for QDMA, add the sport value to identify +packet from GDM3 port. + +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -585,6 +585,9 @@ static int airoha_qdma_get_gdm_port(stru + case 0x18: + port = 3; /* GDM4 */ + break; ++ case 0x16: ++ port = 2; /* GDM3 */ ++ break; + case 0x10 ... 0x14: + port = 0; /* GDM1 */ + break; diff --git a/target/linux/airoha/patches-6.12/605-net-pcs-airoha-add-support-for-optional-xfi-reset-li.patch b/target/linux/airoha/patches-6.12/605-net-pcs-airoha-add-support-for-optional-xfi-reset-li.patch new file mode 100644 index 00000000000..a505475097b --- /dev/null +++ b/target/linux/airoha/patches-6.12/605-net-pcs-airoha-add-support-for-optional-xfi-reset-li.patch @@ -0,0 +1,63 @@ +From 961800f3badd72e4efda39f219ac4cbec5791433 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 26 Jul 2025 22:58:10 +0200 +Subject: [PATCH 7/8] net: pcs: airoha: add support for optional xfi reset line + +On Airoha AN7583 there is a dedicated reset line for the PON XFI Serdes. +This is needed to permit changing the WAN sel register or the system +will stall on accessing the XFI register. + +Add support for this optional dedicated reset to permit correct +configuration of the PON Serdes. + +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/airoha/pcs-airoha-common.c | 12 ++++++++++++ + drivers/net/pcs/airoha/pcs-airoha.h | 1 + + 2 files changed, 13 insertions(+) + +--- a/drivers/net/pcs/airoha/pcs-airoha-common.c ++++ b/drivers/net/pcs/airoha/pcs-airoha-common.c +@@ -144,6 +144,10 @@ static int airoha_pcs_setup_scu(struct a + const struct airoha_pcs_match_data *data = priv->data; + int ret; + ++ ret = reset_control_assert(priv->xfi_rst); ++ if (ret) ++ return ret; ++ + switch (data->port_type) { + case AIROHA_PCS_ETH: + airoha_pcs_setup_scu_eth(priv, interface); +@@ -161,6 +165,10 @@ static int airoha_pcs_setup_scu(struct a + break; + } + ++ ret = reset_control_deassert(priv->xfi_rst); ++ if (ret) ++ return ret; ++ + /* TODO better handle reset from MAC */ + ret = reset_control_bulk_assert(ARRAY_SIZE(priv->rsts), + priv->rsts); +@@ -1298,6 +1306,10 @@ static int airoha_pcs_probe(struct platf + if (ret) + return dev_err_probe(dev, ret, "failed to get bulk reset lines\n"); + ++ priv->xfi_rst = devm_reset_control_get_optional_exclusive(dev, "xfi"); ++ if (IS_ERR(priv->xfi_rst)) ++ return dev_err_probe(dev, PTR_ERR(priv->xfi_rst), "failed to get xfi reset lines\n"); ++ + /* For Ethernet PCS, read the AN7581 SoC revision to check if + * manual rx calibration is needed. This is only limited to + * any SoC revision before E2. +--- a/drivers/net/pcs/airoha/pcs-airoha.h ++++ b/drivers/net/pcs/airoha/pcs-airoha.h +@@ -1654,6 +1654,7 @@ struct airoha_pcs_priv { + struct regmap *pcs_ana; + struct regmap_field **pcs_ana_fields[2]; + ++ struct reset_control *xfi_rst; + struct reset_control_bulk_data rsts[AIROHA_PCS_MAX_NUM_RSTS]; + + struct phy *phy; diff --git a/target/linux/airoha/patches-6.12/606-net-airoha-disable-external-phy-code-if-PCS_AIROHA-i.patch b/target/linux/airoha/patches-6.12/606-net-airoha-disable-external-phy-code-if-PCS_AIROHA-i.patch new file mode 100644 index 00000000000..d43d3303a07 --- /dev/null +++ b/target/linux/airoha/patches-6.12/606-net-airoha-disable-external-phy-code-if-PCS_AIROHA-i.patch @@ -0,0 +1,124 @@ +From 843e2892f2d9353bf039e0dfb5442a600e75009e Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Thu, 9 Oct 2025 23:46:08 +0300 +Subject: [PATCH] net: airoha: disable external phy code if PCS_AIROHA is not + enabled + +External phy code breaks building for EN7523, so disable it if +PCS_AIROHA is not selected. + +Signed-off-by: Mikhail Kshevetskiy +--- + drivers/net/ethernet/airoha/airoha_eth.c | 16 ++++++++++++++++ + drivers/net/ethernet/airoha/airoha_eth.h | 2 ++ + 2 files changed, 18 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -72,10 +72,12 @@ static void airoha_qdma_irq_disable(stru + airoha_qdma_set_irqmask(irq_bank, index, mask, 0); + } + ++#if defined(CONFIG_PCS_AIROHA) + static bool airhoa_is_phy_external(struct airoha_gdm_port *port) + { + return port->id != 1; + } ++#endif + + static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) + { +@@ -1725,6 +1727,7 @@ static int airoha_dev_open(struct net_de + struct airoha_qdma *qdma = port->qdma; + u32 pse_port = FE_PSE_PORT_PPE1; + ++#if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { + err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0); + if (err) { +@@ -1735,6 +1738,7 @@ static int airoha_dev_open(struct net_de + + phylink_start(port->phylink); + } ++#endif + + netif_tx_start_all_queues(dev); + err = airoha_set_vip_for_gdm_port(port, true); +@@ -1797,10 +1801,12 @@ static int airoha_dev_stop(struct net_de + } + } + ++#if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { + phylink_stop(port->phylink); + phylink_disconnect_phy(port->phylink); + } ++#endif + + return 0; + } +@@ -2990,6 +2996,7 @@ bool airoha_is_valid_gdm_port(struct air + return false; + } + ++#if defined(CONFIG_PCS_AIROHA) + static void airoha_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) +@@ -3102,6 +3109,7 @@ static int airoha_setup_phylink(struct n + + return err; + } ++#endif + + static int airoha_alloc_gdm_port(struct airoha_eth *eth, + struct device_node *np) +@@ -3176,11 +3184,13 @@ static int airoha_alloc_gdm_port(struct + port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + eth->ports[p] = port; + ++#if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { + err = airoha_setup_phylink(dev); + if (err) + return err; + } ++#endif + + return airoha_metadata_dst_alloc(port); + } +@@ -3310,8 +3320,10 @@ error_napi_stop: + continue; + + if (port->dev->reg_state == NETREG_REGISTERED) { ++#if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) + phylink_destroy(port->phylink); ++#endif + unregister_netdev(port->dev); + } + airoha_metadata_dst_free(port); +@@ -3338,8 +3350,10 @@ static void airoha_remove(struct platfor + if (!port) + continue; + ++#if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) + phylink_destroy(port->phylink); ++#endif + unregister_netdev(port->dev); + airoha_metadata_dst_free(port); + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -542,8 +542,10 @@ struct airoha_gdm_port { + int id; + int nbq; + ++#if defined(CONFIG_PCS_AIROHA) + struct phylink *phylink; + struct phylink_config phylink_config; ++#endif + + struct airoha_hw_stats stats; + diff --git a/target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch b/target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch new file mode 100644 index 00000000000..0c273f000fe --- /dev/null +++ b/target/linux/airoha/patches-6.12/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch @@ -0,0 +1,129 @@ +From f2c6f8711c3866caafee997cfa60af4f38879be0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 25 Jun 2025 00:45:11 +0200 +Subject: [PATCH 1/2] net: phy: add PHY_DETACH_NO_HW_RESET PHY flag + +Some PHY require a firmware to correctly work and such firmware might +get reset when the GPIO reset is assert. + +This is the case for the Aeonsemi PHY where when the PHY is torn down, +phy_detach() is called that assert the GPIO reset pin resetting the +firmware. + +To handle this introduce a flag, PHY_DETACH_NO_HW_RESET that instruct +phy_detach() to skip asserting the GPIO reset on detaching the PHY. + +The PHY is still reset in all other case where it's removed or the PHY +fails to probe. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/as21xxx.c | 10 ++++++++++ + drivers/net/phy/phy_device.c | 3 ++- + include/linux/phy.h | 1 + + 3 files changed, 13 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -965,6 +965,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), +@@ -977,6 +978,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), +@@ -989,6 +991,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), +@@ -1001,6 +1004,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), +@@ -1013,6 +1017,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), +@@ -1025,6 +1030,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), +@@ -1037,6 +1043,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), +@@ -1049,6 +1056,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), +@@ -1061,6 +1069,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), +@@ -1073,6 +1082,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, ++ .flags = PHY_DETACH_NO_RESET, + }, + }; + module_phy_driver(as21xxx_drivers); +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2069,7 +2069,8 @@ void phy_detach(struct phy_device *phyde + device_release_driver(&phydev->mdio.dev); + + /* Assert the reset signal */ +- phy_device_reset(phydev, 1); ++ if (!phydev->drv || !(phydev->drv->flags & PHY_DETACH_NO_HW_RESET)) ++ phy_device_reset(phydev, 1); + + /* + * The phydev might go away on the put_device() below, so avoid +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -90,6 +90,7 @@ extern const int phy_10gbit_features_arr + #define PHY_RST_AFTER_CLK_EN 0x00000002 + #define PHY_POLL_CABLE_TEST 0x00000004 + #define PHY_ALWAYS_CALL_SUSPEND 0x00000008 ++#define PHY_DETACH_NO_HW_RESET 0x00000010 + #define MDIO_DEVICE_IS_PHY 0x80000000 + + /** diff --git a/target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch b/target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch new file mode 100644 index 00000000000..029bdb5ab15 --- /dev/null +++ b/target/linux/airoha/patches-6.12/801-02-net-phy-as21xxx-add-flag-PHY_DETACH_NO_HW_RESET.patch @@ -0,0 +1,108 @@ +From 7ad1470c3d08c1abea747aa0c789e924f63fcbc4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 25 Jun 2025 00:51:45 +0200 +Subject: [PATCH 2/2] net: phy: as21xxx: add flag PHY_DETACH_NO_HW_RESET + +Add flag PHY_DETACH_NO_HW_RESET to handle firmware getting reset on +calling phy_detach() if the GPIO reset PIN is defined in DT. + +This will skip the firmware from getting reset permitting the PHY to +continue work when the PHY is torn down and gets up again. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/as21xxx.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -965,7 +965,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), +@@ -978,7 +978,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), +@@ -991,7 +991,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), +@@ -1004,7 +1004,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), +@@ -1017,7 +1017,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), +@@ -1030,7 +1030,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), +@@ -1043,7 +1043,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), +@@ -1056,7 +1056,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), +@@ -1069,7 +1069,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + { + PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), +@@ -1082,7 +1082,7 @@ static struct phy_driver as21xxx_drivers + .led_hw_control_set = as21xxx_led_hw_control_set, + .led_hw_control_get = as21xxx_led_hw_control_get, + .led_polarity_set = as21xxx_led_polarity_set, +- .flags = PHY_DETACH_NO_RESET, ++ .flags = PHY_DETACH_NO_HW_RESET, + }, + }; + module_phy_driver(as21xxx_drivers); diff --git a/target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch b/target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch new file mode 100644 index 00000000000..6e599cdd3f0 --- /dev/null +++ b/target/linux/airoha/patches-6.12/802-01-net-phy-as21xxx-handle-corner-case-with-link-and-aut.patch @@ -0,0 +1,31 @@ +From 0146a02d9d182796c3d8e4a432c4d94cac042f8e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 7 Jul 2025 18:58:25 +0200 +Subject: [PATCH 1/4] net: phy: as21xxx: handle corner case with link and + autoneg complete + +Add missing case in custom read_link, when autoneg is started, autoneg +complete bit is reset but link is still not up. + +Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs") +Signed-off-by: Christian Marangi +--- + drivers/net/phy/as21xxx.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -658,6 +658,13 @@ static int as21xxx_read_link(struct phy_ + return status; + + phydev->link = !!(status & MDIO_STAT1_LSTATUS); ++ phydev->autoneg_complete = !!(status & MDIO_AN_STAT1_COMPLETE); ++ ++ /* Consider the case that autoneg was started and "aneg complete" ++ * bit has been reset, but "link up" bit not yet. ++ */ ++ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) ++ phydev->link = 0; + + return 0; + } diff --git a/target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch b/target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch new file mode 100644 index 00000000000..4010c4093b0 --- /dev/null +++ b/target/linux/airoha/patches-6.12/802-02-net-phy-as21xxx-fix-read_status-speed-handling.patch @@ -0,0 +1,156 @@ +From d90186b1e48dd4a428abf889b1eb17d2469de08b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 8 Jul 2025 10:50:42 +0200 +Subject: [PATCH 2/4] net: phy: as21xxx: fix read_status speed handling + +With further test with 2.5G NIC it was discovered that +phy_resolve_aneg_linkmode is not enough to detect speed higher that 1G +when autoneg is enabled. + +Also in the switch case there is a typo where the speed mask is AND with +VEND1_SPEED_STATUS instead of the correct mask VEND1_SPEED_MASK. + +Rework the read_status code to always read the speed from the vendor +register and parse the generic bit only for the pause frame. + +Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs") +Signed-off-by: Christian Marangi +--- + drivers/net/phy/as21xxx.c | 96 +++++++++++++++++++++------------------ + 1 file changed, 53 insertions(+), 43 deletions(-) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -671,7 +671,7 @@ static int as21xxx_read_link(struct phy_ + + static int as21xxx_read_c22_lpa(struct phy_device *phydev) + { +- int lpagb; ++ int lpagb, lpa; + + /* MII_STAT1000 are only filled in the mapped C22 + * in C45, use that to fill lpagb values and check. +@@ -698,12 +698,20 @@ static int as21xxx_read_c22_lpa(struct p + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, + lpagb); + ++ lpa = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_LPA); ++ if (lpa < 0) ++ return lpa; ++ ++ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); ++ + return 0; + } + + static int as21xxx_read_status(struct phy_device *phydev) + { + int bmcr, old_link = phydev->link; ++ int speed; + int ret; + + ret = as21xxx_read_link(phydev, &bmcr); +@@ -720,58 +728,60 @@ static int as21xxx_read_status(struct ph + phydev->asym_pause = 0; + + if (phydev->autoneg == AUTONEG_ENABLE) { +- ret = genphy_c45_read_lpa(phydev); +- if (ret) +- return ret; ++ if (!phydev->autoneg_complete) { ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ++ 0); ++ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0); ++ return 0; ++ } + + ret = as21xxx_read_c22_lpa(phydev); + if (ret) + return ret; +- +- phy_resolve_aneg_linkmode(phydev); + } else { +- int speed; +- + linkmode_zero(phydev->lp_advertising); ++ } + +- speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, +- VEND1_SPEED_STATUS); +- if (speed < 0) +- return speed; +- +- switch (speed & VEND1_SPEED_STATUS) { +- case VEND1_SPEED_10000: +- phydev->speed = SPEED_10000; ++ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_SPEED_STATUS); ++ if (speed < 0) ++ return speed; ++ ++ switch (speed & VEND1_SPEED_MASK) { ++ case VEND1_SPEED_10000: ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_5000: ++ phydev->speed = SPEED_5000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; +- break; +- case VEND1_SPEED_5000: +- phydev->speed = SPEED_5000; +- phydev->duplex = DUPLEX_FULL; +- break; +- case VEND1_SPEED_2500: +- phydev->speed = SPEED_2500; +- phydev->duplex = DUPLEX_FULL; +- break; +- case VEND1_SPEED_1000: +- phydev->speed = SPEED_1000; +- if (bmcr & BMCR_FULLDPLX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- break; +- case VEND1_SPEED_100: +- phydev->speed = SPEED_100; +- phydev->duplex = DUPLEX_FULL; +- break; +- case VEND1_SPEED_10: +- phydev->speed = SPEED_10; +- phydev->duplex = DUPLEX_FULL; +- break; +- default: +- return -EINVAL; +- } ++ else ++ phydev->duplex = DUPLEX_HALF; ++ break; ++ case VEND1_SPEED_100: ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_10: ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ default: ++ return -EINVAL; + } + ++ if (phydev->autoneg == AUTONEG_ENABLE) ++ phy_resolve_aneg_pause(phydev); ++ + return 0; + } + diff --git a/target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch b/target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch new file mode 100644 index 00000000000..19118db7864 --- /dev/null +++ b/target/linux/airoha/patches-6.12/802-03-net-phy-as21xxx-force-C45-OPs-for-AUTONEG.patch @@ -0,0 +1,34 @@ +From 6003da596beb6b8974e61b7ff494476a323fbef5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 8 Jul 2025 11:29:49 +0200 +Subject: [PATCH 3/4] net: phy: as21xxx: force C45 OPs for AUTONEG + +With further testing with 2.5G NIC, it was discovered that the PHY +require the C45 OPs to configure and restart ANEG or speed higher than +1G doesn't function correctly. + +To force C45 OPs with generic PHY function, clear the C22 bit from +devices_in_package bitmask. + +Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs") +Signed-off-by: Christian Marangi +--- + drivers/net/phy/as21xxx.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -616,6 +616,13 @@ static int as21xxx_probe(struct phy_devi + if (ret) + return ret; + ++ /* Even if PHY declare support for Clause 22 register, ++ * Clause 45 register should be used for ANEG configuration ++ * and restart. Clear the C22 bit for devices_in_package to ++ * force C45 generic OPs in generic PHY ANGE OPs. ++ */ ++ phydev->c45_ids.devices_in_package &= ~BIT(0); ++ + ret = aeon_ipc_sync_parity(phydev, priv); + if (ret) + return ret; diff --git a/target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch b/target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch new file mode 100644 index 00000000000..0c24e9e20f8 --- /dev/null +++ b/target/linux/airoha/patches-6.12/804-net-phy-as21xxx-implement-read-workaround-for-C45-re.patch @@ -0,0 +1,132 @@ +From fabaa8a7183d10217e14af437fd3805bd6dd9eba Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 18 Oct 2025 04:12:41 +0200 +Subject: [PATCH] net: phy: as21xxx: implement read workaround for C45 read + +This PHY have lots of problems with MDIO read operation. We somehow +workaround this with using C45 operation for pretty much everything but +this is not enough. The reference code for this PHY makes a write to an +unused PHY to workaround this read problem. This was also confirmed by +Aeonsemi. + +Various test were made to try to workaround this ins alternative way +than the random write. + +One effective solution was to limit the write only to BMSR. And also +write to BMSR is safe since they are only read only registers. + +This is only done for read operation as write operation doesn't suffer +from this problem. + +Worth to mention that when multiple Aeonsemi PHY are mounted, the +workaround doesn't work if we write to another Aeonsemi PHY. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/as21xxx.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +--- a/drivers/net/phy/as21xxx.c ++++ b/drivers/net/phy/as21xxx.c +@@ -967,6 +967,21 @@ out: + return ret; + } + ++static int as21xxx_read_mmd(struct phy_device *phydev, int devad, ++ u16 regnum) ++{ ++ struct mii_bus *bus = phydev->mdio.bus; ++ int val; ++ ++ val = __mdiobus_c45_read(bus, phydev->mdio.addr, devad, ++ regnum); ++ ++ /* FIXME: verify if it's actually ok to limit this to MII_BMSR */ ++ __mdiobus_write(bus, 0x0, MII_BMSR, 0x1); ++ ++ return val; ++} ++ + static struct phy_driver as21xxx_drivers[] = { + { + /* PHY expose in C45 as 0x7500 0x9410 +@@ -984,6 +999,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -997,6 +1013,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1010,6 +1027,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1023,6 +1041,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1036,6 +1055,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1049,6 +1069,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1062,6 +1083,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1075,6 +1097,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1088,6 +1111,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, +@@ -1101,6 +1125,7 @@ static struct phy_driver as21xxx_drivers + .probe = as21xxx_probe, + .match_phy_device = as21xxx_match_phy_device, + .read_status = as21xxx_read_status, ++ .read_mmd = as21xxx_read_mmd, + .led_brightness_set = as21xxx_led_brightness_set, + .led_hw_is_supported = as21xxx_led_hw_is_supported, + .led_hw_control_set = as21xxx_led_hw_control_set, diff --git a/target/linux/airoha/patches-6.12/885-i2c-mt7621-optional-reset.patch b/target/linux/airoha/patches-6.12/885-i2c-mt7621-optional-reset.patch new file mode 100644 index 00000000000..1fad1bdd01f --- /dev/null +++ b/target/linux/airoha/patches-6.12/885-i2c-mt7621-optional-reset.patch @@ -0,0 +1,11 @@ +--- a/drivers/i2c/busses/i2c-mt7621.c ++++ b/drivers/i2c/busses/i2c-mt7621.c +@@ -85,7 +85,7 @@ static void mtk_i2c_reset(struct mtk_i2c + { + int ret; + +- ret = device_reset(i2c->adap.dev.parent); ++ ret = device_reset_optional(i2c->adap.dev.parent); + if (ret) + dev_err(i2c->dev, "I2C reset failed!\n"); + diff --git a/target/linux/airoha/patches-6.12/886-uart-add-en7523-support.patch b/target/linux/airoha/patches-6.12/886-uart-add-en7523-support.patch new file mode 100644 index 00000000000..486208f71ad --- /dev/null +++ b/target/linux/airoha/patches-6.12/886-uart-add-en7523-support.patch @@ -0,0 +1,206 @@ +--- /dev/null ++++ b/drivers/tty/serial/8250/8250_en7523.c +@@ -0,0 +1,94 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Airoha EN7523 driver. ++ * ++ * Copyright (c) 2022 Genexis Sweden AB ++ * Author: Benjamin Larsson ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++ ++/* The Airoha UART is 16550-compatible except for the baud rate calculation. ++ * ++ * crystal_clock = 20 MHz ++ * xindiv_clock = crystal_clock / clock_div ++ * (x/y) = XYD, 32 bit register with 16 bits of x and and then 16 bits of y ++ * clock_div = XINCLK_DIVCNT (default set to 10 (0x4)), ++ * - 3 bit register [ 1, 2, 4, 8, 10, 12, 16, 20 ] ++ * ++ * baud_rate = ((xindiv_clock) * (x/y)) / ([BRDH,BRDL] * 16) ++ * ++ * XYD_y seems to need to be larger then XYD_x for things to work. ++ * Setting [BRDH,BRDL] to [0,1] and XYD_y to 65000 give even values ++ * for usual baud rates. ++ * ++ * Selecting divider needs to fulfill ++ * 1.8432 MHz <= xindiv_clk <= APB clock / 2 ++ * The clocks are unknown but a divider of value 1 did not work. ++ * ++ * Optimally the XYD, BRD and XINCLK_DIVCNT registers could be searched to ++ * find values that gives the least error for every baud rate. But searching ++ * the space takes time and in practise only a few rates are of interest. ++ * With some value combinations not working a tested subset is used giving ++ * a usable range from 110 to 460800 baud. ++ */ ++ ++#define CLOCK_DIV_TAB_ELEMS 3 ++#define XYD_Y 65000 ++#define XINDIV_CLOCK 20000000 ++#define UART_BRDL_20M 0x01 ++#define UART_BRDH_20M 0x00 ++ ++static int clock_div_tab[] = { 10, 4, 2}; ++static int clock_div_reg[] = { 4, 2, 1}; ++ ++ ++int en7523_set_uart_baud_rate (struct uart_port *port, unsigned int baud) ++{ ++ struct uart_8250_port *up = up_to_u8250p(port); ++ unsigned int xyd_x, nom, denom; ++ int i; ++ ++ /* set DLAB to access the baud rate divider registers (BRDH, BRDL) */ ++ serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); ++ ++ /* set baud rate calculation defaults */ ++ ++ /* set BRDIV ([BRDH,BRDL]) to 1 */ ++ serial_port_out(port, UART_BRDL, UART_BRDL_20M); ++ serial_port_out(port, UART_BRDH, UART_BRDH_20M); ++ ++ /* calculate XYD_x and XINCLKDR register */ ++ ++ for (i = 0 ; i < CLOCK_DIV_TAB_ELEMS ; i++) { ++ denom = (XINDIV_CLOCK/40) / clock_div_tab[i]; ++ nom = (baud * (XYD_Y/40)); ++ xyd_x = ((nom/denom) << 4); ++ if (xyd_x < XYD_Y) break; ++ } ++ ++ serial_port_out(port, UART_XINCLKDR, clock_div_reg[i]); ++ serial_port_out(port, UART_XYD, (xyd_x<<16) | XYD_Y); ++ ++ /* unset DLAB */ ++ serial_port_out(port, UART_LCR, up->lcr); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(en7523_set_uart_baud_rate); +--- a/drivers/tty/serial/8250/8250_of.c ++++ b/drivers/tty/serial/8250/8250_of.c +@@ -341,6 +341,7 @@ static const struct of_device_id of_plat + { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, ++ { .compatible = "airoha,en7523-uart", .data = (void *)PORT_AIROHA, }, + { /* end of list */ }, + }; + MODULE_DEVICE_TABLE(of, of_platform_serial_table); +--- a/drivers/tty/serial/8250/8250_port.c ++++ b/drivers/tty/serial/8250/8250_port.c +@@ -319,6 +319,14 @@ static const struct serial8250_config ua + .rxtrig_bytes = {1, 8, 16, 30}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, ++ [PORT_AIROHA] = { ++ .name = "Airoha 16550", ++ .fifo_size = 8, ++ .tx_loadsz = 1, ++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, ++ .rxtrig_bytes = {1, 4}, ++ .flags = UART_CAP_FIFO, ++ }, + }; + + /* Uart divisor latch read */ +@@ -2841,6 +2849,12 @@ serial8250_do_set_termios(struct uart_po + + serial8250_set_divisor(port, baud, quot, frac); + ++#ifdef CONFIG_SERIAL_8250_AIROHA ++ /* Airoha SoCs have custom registers for baud rate settings */ ++ if (port->type == PORT_AIROHA) ++ en7523_set_uart_baud_rate(port, baud); ++#endif ++ + /* + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR + * is written without DLAB set, this mode will be disabled. +--- a/drivers/tty/serial/8250/Kconfig ++++ b/drivers/tty/serial/8250/Kconfig +@@ -355,6 +355,16 @@ config SERIAL_8250_ACORN + system, say Y to this option. The driver can handle 1, 2, or 3 port + cards. If unsure, say N. + ++config SERIAL_8250_AIROHA ++ tristate "Airoha UART support" ++ depends on (ARCH_AIROHA || COMPILE_TEST) && OF && SERIAL_8250 ++ help ++ Selecting this option enables an Airoha SoC specific baud rate ++ calculation routine on an otherwise 16550 compatible UART hardware. ++ ++ If you have an Airoha based board and want to use the serial port, ++ say Y to this option. If unsure, say N. ++ + config SERIAL_8250_BCM2835AUX + tristate "BCM2835 auxiliar mini UART support" + depends on ARCH_BCM2835 || COMPILE_TEST +--- a/drivers/tty/serial/8250/Makefile ++++ b/drivers/tty/serial/8250/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_SERIAL_8250_CONSOLE) += 825 + + obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o + obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o ++obj-$(CONFIG_SERIAL_8250_AIROHA) += 8250_en7523.o + obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o + obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o + obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o +--- a/include/uapi/linux/serial_reg.h ++++ b/include/uapi/linux/serial_reg.h +@@ -383,5 +383,17 @@ + #define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */ + #define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */ + ++/* ++ * These are definitions for the Airoha EN75XX uart registers ++ * Normalized because of 32 bits registers. ++ */ ++#define UART_BRDL 0 ++#define UART_BRDH 1 ++#define UART_XINCLKDR 10 ++#define UART_XYD 11 ++#define UART_TXLVLCNT 12 ++#define UART_RXLVLCNT 13 ++#define UART_FINTLVL 14 ++ + #endif /* _LINUX_SERIAL_REG_H */ + +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -31,6 +31,7 @@ + #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */ + #define PORT_RT2880 29 /* Ralink RT2880 internal UART */ + #define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */ ++#define PORT_AIROHA 31 /* Airoha 16550 UART */ + + /* + * ARM specific type numbers. These are not currently guaranteed +--- a/include/linux/serial_8250.h ++++ b/include/linux/serial_8250.h +@@ -195,6 +195,7 @@ void serial8250_do_set_mctrl(struct uart + void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot); + int fsl8250_handle_irq(struct uart_port *port); ++int en7523_set_uart_baud_rate(struct uart_port *port, unsigned int baud); + int serial8250_handle_irq(struct uart_port *port, unsigned int iir); + u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr); + void serial8250_read_char(struct uart_8250_port *up, u16 lsr); diff --git a/target/linux/airoha/patches-6.12/900-airoha-bmt-support.patch b/target/linux/airoha/patches-6.12/900-airoha-bmt-support.patch new file mode 100644 index 00000000000..5ba31c0829a --- /dev/null +++ b/target/linux/airoha/patches-6.12/900-airoha-bmt-support.patch @@ -0,0 +1,578 @@ +--- /dev/null ++++ b/drivers/mtd/nand/airoha_bmt.c +@@ -0,0 +1,575 @@ ++ ++/* ++ * Airoha BMT algorithm ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "mtk_bmt.h" ++ ++#define MAX_BMT_SIZE (250) ++#define MAX_RAW_BAD_BLOCK_SIZE (250) ++#define POOL_GOOD_BLOCK_PERCENT 8/100 ++#define MAX_BMT_PERCENT 1/8 ++ ++typedef struct { ++ char signature[3]; ++ u8 version; ++ u8 bad_count; // this field is useless ++ u8 size; ++ u8 checksum; ++ u8 reseverd[13]; ++} bmt_table_header; ++ ++typedef struct { ++ u16 from; ++ u16 to; ++} bmt_entry; ++ ++typedef struct { ++ bmt_table_header header; ++ bmt_entry table[MAX_BMT_SIZE]; ++} bmt_table; ++ ++typedef struct { ++ char signature[4]; ++ u32 checksum; ++ u8 version; ++ u8 size; ++ u8 reserved[2]; ++} bbt_table_header; ++ ++typedef struct { ++ bbt_table_header header; ++ u16 table[MAX_RAW_BAD_BLOCK_SIZE]; ++} bbt_table; ++ ++bbt_table bbt; ++bmt_table bmt; ++ ++int bmt_index=0xffff; ++int bbt_index=0xffff; ++unsigned int total_blks , system_blks , bmt_blks, _to, _to2, val; ++ ++module_param(bmt_index, int, S_IRUSR | S_IWUSR); ++module_param(bbt_index, int, S_IRUSR | S_IWUSR); ++module_param(total_blks, int, S_IRUSR | S_IWUSR); ++module_param(system_blks, int, S_IRUSR | S_IWUSR); ++module_param(bmt_blks, int, S_IRUSR | S_IWUSR); ++module_param(_to, int, S_IRUSR | S_IWUSR); ++module_param(_to2, int, S_IRUSR | S_IWUSR); ++module_param(val, int, S_IRUSR | S_IWUSR); ++ ++ ++static bool is_bad_raw(int block) { ++ u8 fdm[4]; ++ int ret; ++ ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ if (ret || fdm[0] != 0xff ){ ++ return true; ++ } ++ return false; ++} ++ ++static bool is_bad( int block) { ++ u8 fdm[4]; ++ int ret; ++ ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ //printk("%x %x %x %x\n", fdm[0], fdm[1], fdm[2], fdm[3]); ++ if (ret || fdm[0] != 0xff || fdm[1] != 0xff ){ ++ return true; ++ } ++ return false; ++} ++ ++ ++static bool is_mapped( int block) { ++ u16 mapped_block; ++ u8 fdm[4]; ++ int ret; ++ ++ ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ mapped_block = (fdm[2] << 8) | fdm[3]; ++ //printk("%u is mapped to %d\n", mapped_block); ++ if (mapped_block == 0xffff) ++ return false; ++ else return true; ++} ++ ++static void mark_bad(int block) { ++ u8 fdm[4] = {0xff, 0xff, 0xff, 0xff}; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 4, ++ .oobbuf = fdm, ++ .datbuf = NULL, ++ .len = 0, ++ }; ++ int retlen; ++ ++ printk("marking bad :%d\n", block); ++ if (block < system_blks) ++ fdm[0] = 0x00; ++ else fdm[1] = 0x00; ++ ++ retlen = bmtd._write_oob(bmtd.mtd, block << bmtd.blk_shift , &ops) ; ++ if (retlen < 0) { ++ printk("marking bad block failed \n"); ++ } ++} ++ ++ ++static void mark_good(int block) { ++ u8 fdm[4] = {0xff, 0xff, 0xff, 0xff}; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 4, ++ .oobbuf = fdm, ++ .datbuf = NULL, ++ .len = 0, ++ }; ++ int retlen; ++ retlen = bmtd._write_oob(bmtd.mtd, block << bmtd.blk_shift , &ops) ; ++ if (retlen < 0) { ++ printk("marking bad block failed \n"); ++ } ++} ++ ++static void make_mapping(u16 from , u16 to) { ++ u8 fdm[4] = {0xff, 0xff, 0xff , 0xff}; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 4, ++ .oobbuf = fdm, ++ .datbuf = NULL, ++ .len = 0, ++ }; ++ int retlen; ++ ++ memcpy(fdm + 2, &to, sizeof(to)); // this has to be exactly like this . ++ retlen = bmtd._write_oob(bmtd.mtd, from << bmtd.blk_shift , &ops) ; ++ if (retlen < 0) { ++ printk("marking bad block failed \n"); ++ } ++} ++ ++static u16 bbt_checksum(void) { ++ int i=0; ++ u16 checksum =0; ++ u8 *data = (u8*) &bbt; ++ checksum += bbt.header.version; ++ checksum += bbt.header.size; ++ data += sizeof(bbt_table_header); ++ for (; i < sizeof(bbt.table); i++) ++ checksum += data[i]; ++ return checksum; ++} ++ ++static bool parse_bbt(void) { ++ int i = system_blks; ++ u8 fdm[4]; ++ for (; i < total_blks; i++) { ++ if( !is_bad(i) ++ && !bbt_nand_read(blk_pg(i),(unsigned char *)&bbt, sizeof(bbt), fdm, sizeof(fdm)) ++ && (strncmp(bbt.header.signature , "RAWB", 4)==0) ++ && (bbt.header.checksum == bbt_checksum()) ++ ) { ++ bbt_index = i; ++ return true; ++ } ++ } ++ return false; ++} ++ ++static u8 bmt_checksum(void) { ++ int i; ++ u8 checksum = 0; ++ u8* data = (u8*)&bmt; ++ checksum += bmt.header.version; ++ checksum += bmt.header.size; ++ data += sizeof(bmt_table_header); ++ for (i=0;i system_blks;i--) { ++ if ( !is_bad(i) ++ && !bbt_nand_read(blk_pg(i),(unsigned char *)&bmt, sizeof(bmt), fdm, sizeof(fdm)) ++ && (strncmp(bmt.header.signature , "BMT", 3)==0) ++ && (bmt.header.checksum == bmt_checksum()) ++ ) { ++ bmt_index = i ; ++ return true; ++ } ++ } ++ return false; ++} ++ ++static void variable_setup(void) { ++ unsigned int need_valid_block_num; ++ int valid_blks = 0; ++ int last_blk; ++ ++ total_blks = bmtd.total_blks; ++ last_blk = total_blks - 1; ++ need_valid_block_num = total_blks * POOL_GOOD_BLOCK_PERCENT; ++ ++ for (; last_blk > 0 ;last_blk--) { ++ if (is_bad_raw(last_blk)) { ++ continue; ++ } ++ valid_blks++; ++ if (valid_blks == need_valid_block_num) { ++ break; ++ } ++ } ++ bmt_blks = total_blks - last_blk; ++ system_blks = total_blks - bmt_blks; ++ bmtd.mtd->size = (total_blks - total_blks * MAX_BMT_PERCENT) * bmtd.mtd->erasesize; ++} ++ ++ ++static int find_available_block(bool start_from_end) { ++ int i=system_blks,d=1; ++ int count = 0; ++ if (start_from_end) ++ i=total_blks-1,d=-1; ++ for (; count < (total_blks - system_blks); count++, i+=d) { ++ if(bmt_index == i || bbt_index == i || is_bad(i) || is_mapped(i)) ++ continue; ++ return i ; ++ } ++ //TODO: handle OOM ++ return -1; ++} ++ ++static void update_bmt_bbt( void ) { ++ int retlen = 0; ++ struct mtd_oob_ops ops , ops1; ++ ++ bbt.header.checksum = bbt_checksum(); ++ bmt.header.checksum = bmt_checksum(); ++ ++ if(bbt_index ==0xffff) bbt_index = find_available_block(false); ++ if(bmt_index ==0xffff) bmt_index = find_available_block(true); ++ ++ bbt_nand_erase(bmt_index); ++ bbt_nand_erase(bbt_index); ++ printk("putting back in bbt_index: %d, bmt_index: %d\n" , bbt_index, bmt_index); ++ ++ ops = (struct mtd_oob_ops) { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bmt), ++ .datbuf = (u8 *)&bmt, ++ }; ++ ++retry_bmt: ++ retlen = bmtd._write_oob(bmtd.mtd, bmt_index << bmtd.blk_shift, &ops); ++ if (retlen) { ++ printk("error while write"); ++ mark_bad(bmt_index); ++ if (bmt_index > system_blks) { ++ bmt_index--; ++ goto retry_bmt; ++ } ++ return; ++ } ++ ops1 = (struct mtd_oob_ops) { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bbt), ++ .datbuf = (u8 *)&bbt, ++ }; ++ ++retry_bbt: ++ retlen = bmtd._write_oob(bmtd.mtd, bbt_index << bmtd.blk_shift, &ops1); ++ if (retlen) { ++ printk("error while write"); ++ mark_bad(bbt_index); ++ if (bbt_index < total_blks) { ++ bbt_index++; ++ goto retry_bbt; ++ } ++ return; ++ } ++} ++ ++static bool is_in_bmt(int block) { ++ int i; ++ for (i=0;i= system_blks ;i--) { ++ unsigned short mapped_block; ++ u8 fdm[4]; ++ int ret; ++ ++ if (is_bad(i)) continue; ++ ret = bbt_nand_read(blk_pg(i), bmtd.data_buf, bmtd.pg_size, ++ fdm, sizeof(fdm)); ++ if (ret < 0) ++ mark_bad(i); ++ ++ memcpy(&mapped_block,fdm+2,2); // need to be this way ++ if (mapped_block >= system_blks) continue; ++ printk("block %X was mapped to :%X\n", mapped_block, i); ++ bmt.table[bmt.header.size++] = (bmt_entry){.from = mapped_block , .to = i}; ++ } ++ memset(&bbt,0x00,sizeof(bbt)); ++ memcpy(&bbt.header.signature , "RAWB", 4); ++ bbt.header.version = 1; ++ bbt.header.size = 0; ++ for ( i = 0 ; i < system_blks; i++) { ++ if (is_bad_raw(i) && !is_in_bmt(i)) ++ bbt.table[bbt.header.size++] = (u16)i; ++ } ++ bmt.header.checksum = bmt_checksum(); ++ bbt.header.checksum = bbt_checksum(); ++ update_bmt_bbt(); ++ printk("bbt and bmt reconstructed successfully\n"); ++} ++ ++ ++static bool remap_block(u16 block , u16 mapped_block, int copy_len) { ++ bool mapped_already_in_bbt = false; ++ bool mapped_already_in_bmt = false; ++ bool block_already_in_bbt = false; ++ u16 new_block = find_available_block(false); ++ int i; ++ // TODO check for -1 ++ ++ bbt_nand_erase(new_block); ++ if (copy_len) ++ bbt_nand_copy(new_block , mapped_block , copy_len); ++ ++ for (i=0; i < bmt.header.size; i++) ++ if (bmt.table[i].from == block) { ++ bmt.table[i].to = new_block; ++ mapped_already_in_bmt = true; ++ break; ++ } ++ ++ if (!mapped_already_in_bmt) ++ bmt.table[bmt.header.size++] = (bmt_entry){ .from = block, .to = new_block}; ++ ++ for (i=0;i system_blks) ++ return block; ++ for (i = 0; i < bmt.header.size; i++) ++ if (bmt.table[i].from == block) ++ return bmt.table[i].to; ++ return block; ++} ++ ++static void unmap_block( u16 block) { // not required ++ printk("unmapping is called on block : %d\n", block); ++} ++ ++ ++static int debug( void* data , u64 cmd) { ++ int i; ++ printk("val: %d\n", val); ++ printk("_to: %d\n", _to); ++ if (val == 0 ) { ++ printk("fixing all\n"); ++ for (i=0;ierasesize); ++ mapped_block = get_mapping_block(_to); ++ printk("after mapped to: %d\n", mapped_block); ++ } else if(val ==2 ) { ++ printk("bmt table: \n"); ++ for (i = 0 ; i < bmt.header.size;i++) { ++ printk("%d->%d\n", bmt.table[i].from , bmt.table[i].to); ++ } ++ printk("bbt table\n"); ++ for (i =0;i< bbt.header.size;i++) { ++ printk("%d ", bbt.table[i]); ++ } ++ printk("\n"); ++ } else if(val == 3) { ++ printk("reconstruct from oob\n"); ++ reconstruct_from_oob(); ++ } else if (val == 4) { ++ printk("showing the oobreconstruct_from_oob of %d\n", _to); ++ printk("%d\n",is_bad(_to)); ++ } else if (val == 5 ) { ++ printk("trying to parse_bmt again %d\n", parse_bmt()); ++ } else if (val == 6 ) { ++ printk("marking bad : %d", _to); ++ mark_bad(_to); ++ } else if ( val == 7) { ++ struct mtd_oob_ops opsk = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bmt), ++ .datbuf = (u8 *)&bmt, ++ }; ++ int retlen; ++ printk("parse bmt from the %d block \n", _to); ++ retlen = bmtd._read_oob(bmtd.mtd, _to << bmtd.blk_shift , &opsk); ++ ++ printk("status : %d\n", retlen); ++ } else if (val == 8) { ++ u8 *data; ++ int j; ++ printk("dump bmt hex\n"); ++ data = (u8 *)&bmt; ++ for (j =0;j < 50;j++) { ++ if(j%20==0) printk("\n"); ++ printk("%X ", data[j]); ++ } ++ printk("bbt table\n"); ++ data = (u8 *)&bbt; ++ for (j =0;j < 50;j++) { ++ if(j%20==0) printk("\n"); ++ printk("%X ", data[j]); ++ } ++ } else if (val == 9) { ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ .len = sizeof(bmt), ++ .datbuf = (u8 *)&bmt, ++ }; ++ int retlen; ++ printk("put bmt at index\n"); ++ retlen = bmtd._write_oob(bmtd.mtd, _to << bmtd.blk_shift, &ops); ++ bmt.header.checksum = bmt_checksum(); ++ if (retlen < 0) { ++ printk("error while write"); ++ } ++ } else if (val == 10) { ++ printk("erase block %d\n", _to); ++ bbt_nand_erase(_to); ++ } else if (val == 11) { ++ char *buf1, *buf2; ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ }; ++ struct mtd_oob_ops ops1 = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .ooboffs = 0, ++ .ooblen = 0, ++ .oobbuf = NULL, ++ }; ++ int retlen; ++ int j; ++ ++ printk("tranfering content from block :%d to %d\n", _to , _to2); ++ bbt_nand_copy(_to2, _to, bmtd.mtd->erasesize); ++ printk("now we check size\n"); ++ ++ buf1 = (char*) kzalloc(sizeof(char) * bmtd.mtd->erasesize , GFP_KERNEL); ++ buf2 = (char*) kzalloc(sizeof(char) * bmtd.mtd->erasesize , GFP_KERNEL); ++ ++ ops.len = sizeof(char) * bmtd.mtd->erasesize; ++ ops.datbuf = buf1; ++ retlen = bmtd._read_oob(bmtd.mtd, _to << bmtd.blk_shift, &ops); ++ if (retlen < 0) { ++ printk("error while write\n"); ++ } ++ ++ ops1.len = sizeof(char) * bmtd.mtd->erasesize; ++ ops1.datbuf = buf2; ++ retlen = bmtd._read_oob(bmtd.mtd, _to << bmtd.blk_shift, &ops1); ++ if (retlen < 0) { ++ printk("error while write"); ++ } ++ for (j = 0 ; j < bmtd.mtd->erasesize ;j++) { ++ if (j%20==0) { ++ printk("\n"); ++ } ++ printk("%X %X ", buf1[j], buf2[j]); ++ } ++ printk("\n"); ++ ++ } ++ return 0; ++} ++ ++ ++const struct mtk_bmt_ops airoha_bmt_ops = { ++ .sig = "bmt", ++ .sig_len = 3, ++ .init = init, ++ .remap_block = remap_block, ++ .unmap_block = unmap_block, ++ .get_mapping_block = get_mapping_block, ++ .debug = debug, ++}; diff --git a/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch b/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch new file mode 100644 index 00000000000..2dc81292ec4 --- /dev/null +++ b/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch @@ -0,0 +1,34 @@ +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) + { +@@ -1604,6 +1605,7 @@ static int spinand_probe(struct spi_mem + if (ret) + return ret; + ++ mtk_bmt_attach(mtd); + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + goto err_spinand_cleanup; +@@ -1611,6 +1613,7 @@ static int spinand_probe(struct spi_mem + return 0; + + err_spinand_cleanup: ++ mtk_bmt_detach(mtd); + spinand_cleanup(spinand); + + return ret; +@@ -1629,6 +1632,7 @@ static int spinand_remove(struct spi_mem + if (ret) + return ret; + ++ mtk_bmt_detach(mtd); + spinand_cleanup(spinand); + + return 0; diff --git a/target/linux/airoha/patches-6.12/911-clk-en7581-Separate-PERST-from-refclk-in-PCIe-clock.patch b/target/linux/airoha/patches-6.12/911-clk-en7581-Separate-PERST-from-refclk-in-PCIe-clock.patch new file mode 100644 index 00000000000..9469c1c5ba2 --- /dev/null +++ b/target/linux/airoha/patches-6.12/911-clk-en7581-Separate-PERST-from-refclk-in-PCIe-clock.patch @@ -0,0 +1,42 @@ +From: Ryan Chen +Subject: clk: en7581: Separate PERST from refclk in PCIe clock callbacks + +The EN7581 PCIe clock enable/disable callbacks currently toggle both +PERST (reset) and reference clock signals together. This prevents the +PCIe controller driver from properly sequencing PERST relative to MAC +register configuration, which is required for x2 link mode. + +Separate the two concerns: clock callbacks only manage reference clocks +(REFCLK_EN0/EN1), while PERST (PERSTOUT/PERSTOUT1/PERSTOUT2) is left +to the PCIe controller driver to manage directly via the NP_SCU regmap. + +Signed-off-by: Ryan Chen +--- a/drivers/clk/clk-en7523.c ++++ b/drivers/clk/clk-en7523.c +@@ -960,9 +960,11 @@ static int en7581_pci_enable(struct clk_ + struct regmap *map = cg->map; + u32 mask; + +- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | +- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | +- REG_PCI_CONTROL_PERSTOUT; ++ /* Only enable reference clocks - PERST is managed separately by the ++ * PCIe controller driver to allow proper sequencing of MAC register ++ * configuration between PERST assert and deassert. ++ */ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; + regmap_set_bits(map, REG_PCI_CONTROL, mask); + + return 0; +@@ -974,9 +976,8 @@ static void en7581_pci_disable(struct cl + struct regmap *map = cg->map; + u32 mask; + +- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | +- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | +- REG_PCI_CONTROL_PERSTOUT; ++ /* Only disable reference clocks - PCIe driver manages PERST */ ++ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; + regmap_clear_bits(map, REG_PCI_CONTROL, mask); + usleep_range(1000, 2000); + } diff --git a/target/linux/airoha/patches-6.12/912-pcie-mediatek-gen3-Add-x2-link-support-for-Airoha-EN7581.patch b/target/linux/airoha/patches-6.12/912-pcie-mediatek-gen3-Add-x2-link-support-for-Airoha-EN7581.patch new file mode 100644 index 00000000000..94267562036 --- /dev/null +++ b/target/linux/airoha/patches-6.12/912-pcie-mediatek-gen3-Add-x2-link-support-for-Airoha-EN7581.patch @@ -0,0 +1,244 @@ +From: Ryan Chen +Subject: PCI: mediatek-gen3: Add PCIe x2 link support for Airoha EN7581 + +The Airoha EN7581 SoC supports PCIe x2 mode where two PCIe lanes are +bonded together for a single x2 link. This requires coordination with +the NP_SCU system controller for serdes mux routing, PERST management, +and lane configuration. + +The x2 initialization sequence: +1. Assert serdes reset and PERST before enabling clocks +2. Configure serdes mux for 2-lane mode +3. Enable reference clocks, deassert serdes reset +4. Clear x1 mode bit and write EQ presets on both MACs +5. Deassert PERST to start link training + +After initial link training, if the link negotiates Gen2 instead of +Gen3, a serdes reset toggle forces the MAC to re-discover the PHY +Gen3 capability from the Link Capabilities 2 register, allowing the +link to retrain at Gen3 x2 (8 GT/s). + +Signed-off-by: Ryan Chen +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -61,6 +61,14 @@ + #define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24) + #define PCIE_LTSSM_STATE_L2_IDLE 0x14 + ++/* EN7581 x2 mode support */ ++#define PCIE_SETTING_REG_X1_MODE BIT(13) ++ ++/* EN7581 NP_SCU register offsets for x2 link init */ ++#define NP_SCU_LANE_CFG0 0x830 ++#define NP_SCU_LANE_CFG1 0x834 ++#define NP_SCU_CTRL_REG 0x88 ++ + #define PCIE_LINK_STATUS_REG 0x154 + #define PCIE_PORT_LINKUP BIT(8) + +@@ -205,6 +213,11 @@ struct mtk_gen3_pcie { + DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM); + + const struct mtk_gen3_pcie_pdata *soc; ++ ++ /* EN7581 x2 mode support */ ++ struct regmap *np_scu; ++ bool x2_mode; ++ void __iomem *sister_base; + }; + + /* LTSSM state in PCIE_LTSSM_STATUS_REG bit[28:24] */ +@@ -925,6 +938,28 @@ static int mtk_pcie_en7581_power_up(stru + size = lower_32_bits(resource_size(entry->res)); + regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size))); + ++ /* Lookup NP_SCU regmap for x2 mode support */ ++ pcie->np_scu = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,np-scu"); ++ if (IS_ERR(pcie->np_scu)) { ++ dev_dbg(dev, "np_scu not available, x2 mode disabled\n"); ++ pcie->np_scu = NULL; ++ } ++ ++ /* Check for x2 mode property */ ++ pcie->x2_mode = of_property_read_bool(dev->of_node, "airoha,x2-mode"); ++ if (pcie->x2_mode) ++ dev_info(dev, "x2 mode enabled\n"); ++ ++ /* Map sister MAC for x2 mode (MAC1 at +0x20000) */ ++ if (pcie->x2_mode) { ++ pcie->sister_base = devm_ioremap(dev, ++ pcie->reg_base + 0x20000, 0x20000); ++ if (!pcie->sister_base) ++ dev_warn(dev, "failed to map sister MAC for x2\n"); ++ else ++ dev_info(dev, "x2 mode: sister MAC mapped\n"); ++ } ++ + err = phy_set_mode(pcie->phy, PHY_MODE_PCIE); + if (err) { + dev_err(dev, "failed to set PHY mode\n"); +@@ -962,17 +997,28 @@ static int mtk_pcie_en7581_power_up(stru + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + +- val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | +- FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | +- FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | +- FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); +- writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG); +- +- val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | +- FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | +- FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | +- FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); +- writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); ++ /* ++ * EN7581 x2: Assert PERST and serdes reset before enabling clocks ++ * so that MAC registers can be configured while devices are held ++ * in reset, ensuring link trains with the correct x2 settings. ++ */ ++ if (pcie->x2_mode && pcie->np_scu) { ++ /* Assert serdes reset on all lanes */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), BIT(26) | BIT(27)); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(27), BIT(27)); ++ msleep(100); ++ ++ /* Assert PERST on all ports */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(16) | BIT(26) | BIT(29), 0); ++ ++ /* Set serdes mux for 2-lane mode (bits[1:0] = 2) */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(0) | BIT(1), BIT(1)); ++ mdelay(1); ++ } + + err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (err) { +@@ -981,12 +1027,121 @@ static int mtk_pcie_en7581_power_up(stru + } + + /* +- * Airoha EN7581 performs PCIe reset via clk callbacks since it has a +- * hw issue with PCIE_PE_RSTB signal. Add wait for the time needed to +- * complete the PCIe reset. ++ * Airoha EN7581: clock enable only provides refclk (patch 911). ++ * For x2, PERST + serdes are already asserted above. ++ * Wait for refclk to stabilize. + */ + msleep(PCIE_T_PVPERL_MS); + ++ if (pcie->x2_mode && pcie->np_scu) { ++ /* ++ * EN7581 x2: PERST asserted, serdes reset asserted, ++ * refclk now stable. Complete the init sequence. ++ */ ++ msleep(30); ++ ++ /* Deassert serdes reset on all lanes */ ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), 0); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(27), 0); ++ ++ /* Clear SETTING_REG bit 13 for x2 mode on both MACs */ ++ val = readl_relaxed(pcie->base + PCIE_SETTING_REG); ++ writel_relaxed(val & ~PCIE_SETTING_REG_X1_MODE, ++ pcie->base + PCIE_SETTING_REG); ++ if (pcie->sister_base) { ++ val = readl_relaxed(pcie->sister_base + 0x80); ++ writel_relaxed(val & ~PCIE_SETTING_REG_X1_MODE, ++ pcie->sister_base + 0x80); ++ } ++ ++ /* EQ presets on both MACs */ ++ writel_relaxed(0x41474147, pcie->base + PCIE_EQ_PRESET_01_REG); ++ if (pcie->sister_base) ++ writel_relaxed(0x41474147, pcie->sister_base + 0x100); ++ writel_relaxed(0x1018020f, pcie->base + PCIE_PIPE4_PIE8_REG); ++ if (pcie->sister_base) ++ writel_relaxed(0x1018020f, pcie->sister_base + 0x338); ++ ++ /* Deassert PERST for all ports - link training starts */ ++ msleep(10); ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(16) | BIT(26) | BIT(29), ++ BIT(16) | BIT(26) | BIT(29)); ++ ++ /* Wait for link training to complete */ ++ msleep(800); ++ ++ /* ++ * Check if link trained at Gen3. If not, toggle serdes ++ * reset to force MAC to re-discover PHY Gen3 capability. ++ * Without this, MAC only advertises Gen1-Gen2 in LnkCap2. ++ */ ++ val = readl_relaxed(pcie->base + PCIE_LINK_STATUS_REG); ++ if (val & PCIE_PORT_LINKUP) { ++ void __iomem *cfg = pcie->base + PCIE_CFG_OFFSET_ADDR; ++ u8 cap_ptr; ++ int speed = 0; ++ ++ /* Walk PCI cap list to find PCIe cap (ID=0x10) */ ++ cap_ptr = readl_relaxed(cfg + PCI_CAPABILITY_LIST) & 0xFF; ++ while (cap_ptr >= 0x40) { ++ u32 hdr = readl_relaxed(cfg + cap_ptr); ++ if ((hdr & 0xFF) == PCI_CAP_ID_EXP) { ++ u32 lnk = readl_relaxed(cfg + cap_ptr + PCI_EXP_LNKCTL); ++ speed = (lnk >> 16) & PCI_EXP_LNKSTA_CLS; ++ break; ++ } ++ cap_ptr = (hdr >> 8) & 0xFF; ++ } ++ ++ if (speed > 0 && speed < 3) { ++ dev_info(dev, "x2: link at Gen%d, toggling serdes for Gen3\n", speed); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(7) | BIT(8), BIT(7) | BIT(8)); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), BIT(26) | BIT(27)); ++ msleep(1000); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0, ++ BIT(7) | BIT(8), 0); ++ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1, ++ BIT(26) | BIT(27), 0); ++ msleep(2000); ++ dev_info(dev, "x2: serdes toggle done, link retraining\n"); ++ } else { ++ dev_info(dev, "x2: link at Gen%d, no toggle needed\n", speed); ++ } ++ } else { ++ dev_info(dev, "x2: link not up after init, skipping speed check\n"); ++ } ++ ++ dev_info(dev, "x2: init complete, PERST deasserted\n"); ++ } else { ++ /* ++ * Non-x2 mode: standard EQ config then deassert PERST. ++ */ ++ val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | ++ FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | ++ FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | ++ FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); ++ writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG); ++ ++ val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | ++ FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | ++ FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | ++ FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); ++ writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); ++ ++ /* Deassert PERST for all ports */ ++ if (pcie->np_scu) { ++ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG, ++ BIT(16) | BIT(26) | BIT(29), ++ BIT(16) | BIT(26) | BIT(29)); ++ msleep(100); ++ } ++ } ++ + return 0; + + err_clk_prepare_enable: diff --git a/target/linux/airoha/patches-6.12/915-01-net-netfilter-flowtable-Add-the-capability-to-offloa.patch b/target/linux/airoha/patches-6.12/915-01-net-netfilter-flowtable-Add-the-capability-to-offloa.patch new file mode 100644 index 00000000000..e11d368888c --- /dev/null +++ b/target/linux/airoha/patches-6.12/915-01-net-netfilter-flowtable-Add-the-capability-to-offloa.patch @@ -0,0 +1,150 @@ +From 6408cdca652b1f85e5b8582c283203d11f4dedcb Mon Sep 17 00:00:00 2001 +Message-ID: <6408cdca652b1f85e5b8582c283203d11f4dedcb.1779086987.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Sun, 17 May 2026 21:11:27 +0200 +Subject: [PATCH net-next 1/2] net: netfilter: flowtable: Add the capability to + offload dscp field + +Introduce the capability to hw offload via netfilter flowtable APIs the +IP TOS info. Implement the sw offloading for DSCP field via the +netfilter flowtable APIs. + +Signed-off-by: Lorenzo Bianconi +--- + include/net/netfilter/nf_flow_table.h | 2 ++ + net/netfilter/nf_flow_table_ip.c | 12 ++++++++++++ + net/netfilter/nf_flow_table_offload.c | 5 +++++ + net/netfilter/nft_flow_offload.c | 22 ++++++++++++++++++++++ + 4 files changed, 41 insertions(+) + +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -29,6 +29,7 @@ struct nf_flow_key { + struct flow_dissector_key_ipv4_addrs ipv4; + struct flow_dissector_key_ipv6_addrs ipv6; + }; ++ struct flow_dissector_key_ip ip; + struct flow_dissector_key_keyid enc_key_id; + union { + struct flow_dissector_key_ipv4_addrs enc_ipv4; +@@ -138,6 +139,7 @@ struct flow_offload_tuple { + encap_num:2, + in_vlan_ingress:2; + u16 mtu; ++ u8 dscp; + union { + struct { + struct dst_entry *dst_cache; +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -372,6 +372,7 @@ static int nf_flow_offload_forward(struc + struct flow_offload *flow; + unsigned int thoff, mtu; + struct iphdr *iph; ++ u8 dscp; + + dir = tuplehash->tuple.dir; + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); +@@ -401,6 +402,12 @@ static int nf_flow_offload_forward(struc + iph = ip_hdr(skb); + nf_flow_nat_ip(flow, skb, thoff, dir, iph); + ++ dscp = FIELD_GET(INET_DSCP_MASK, ipv4_get_dsfield(iph)); ++ if (tuplehash->tuple.dscp != dscp) ++ ipv4_change_dsfield(iph, INET_ECN_MASK, ++ FIELD_PREP(INET_DSCP_MASK, ++ tuplehash->tuple.dscp)); ++ + ip_decrease_ttl(iph); + skb_clear_tstamp(skb); + +@@ -651,6 +658,7 @@ static int nf_flow_offload_ipv6_forward( + struct flow_offload *flow; + unsigned int thoff, mtu; + struct ipv6hdr *ip6h; ++ u8 dscp; + + dir = tuplehash->tuple.dir; + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); +@@ -679,6 +687,12 @@ static int nf_flow_offload_ipv6_forward( + ip6h = ipv6_hdr(skb); + nf_flow_nat_ipv6(flow, skb, dir, ip6h); + ++ dscp = FIELD_GET(INET_DSCP_MASK, ipv6_get_dsfield(ip6h)); ++ if (tuplehash->tuple.dscp != dscp) ++ ipv6_change_dsfield(ip6h, INET_ECN_MASK, ++ FIELD_PREP(INET_DSCP_MASK, ++ tuplehash->tuple.dscp)); ++ + ip6h->hop_limit--; + skb_clear_tstamp(skb); + +--- a/net/netfilter/nf_flow_table_offload.c ++++ b/net/netfilter/nf_flow_table_offload.c +@@ -103,6 +103,7 @@ static int nf_flow_rule_match(struct nf_ + NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_BASIC, basic); + NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); + NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); ++ NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IP, ip); + NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_TCP, tcp); + NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_PORTS, tp); + +@@ -168,6 +169,10 @@ static int nf_flow_rule_match(struct nf_ + match->dissector.used_keys |= BIT_ULL(key->control.addr_type); + mask->basic.n_proto = 0xffff; + ++ key->ip.tos = FIELD_PREP(INET_DSCP_MASK, tuple->dscp); ++ mask->ip.tos = 0xff; ++ match->dissector.used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_IP); ++ + switch (tuple->l4proto) { + case IPPROTO_TCP: + key->tcp.flags = 0; +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include /* for ipv4 options. */ + #include + #include +@@ -279,6 +280,27 @@ static int nft_flow_route(const struct n + return 0; + } + ++static void nft_flow_set_dscp(const struct nft_pktinfo *pkt, ++ struct flow_offload *flow, ++ enum ip_conntrack_dir dir) ++{ ++ struct flow_offload_tuple *tuple = &flow->tuplehash[dir].tuple; ++ struct sk_buff *skb = pkt->skb; ++ ++ switch (skb->protocol) { ++ case htons(ETH_P_IP): ++ tuple->dscp = FIELD_GET(INET_DSCP_MASK, ++ ipv4_get_dsfield(ip_hdr(skb))); ++ break; ++ case htons(ETH_P_IPV6): ++ tuple->dscp = FIELD_GET(INET_DSCP_MASK, ++ ipv6_get_dsfield(ipv6_hdr(skb))); ++ break; ++ default: ++ break; ++ } ++} ++ + static bool nft_flow_offload_skip(struct sk_buff *skb, int family) + { + if (skb_sec_path(skb)) +@@ -371,6 +393,9 @@ static void nft_flow_offload_eval(const + if (!flow) + goto err_flow_alloc; + ++ nft_flow_set_dscp(pkt, flow, dir); ++ nft_flow_set_dscp(pkt, flow, !dir); ++ + flow_offload_route_init(flow, &route); + if (tcph) + flow_offload_ct_tcp(ct); diff --git a/target/linux/airoha/patches-6.12/915-02-net-airoha-Set-hw-QoS-parameter-according-to-the-pac.patch b/target/linux/airoha/patches-6.12/915-02-net-airoha-Set-hw-QoS-parameter-according-to-the-pac.patch new file mode 100644 index 00000000000..15eded87d32 --- /dev/null +++ b/target/linux/airoha/patches-6.12/915-02-net-airoha-Set-hw-QoS-parameter-according-to-the-pac.patch @@ -0,0 +1,94 @@ +From b9870ade9498f4119d3f8f8368fcd13e1fa0c7c9 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: <6408cdca652b1f85e5b8582c283203d11f4dedcb.1779086987.git.lorenzo@kernel.org> +References: <6408cdca652b1f85e5b8582c283203d11f4dedcb.1779086987.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Mon, 18 May 2026 08:36:20 +0200 +Subject: [PATCH net-next 2/2] net: airoha: Set hw QoS parameter according to + the packet dscp + +Introduce the capability to hw offload via netfilter flowtable APIs the +IP TOS info in order to configure hw queue and dscp field. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + #include "airoha_regs.h" + #include "airoha_eth.h" +@@ -298,7 +299,7 @@ static int airoha_ppe_foe_entry_prepare( + struct airoha_foe_entry *hwe, + struct net_device *dev, int type, + struct airoha_flow_data *data, +- int l4proto) ++ int l4proto, u8 dsfield) + { + u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val; + int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev); +@@ -331,7 +332,7 @@ static int airoha_ppe_foe_entry_prepare( + info.wcid); + } else { + struct airoha_gdm_port *port = netdev_priv(dev); +- u8 pse_port, channel; ++ u8 pse_port, channel, priority; + + if (!airoha_is_valid_gdm_port(eth, port)) + return -EINVAL; +@@ -350,9 +351,13 @@ static int airoha_ppe_foe_entry_prepare( + */ + channel = dsa_port >= 0 ? dsa_port : port->id; + channel = channel % AIROHA_NUM_QOS_CHANNELS; +- qdata |= FIELD_PREP(AIROHA_FOE_CHANNEL, channel); ++ priority = rt_tos2priority(dsfield); ++ priority = priority % AIROHA_NUM_QOS_QUEUES; ++ qdata |= FIELD_PREP(AIROHA_FOE_CHANNEL, channel) | ++ FIELD_PREP(AIROHA_FOE_QID, priority); + + val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port) | ++ FIELD_PREP(AIROHA_FOE_IB2_DSCP, dsfield) | + AIROHA_FOE_IB2_PSE_QOS; + /* For downlink traffic consume SRAM memory for hw + * forwarding descriptors queue. +@@ -1044,9 +1049,9 @@ static int airoha_ppe_flow_offload_repla + struct net_device *odev = NULL; + struct flow_action_entry *act; + struct airoha_foe_entry hwe; ++ u8 dsfield = 0, l4proto = 0; + int err, i, offload_type; + u16 addr_type = 0; +- u8 l4proto = 0; + + if (rhashtable_lookup(ð->flow_table, &f->cookie, + airoha_flow_table_params)) +@@ -1076,6 +1081,13 @@ static int airoha_ppe_flow_offload_repla + return -EOPNOTSUPP; + } + ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) { ++ struct flow_match_ip match; ++ ++ flow_rule_match_ip(rule, &match); ++ dsfield = match.key->tos; ++ } ++ + switch (addr_type) { + case 0: + offload_type = PPE_PKT_TYPE_BRIDGE; +@@ -1141,7 +1153,7 @@ static int airoha_ppe_flow_offload_repla + return -EINVAL; + + err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type, +- &data, l4proto); ++ &data, l4proto, dsfield); + if (err) + return err; + diff --git a/target/linux/airoha/patches-6.12/916-net-airoha-Implement-LRO-TCP-support.patch b/target/linux/airoha/patches-6.12/916-net-airoha-Implement-LRO-TCP-support.patch new file mode 100644 index 00000000000..7636ecbbc82 --- /dev/null +++ b/target/linux/airoha/patches-6.12/916-net-airoha-Implement-LRO-TCP-support.patch @@ -0,0 +1,392 @@ +From 598e1ddfe85ad0f4778eeadd5d878209dd931280 Mon Sep 17 00:00:00 2001 +Message-ID: <598e1ddfe85ad0f4778eeadd5d878209dd931280.1779112628.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Thu, 14 May 2026 20:25:19 +0200 +Subject: [PATCH] net: airoha: Implement LRO TCP support + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 178 ++++++++++++++++++++-- + drivers/net/ethernet/airoha/airoha_eth.h | 23 +++ + drivers/net/ethernet/airoha/airoha_regs.h | 22 ++- + 3 files changed, 208 insertions(+), 15 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + + #include "airoha_regs.h" +@@ -439,6 +440,48 @@ static void airoha_fe_crsn_qsel_init(str + CDM_CRSN_QSEL_Q1)); + } + ++static void airoha_fe_lro_init_rx_queue(struct airoha_eth *eth, int qdma_id, ++ int lro_queue_index, int qid, ++ int buf_size) ++{ ++ int id = qdma_id + 1; ++ ++ airoha_fe_rmw(eth, REG_CDM_LRO_LIMIT(id), ++ CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK, ++ FIELD_PREP(CDM_LRO_AGG_SIZE_MASK, buf_size) | ++ FIELD_PREP(CDM_LRO_AGG_NUM_MASK, ++ AIROHA_RXQ_LRO_MAX_AGG_COUNT)); ++ airoha_fe_rmw(eth, REG_CDM_LRO_AGE_TIME(id), ++ CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK, ++ FIELD_PREP(CDM_LRO_AGE_TIME_MASK, ++ AIROHA_RXQ_LRO_MAX_AGE_TIME) | ++ FIELD_PREP(CDM_LRO_AGG_TIME_MASK, ++ AIROHA_RXQ_LRO_MAX_AGG_TIME)); ++ airoha_fe_rmw(eth, REG_CDM_LRO_RXQ(id, lro_queue_index), ++ LRO_RXQ_MASK(lro_queue_index), ++ __field_prep(LRO_RXQ_MASK(lro_queue_index), qid)); ++ airoha_fe_set(eth, REG_CDM_LRO_EN(id), BIT(lro_queue_index)); ++} ++ ++static void airoha_fe_lro_disable(struct airoha_eth *eth, int qdma_id) ++{ ++ int i, id = qdma_id + 1; ++ ++ airoha_fe_clear(eth, REG_CDM_LRO_EN(id), LRO_RXQ_EN_MASK); ++ airoha_fe_clear(eth, REG_CDM_LRO_LIMIT(id), ++ CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK); ++ airoha_fe_clear(eth, REG_CDM_LRO_AGE_TIME(id), ++ CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK); ++ for (i = 0; i < AIROHA_MAX_NUM_LRO_QUEUES; i++) ++ airoha_fe_clear(eth, REG_CDM_LRO_RXQ(id, i), LRO_RXQ_MASK(i)); ++} ++ ++static bool airoha_fe_lro_is_enabled(struct airoha_eth *eth, int qdma_id) ++{ ++ return airoha_fe_get(eth, REG_CDM_LRO_EN(qdma_id + 1), ++ LRO_RXQ_EN_MASK); ++} ++ + static int airoha_fe_init(struct airoha_eth *eth) + { + airoha_fe_maccr_init(eth); +@@ -603,6 +646,85 @@ static int airoha_qdma_get_gdm_port(stru + return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; + } + ++static int airoha_qdma_lro_rx_process(struct airoha_queue *q, ++ struct airoha_qdma_desc *desc) ++{ ++ u32 desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); ++ u32 msg1 = le32_to_cpu(READ_ONCE(desc->msg1)); ++ u32 msg2 = le32_to_cpu(READ_ONCE(desc->msg2)); ++ u32 msg3 = le32_to_cpu(READ_ONCE(desc->msg3)); ++ struct sk_buff *skb = q->skb; ++ u32 len, th_off, tcp_ack_seq; ++ u16 tcp_win, l2_len; ++ struct tcphdr *th; ++ bool ipv4, ipv6; ++ ++ if (FIELD_GET(QDMA_ETH_RXMSG_AGG_COUNT_MASK, msg2) <= 1) ++ return 0; ++ ++ ipv4 = FIELD_GET(QDMA_ETH_RXMSG_IP4_MASK, msg1); ++ ipv6 = FIELD_GET(QDMA_ETH_RXMSG_IP6_MASK, msg1); ++ if (!ipv4 && !ipv6) ++ return -EOPNOTSUPP; ++ ++ l2_len = FIELD_GET(QDMA_ETH_RXMSG_L2_LEN_MASK, msg2); ++ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); ++ if (ipv4) { ++ struct iphdr *iph; ++ ++ if (!pskb_may_pull(skb, l2_len + sizeof(*iph))) ++ return -EINVAL; ++ ++ iph = (struct iphdr *)(skb->data + l2_len); ++ if (iph->protocol != IPPROTO_TCP) ++ return -EOPNOTSUPP; ++ ++ iph->tot_len = cpu_to_be16(len - l2_len); ++ iph->check = 0; ++ iph->check = ip_fast_csum((void *)iph, iph->ihl); ++ th_off = l2_len + (iph->ihl << 2); ++ } else { ++ struct ipv6hdr *ip6h; ++ ++ if (!pskb_may_pull(skb, l2_len + sizeof(*ip6h))) ++ return -EINVAL; ++ ++ ip6h = (struct ipv6hdr *)(skb->data + l2_len); ++ if (ip6h->nexthdr != NEXTHDR_TCP) ++ return -EOPNOTSUPP; ++ ++ ip6h->payload_len = cpu_to_be16(len - l2_len - sizeof(*ip6h)); ++ th_off = l2_len + sizeof(*ip6h); ++ } ++ ++ tcp_win = FIELD_GET(QDMA_ETH_RXMSG_TCP_WIN_MASK, msg3); ++ tcp_ack_seq = le32_to_cpu(READ_ONCE(desc->data)); ++ ++ if (!pskb_may_pull(skb, th_off + sizeof(*th))) ++ return -EINVAL; ++ ++ th = (struct tcphdr *)(skb->data + th_off); ++ th->ack_seq = cpu_to_be32(tcp_ack_seq); ++ th->window = cpu_to_be16(tcp_win); ++ ++ /* Check tcp timestamp option */ ++ if (th->doff == (sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4) { ++ __be32 *topt = (__be32 *)(th + 1); ++ ++ if (*topt == cpu_to_be32((TCPOPT_NOP << 24) | ++ (TCPOPT_NOP << 16) | ++ (TCPOPT_TIMESTAMP << 8) | ++ TCPOLEN_TIMESTAMP)) { ++ __le32 tcp_ts_reply = READ_ONCE(desc->tcp_ts_reply); ++ ++ put_unaligned_be32(le32_to_cpu(tcp_ts_reply), ++ topt + 2); ++ } ++ } ++ ++ return 0; ++} ++ + static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) + { + enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool); +@@ -650,11 +772,15 @@ static int airoha_qdma_rx_process(struct + + skb_reserve(q->skb, AIROHA_RX_HEADROOM); + __skb_put(q->skb, len); +- skb_mark_for_recycle(q->skb); + q->skb->dev = port->dev; +- q->skb->protocol = eth_type_trans(q->skb, port->dev); + q->skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_record_rx_queue(q->skb, qid); ++ ++ if (airoha_qdma_lro_rx_process(q, desc) < 0) ++ goto free_frag; ++ ++ q->skb->protocol = eth_type_trans(q->skb, port->dev); ++ skb_mark_for_recycle(q->skb); + } else { /* scattered frame */ + struct skb_shared_info *shinfo = skb_shinfo(q->skb); + int nr_frags = shinfo->nr_frags; +@@ -743,12 +869,10 @@ static int airoha_qdma_rx_napi_poll(stru + static int airoha_qdma_init_rx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int ndesc) + { +- const struct page_pool_params pp_params = { +- .order = 0, ++ struct page_pool_params pp_params = { + .pool_size = 256, + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .dma_dir = DMA_FROM_DEVICE, +- .max_len = PAGE_SIZE, + .nid = NUMA_NO_NODE, + .dev = qdma->eth->dev, + .napi = &q->napi, +@@ -756,9 +880,10 @@ static int airoha_qdma_init_rx_queue(str + struct airoha_eth *eth = qdma->eth; + int qid = q - &qdma->q_rx[0], thr; + dma_addr_t dma_addr; ++ bool lro_q; + +- q->buf_size = PAGE_SIZE / 2; + q->qdma = qdma; ++ lro_q = airoha_qdma_is_lro_queue(q); + + q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry), + GFP_KERNEL); +@@ -770,6 +895,9 @@ static int airoha_qdma_init_rx_queue(str + if (!q->desc) + return -ENOMEM; + ++ pp_params.order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0; ++ pp_params.max_len = PAGE_SIZE << pp_params.order; ++ + q->page_pool = page_pool_create(&pp_params); + if (IS_ERR(q->page_pool)) { + int err = PTR_ERR(q->page_pool); +@@ -778,6 +906,7 @@ static int airoha_qdma_init_rx_queue(str + return err; + } + ++ q->buf_size = lro_q ? pp_params.max_len : pp_params.max_len / 2; + q->ndesc = ndesc; + netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); + +@@ -2034,6 +2163,67 @@ int airoha_get_fe_port(struct airoha_gdm + } + } + ++static int airoha_dev_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ netdev_features_t diff = dev->features ^ features; ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_qdma *qdma = port->qdma; ++ struct airoha_eth *eth = qdma->eth; ++ int qdma_id = qdma - ð->qdma[0]; ++ int i; ++ ++ if (!(diff & NETIF_F_LRO)) ++ return 0; ++ ++ if (netif_running(dev)) ++ return -EBUSY; ++ ++ /* reset LRO configuration */ ++ if (features & NETIF_F_LRO) { ++ int lro_queue_index = 0; ++ ++ if (airoha_fe_lro_is_enabled(eth, qdma_id)) ++ return 0; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ struct airoha_queue *q = &qdma->q_rx[i]; ++ u32 size; ++ ++ if (!q->ndesc) ++ continue; ++ ++ if (!airoha_qdma_is_lro_queue(q)) ++ continue; ++ ++ size = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size)); ++ size = min_t(u32, size, CDM_LRO_AGG_SIZE_MASK); ++ airoha_fe_lro_init_rx_queue(eth, qdma_id, ++ lro_queue_index, i, size); ++ lro_queue_index++; ++ } ++ } else { ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *p = eth->ports[i]; ++ ++ if (!p) ++ continue; ++ ++ if (p->qdma != qdma) ++ continue; ++ ++ if (p->dev == dev) ++ continue; ++ ++ if (p->dev->features & NETIF_F_LRO) ++ return 0; ++ } ++ airoha_fe_lro_disable(eth, qdma_id); ++ } ++ ++ return 0; ++} ++ + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, + struct net_device *dev) + { +@@ -2933,6 +3123,7 @@ static const struct net_device_ops airoh + .ndo_stop = airoha_dev_stop, + .ndo_change_mtu = airoha_dev_change_mtu, + .ndo_select_queue = airoha_dev_select_queue, ++ .ndo_set_features = airoha_dev_set_features, + .ndo_start_xmit = airoha_dev_xmit, + .ndo_get_stats64 = airoha_dev_get_stats64, + .ndo_set_mac_address = airoha_dev_set_macaddr, +@@ -3150,12 +3341,9 @@ static int airoha_alloc_gdm_port(struct + dev->ethtool_ops = &airoha_ethtool_ops; + dev->max_mtu = AIROHA_MAX_MTU; + dev->watchdog_timeo = 5 * HZ; +- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | +- NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | +- NETIF_F_SG | NETIF_F_TSO | +- NETIF_F_HW_TC; +- dev->features |= dev->hw_features; +- dev->vlan_features = dev->hw_features; ++ dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO; ++ dev->features |= AIROHA_HW_FEATURES; ++ dev->vlan_features = AIROHA_HW_FEATURES; + dev->dev.of_node = np; + SET_NETDEV_DEV(dev, eth->dev); + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -43,6 +43,18 @@ + (_n) == 15 ? 128 : \ + (_n) == 0 ? 1024 : 16) + ++#define AIROHA_LRO_PAGE_ORDER 2 ++#define AIROHA_MAX_NUM_LRO_QUEUES 8 ++#define AIROHA_RXQ_LRO_EN_MASK 0xff000000 ++#define AIROHA_RXQ_LRO_MAX_AGG_COUNT 64 ++#define AIROHA_RXQ_LRO_MAX_AGG_TIME 100 ++#define AIROHA_RXQ_LRO_MAX_AGE_TIME 2000 /* 1ms */ ++ ++#define AIROHA_HW_FEATURES \ ++ (NETIF_F_IP_CSUM | NETIF_F_RXCSUM | \ ++ NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | \ ++ NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_TC) ++ + #define PSE_RSV_PAGES 128 + #define PSE_QUEUE_RSV_PAGES 64 + +@@ -666,6 +678,18 @@ static inline bool airoha_is_7583(struct + return eth->soc->version == 0x7583; + } + ++static inline bool airoha_qdma_is_lro_queue(struct airoha_queue *q) ++{ ++ struct airoha_qdma *qdma = q->qdma; ++ int qid = q - &qdma->q_rx[0]; ++ ++ /* EN7581 SoC supports at most 8 LRO rx queues */ ++ BUILD_BUG_ON(hweight32(AIROHA_RXQ_LRO_EN_MASK) > ++ AIROHA_MAX_NUM_LRO_QUEUES); ++ ++ return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid)); ++} ++ + int airoha_get_fe_port(struct airoha_gdm_port *port); + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -122,6 +122,20 @@ + #define CDM_CRSN_QSEL_REASON_MASK(_n) \ + GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3)) + ++#define REG_CDM_LRO_RXQ(_n, _m) (CDM_BASE(_n) + 0x78 + ((_m) & 0x4)) ++#define LRO_RXQ_MASK(_n) GENMASK(4 + (((_n) & 0x3) << 3), ((_n) & 0x3) << 3) ++ ++#define REG_CDM_LRO_EN(_n) (CDM_BASE(_n) + 0x80) ++#define LRO_RXQ_EN_MASK GENMASK(7, 0) ++ ++#define REG_CDM_LRO_LIMIT(_n) (CDM_BASE(_n) + 0x84) ++#define CDM_LRO_AGG_NUM_MASK GENMASK(23, 16) ++#define CDM_LRO_AGG_SIZE_MASK GENMASK(15, 0) ++ ++#define REG_CDM_LRO_AGE_TIME(_n) (CDM_BASE(_n) + 0x88) ++#define CDM_LRO_AGE_TIME_MASK GENMASK(31, 16) ++#define CDM_LRO_AGG_TIME_MASK GENMASK(15, 0) ++ + #define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) + #define GDM_PAD_EN_MASK BIT(28) + #define GDM_DROP_CRC_ERR_MASK BIT(23) +@@ -895,9 +909,15 @@ + #define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) + #define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) + #define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) ++/* RX MSG2 */ ++#define QDMA_ETH_RXMSG_AGG_COUNT_MASK GENMASK(31, 24) ++#define QDMA_ETH_RXMSG_L2_LEN_MASK GENMASK(6, 0) ++/* RX MSG3 */ ++#define QDMA_ETH_RXMSG_AGG_LEN_MASK GENMASK(31, 16) ++#define QDMA_ETH_RXMSG_TCP_WIN_MASK GENMASK(15, 0) + + struct airoha_qdma_desc { +- __le32 rsv; ++ __le32 tcp_ts_reply; + __le32 ctrl; + __le32 addr; + __le32 data; diff --git a/target/linux/airoha/patches-6.12/920-01-net-airoha-Introduce-airoha_gdm_dev-struct.patch b/target/linux/airoha/patches-6.12/920-01-net-airoha-Introduce-airoha_gdm_dev-struct.patch new file mode 100644 index 00000000000..0bb339c9104 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-01-net-airoha-Introduce-airoha_gdm_dev-struct.patch @@ -0,0 +1,1018 @@ +From e15783f7c987e199ecf80b3d858ed5a86d33c508 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Lorenzo Bianconi +Date: Sat, 1 Nov 2025 11:25:52 +0100 +Subject: [PATCH 01/13] net: airoha: Introduce airoha_gdm_dev struct + +EN7581 and AN7583 SoCs support connecting multiple external SerDes to GDM3 +or GDM4 ports via a hw arbiter that manages the traffic in a TDM manner. +As a result multiple net_devices can connect to the same GDM{3,4} port +and there is a theoretical "1:n" relation between GDM port and +net_devices. +Introduce airoha_gdm_dev struct to collect net_device related info (e.g. +net_device and external phy pointer). Please note this is just a +preliminary patch and we are still supporting a single net_device for +each GDM port. Subsequent patches will add support for multiple net_devices +connected to the same GDM port. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 312 ++++++++++++++--------- + drivers/net/ethernet/airoha/airoha_eth.h | 13 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 17 +- + 3 files changed, 206 insertions(+), 136 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -738,6 +738,7 @@ static int airoha_qdma_rx_process(struct + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + u32 hash, reason, msg1, desc_ctrl; + struct airoha_gdm_port *port; ++ struct net_device *netdev; + int data_len, len, p; + struct page *page; + +@@ -764,6 +765,10 @@ static int airoha_qdma_rx_process(struct + goto free_frag; + + port = eth->ports[p]; ++ if (!port->dev) ++ goto free_frag; ++ ++ netdev = port->dev->dev; + if (!q->skb) { /* first buffer */ + q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM, + q->buf_size); +@@ -772,14 +777,14 @@ static int airoha_qdma_rx_process(struct + + skb_reserve(q->skb, AIROHA_RX_HEADROOM); + __skb_put(q->skb, len); +- q->skb->dev = port->dev; ++ q->skb->dev = netdev; + q->skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_record_rx_queue(q->skb, qid); + + if (airoha_qdma_lro_rx_process(q, desc) < 0) + goto free_frag; + +- q->skb->protocol = eth_type_trans(q->skb, port->dev); ++ q->skb->protocol = eth_type_trans(q->skb, netdev); + skb_mark_for_recycle(q->skb); + } else { /* scattered frame */ + struct skb_shared_info *shinfo = skb_shinfo(q->skb); +@@ -796,7 +801,7 @@ static int airoha_qdma_rx_process(struct + if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl)) + continue; + +- if (netdev_uses_dsa(port->dev)) { ++ if (netdev_uses_dsa(netdev)) { + /* PPE module requires untagged packets to work + * properly and it provides DSA port index via the + * DMA descriptor. Report DSA tag to the DSA stack +@@ -993,6 +998,7 @@ static void airoha_qdma_wake_netdev_txqs + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; ++ struct airoha_gdm_dev *dev; + int j; + + if (!port) +@@ -1001,11 +1007,12 @@ static void airoha_qdma_wake_netdev_txqs + if (port->qdma != qdma) + continue; + +- for (j = 0; j < port->dev->num_tx_queues; j++) { ++ dev = port->dev; ++ for (j = 0; j < dev->dev->num_tx_queues; j++) { + if (airoha_qdma_get_txq(qdma, j) != qid) + continue; + +- netif_wake_subqueue(port->dev, j); ++ netif_wake_subqueue(dev->dev, j); + } + } + q->txq_stopped = false; +@@ -1849,33 +1856,34 @@ static void airoha_update_hw_stats(struc + spin_unlock(&port->stats.lock); + } + +-static int airoha_dev_open(struct net_device *dev) ++static int airoha_dev_open(struct net_device *netdev) + { +- int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_qdma *qdma = port->qdma; + u32 pse_port = FE_PSE_PORT_PPE1; + + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { +- err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0); ++ err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0); + if (err) { +- netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, ++ netdev_err(netdev, "%s: could not attach PHY: %d\n", __func__, + err); + return err; + } + +- phylink_start(port->phylink); ++ phylink_start(dev->phylink); + } + #endif + +- netif_tx_start_all_queues(dev); ++ netif_tx_start_all_queues(netdev); + err = airoha_set_vip_for_gdm_port(port, true); + if (err) + return err; + + /* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */ +- if (netdev_uses_dsa(dev) || port->id > 2) ++ if (netdev_uses_dsa(netdev) || port->id > 2) + airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + else +@@ -1903,16 +1911,17 @@ static int airoha_dev_open(struct net_de + return 0; + } + +-static int airoha_dev_stop(struct net_device *dev) ++static int airoha_dev_stop(struct net_device *netdev) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_qdma *qdma = port->qdma; + int i; + +- netif_tx_disable(dev); ++ netif_tx_disable(netdev); + airoha_set_vip_for_gdm_port(port, false); +- for (i = 0; i < dev->num_tx_queues; i++) +- netdev_tx_reset_subqueue(dev, i); ++ for (i = 0; i < netdev->num_tx_queues; i++) ++ netdev_tx_reset_subqueue(netdev, i); + + airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), + FE_PSE_PORT_DROP); +@@ -1932,24 +1941,25 @@ static int airoha_dev_stop(struct net_de + + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { +- phylink_stop(port->phylink); +- phylink_disconnect_phy(port->phylink); ++ phylink_stop(dev->phylink); ++ phylink_disconnect_phy(dev->phylink); + } + #endif + + return 0; + } + +-static int airoha_dev_set_macaddr(struct net_device *dev, void *p) ++static int airoha_dev_set_macaddr(struct net_device *netdev, void *p) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + int err; + +- err = eth_mac_addr(dev, p); ++ err = eth_mac_addr(netdev, p); + if (err) + return err; + +- airoha_set_macaddr(port, dev->dev_addr); ++ airoha_set_macaddr(port, netdev->dev_addr); + + return 0; + } +@@ -2015,16 +2025,17 @@ static int airoha_set_gdm2_loopback(stru + return 0; + } + +-static int airoha_dev_init(struct net_device *dev) ++static int airoha_dev_init(struct net_device *netdev) + { +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->eth; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; + int i; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ + port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; +- port->dev->irq = port->qdma->irq_banks[0].irq; +- airoha_set_macaddr(port, dev->dev_addr); ++ dev->dev->irq = port->qdma->irq_banks[0].irq; ++ airoha_set_macaddr(port, netdev->dev_addr); + + switch (port->id) { + case AIROHA_GDM3_IDX: +@@ -2049,10 +2060,11 @@ static int airoha_dev_init(struct net_de + return 0; + } + +-static void airoha_dev_get_stats64(struct net_device *dev, ++static void airoha_dev_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *storage) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + unsigned int start; + + airoha_update_hw_stats(port); +@@ -2071,36 +2083,39 @@ static void airoha_dev_get_stats64(struc + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + +-static int airoha_dev_change_mtu(struct net_device *dev, int mtu) ++static int airoha_dev_change_mtu(struct net_device *netdev, int mtu) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = port->qdma->eth; + u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; + + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), + GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_LONG_LEN_MASK, len)); +- WRITE_ONCE(dev->mtu, mtu); ++ WRITE_ONCE(netdev->mtu, mtu); + + return 0; + } + +-static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, ++static u16 airoha_dev_select_queue(struct net_device *netdev, ++ struct sk_buff *skb, + struct net_device *sb_dev) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + int queue, channel; + + /* For dsa device select QoS channel according to the dsa user port + * index, rely on port id otherwise. Select QoS queue based on the + * skb priority. + */ +- channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; ++ channel = netdev_uses_dsa(netdev) ? skb_get_queue_mapping(skb) : port->id; + channel = channel % AIROHA_NUM_QOS_CHANNELS; + queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ + queue = channel * AIROHA_NUM_QOS_QUEUES + queue; + +- return queue < dev->num_tx_queues ? queue : 0; ++ return queue < netdev->num_tx_queues ? queue : 0; + } + + static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev) +@@ -2163,11 +2178,12 @@ int airoha_get_fe_port(struct airoha_gdm + } + } + +-static int airoha_dev_set_features(struct net_device *dev, ++static int airoha_dev_set_features(struct net_device *netdev, + netdev_features_t features) + { +- netdev_features_t diff = dev->features ^ features; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ netdev_features_t diff = netdev->features ^ features; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_qdma *qdma = port->qdma; + struct airoha_eth *eth = qdma->eth; + int qdma_id = qdma - ð->qdma[0]; +@@ -2176,7 +2192,7 @@ static int airoha_dev_set_features(struc + if (!(diff & NETIF_F_LRO)) + return 0; + +- if (netif_running(dev)) ++ if (netif_running(netdev)) + return -EBUSY; + + /* reset LRO configuration */ +@@ -2205,6 +2221,7 @@ static int airoha_dev_set_features(struc + } else { + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *p = eth->ports[i]; ++ struct airoha_gdm_dev *d; + + if (!p) + continue; +@@ -2212,10 +2229,11 @@ static int airoha_dev_set_features(struc + if (p->qdma != qdma) + continue; + +- if (p->dev == dev) ++ d = p->dev; ++ if (d->dev == netdev) + continue; + +- if (p->dev->features & NETIF_F_LRO) ++ if (d->dev->features & NETIF_F_LRO) + return 0; + } + airoha_fe_lro_disable(eth, qdma_id); +@@ -2225,9 +2243,10 @@ static int airoha_dev_set_features(struc + } + + static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, +- struct net_device *dev) ++ struct net_device *netdev) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_qdma *qdma = port->qdma; + u32 nr_frags, tag, msg0, msg1, len; + struct airoha_queue_entry *e; +@@ -2240,7 +2259,7 @@ static netdev_tx_t airoha_dev_xmit(struc + u8 fport; + + qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); +- tag = airoha_get_dsa_tag(skb, dev); ++ tag = airoha_get_dsa_tag(skb, netdev); + + msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, + qid / AIROHA_NUM_QOS_QUEUES) | +@@ -2276,7 +2295,7 @@ static netdev_tx_t airoha_dev_xmit(struc + + spin_lock_bh(&q->lock); + +- txq = skb_get_tx_queue(dev, skb); ++ txq = skb_get_tx_queue(netdev, skb); + nr_frags = 1 + skb_shinfo(skb)->nr_frags; + + if (q->queued + nr_frags >= q->ndesc) { +@@ -2300,9 +2319,9 @@ static netdev_tx_t airoha_dev_xmit(struc + dma_addr_t addr; + u32 val; + +- addr = dma_map_single(dev->dev.parent, data, len, ++ addr = dma_map_single(netdev->dev.parent, data, len, + DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(dev->dev.parent, addr))) ++ if (unlikely(dma_mapping_error(netdev->dev.parent, addr))) + goto error_unmap; + + list_move_tail(&e->list, &tx_list); +@@ -2351,7 +2370,7 @@ static netdev_tx_t airoha_dev_xmit(struc + + error_unmap: + list_for_each_entry(e, &tx_list, list) { +- dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len, ++ dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); + e->dma_addr = 0; + } +@@ -2360,25 +2379,27 @@ error_unmap: + spin_unlock_bh(&q->lock); + error: + dev_kfree_skb_any(skb); +- dev->stats.tx_dropped++; ++ netdev->stats.tx_dropped++; + + return NETDEV_TX_OK; + } + +-static void airoha_ethtool_get_drvinfo(struct net_device *dev, ++static void airoha_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = port->qdma->eth; + + strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); + } + +-static void airoha_ethtool_get_mac_stats(struct net_device *dev, ++static void airoha_ethtool_get_mac_stats(struct net_device *netdev, + struct ethtool_eth_mac_stats *stats) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + unsigned int start; + + airoha_update_hw_stats(port); +@@ -2406,11 +2427,12 @@ static const struct ethtool_rmon_hist_ra + }; + + static void +-airoha_ethtool_get_rmon_stats(struct net_device *dev, ++airoha_ethtool_get_rmon_stats(struct net_device *netdev, + struct ethtool_rmon_stats *stats, + const struct ethtool_rmon_hist_range **ranges) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_hw_stats *hw_stats = &port->stats; + unsigned int start; + +@@ -2435,11 +2457,12 @@ airoha_ethtool_get_rmon_stats(struct net + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + +-static int airoha_qdma_set_chan_tx_sched(struct net_device *dev, ++static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, + int channel, enum tx_sched_mode mode, + const u16 *weights, u8 n_weights) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + int i; + + for (i = 0; i < AIROHA_NUM_TX_RING; i++) +@@ -2524,10 +2547,12 @@ static int airoha_qdma_set_tx_ets_sched( + ARRAY_SIZE(w)); + } + +-static int airoha_qdma_get_tx_ets_stats(struct net_device *dev, int channel, ++static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel, + struct tc_ets_qopt_offload *opt) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; ++ + u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, + REG_CNTR_VAL(channel << 1)); + u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, +@@ -2789,11 +2814,12 @@ static int airoha_qdma_set_trtcm_token_b + mode, val); + } + +-static int airoha_qdma_set_tx_rate_limit(struct net_device *dev, ++static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev, + int channel, u32 rate, + u32 bucket_size) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + int i, err; + + for (i = 0; i <= TRTCM_PEAK_MODE; i++) { +@@ -2813,20 +2839,22 @@ static int airoha_qdma_set_tx_rate_limit + return 0; + } + +-static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev, ++static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev, + struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; + u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ +- int err, num_tx_queues = dev->real_num_tx_queues; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ int err, num_tx_queues = netdev->real_num_tx_queues; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + + if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); + return -EINVAL; + } + +- err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum); ++ err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate, ++ opt->quantum); + if (err) { + NL_SET_ERR_MSG_MOD(opt->extack, + "failed configuring htb offload"); +@@ -2836,9 +2864,10 @@ static int airoha_tc_htb_alloc_leaf_queu + if (opt->command == TC_HTB_NODE_MODIFY) + return 0; + +- err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); ++ err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1); + if (err) { +- airoha_qdma_set_tx_rate_limit(dev, channel, 0, opt->quantum); ++ airoha_qdma_set_tx_rate_limit(netdev, channel, 0, ++ opt->quantum); + NL_SET_ERR_MSG_MOD(opt->extack, + "failed setting real_num_tx_queues"); + return err; +@@ -2928,11 +2957,12 @@ static int airoha_tc_matchall_act_valida + return 0; + } + +-static int airoha_dev_tc_matchall(struct net_device *dev, ++static int airoha_dev_tc_matchall(struct net_device *netdev, + struct tc_cls_matchall_offload *f) + { + enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + u32 rate = 0, bucket_size = 0; + + switch (f->command) { +@@ -2967,18 +2997,19 @@ static int airoha_dev_tc_matchall(struct + static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type, + void *type_data, void *cb_priv) + { +- struct net_device *dev = cb_priv; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct net_device *netdev = cb_priv; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = port->qdma->eth; + +- if (!tc_can_offload(dev)) ++ if (!tc_can_offload(netdev)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data); + case TC_SETUP_CLSMATCHALL: +- return airoha_dev_tc_matchall(dev, type_data); ++ return airoha_dev_tc_matchall(netdev, type_data); + default: + return -EOPNOTSUPP; + } +@@ -3025,47 +3056,51 @@ static int airoha_dev_setup_tc_block(str + } + } + +-static void airoha_tc_remove_htb_queue(struct net_device *dev, int queue) ++static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + +- netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); +- airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0); ++ netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1); ++ airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0); + clear_bit(queue, port->qos_sq_bmap); + } + +-static int airoha_tc_htb_delete_leaf_queue(struct net_device *dev, ++static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev, + struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + + if (!test_bit(channel, port->qos_sq_bmap)) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); + return -EINVAL; + } + +- airoha_tc_remove_htb_queue(dev, channel); ++ airoha_tc_remove_htb_queue(netdev, channel); + + return 0; + } + +-static int airoha_tc_htb_destroy(struct net_device *dev) ++static int airoha_tc_htb_destroy(struct net_device *netdev) + { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + int q; + + for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) +- airoha_tc_remove_htb_queue(dev, q); ++ airoha_tc_remove_htb_queue(netdev, q); + + return 0; + } + +-static int airoha_tc_get_htb_get_leaf_queue(struct net_device *dev, ++static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev, + struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + + if (!test_bit(channel, port->qos_sq_bmap)) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); +@@ -3101,8 +3136,8 @@ static int airoha_tc_setup_qdisc_htb(str + return 0; + } + +-static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, +- void *type_data) ++static int airoha_dev_tc_setup(struct net_device *dev, ++ enum tc_setup_type type, void *type_data) + { + switch (type) { + case TC_SETUP_QDISC_ETS: +@@ -3174,13 +3209,18 @@ static void airoha_metadata_dst_free(str + } + } + +-bool airoha_is_valid_gdm_port(struct airoha_eth *eth, +- struct airoha_gdm_port *port) ++bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, ++ struct airoha_gdm_dev *dev) + { + int i; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { +- if (eth->ports[i] == port) ++ struct airoha_gdm_port *port = eth->ports[i]; ++ ++ if (!port) ++ continue; ++ ++ if (port->dev == dev) + return true; + } + +@@ -3192,8 +3232,9 @@ static void airoha_mac_link_up(struct ph + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause) + { +- struct airoha_gdm_port *port = container_of(config, struct airoha_gdm_port, +- phylink_config); ++ struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev, ++ phylink_config); ++ struct airoha_gdm_port *port = dev->port; + struct airoha_qdma *qdma = port->qdma; + struct airoha_eth *eth = qdma->eth; + u32 frag_size_tx, frag_size_rx; +@@ -3249,65 +3290,122 @@ static int airoha_fill_available_pcs(str + &num_available_pcs); + } + +-static int airoha_setup_phylink(struct net_device *dev) ++static int airoha_setup_phylink(struct net_device *netdev) + { +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct device_node *np = dev->dev.of_node; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct device_node *np = netdev->dev.of_node; + phy_interface_t phy_mode; + struct phylink *phylink; + int err; + + err = of_get_phy_mode(np, &phy_mode); + if (err) { +- dev_err(&dev->dev, "incorrect phy-mode\n"); ++ dev_err(&netdev->dev, "incorrect phy-mode\n"); + return err; + } + +- port->phylink_config.dev = &dev->dev; +- port->phylink_config.type = PHYLINK_NETDEV; +- port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +- MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | +- MAC_5000FD | MAC_10000FD; ++ dev->phylink_config.dev = &netdev->dev; ++ dev->phylink_config.type = PHYLINK_NETDEV; ++ dev->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | ++ MAC_5000FD | MAC_10000FD; + +- err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL, +- &port->phylink_config.num_available_pcs); ++ err = fwnode_phylink_pcs_parse(dev_fwnode(&netdev->dev), NULL, ++ &dev->phylink_config.num_available_pcs); + if (err) + return err; + +- port->phylink_config.fill_available_pcs = airoha_fill_available_pcs; ++ dev->phylink_config.fill_available_pcs = airoha_fill_available_pcs; + + __set_bit(PHY_INTERFACE_MODE_SGMII, +- port->phylink_config.supported_interfaces); ++ dev->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, +- port->phylink_config.supported_interfaces); ++ dev->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, +- port->phylink_config.supported_interfaces); ++ dev->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, +- port->phylink_config.supported_interfaces); ++ dev->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, +- port->phylink_config.supported_interfaces); ++ dev->phylink_config.supported_interfaces); + +- phy_interface_copy(port->phylink_config.pcs_interfaces, +- port->phylink_config.supported_interfaces); ++ phy_interface_copy(dev->phylink_config.pcs_interfaces, ++ dev->phylink_config.supported_interfaces); + +- phylink = phylink_create(&port->phylink_config, ++ phylink = phylink_create(&dev->phylink_config, + of_fwnode_handle(np), + phy_mode, &airoha_phylink_ops); + if (IS_ERR(phylink)) + return PTR_ERR(phylink); + +- port->phylink = phylink; ++ dev->phylink = phylink; + + return err; + } + #endif + ++static int airoha_alloc_gdm_device(struct airoha_eth *eth, ++ struct airoha_gdm_port *port, ++ struct device_node *np) ++{ ++ struct airoha_gdm_dev *dev; ++ struct net_device *netdev; ++ int err; ++ ++ netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev), ++ AIROHA_NUM_NETDEV_TX_RINGS, ++ AIROHA_NUM_RX_RING); ++ if (!netdev) { ++ dev_err(eth->dev, "alloc_etherdev failed\n"); ++ return -ENOMEM; ++ } ++ ++ netdev->netdev_ops = &airoha_netdev_ops; ++ netdev->ethtool_ops = &airoha_ethtool_ops; ++ netdev->max_mtu = AIROHA_MAX_MTU; ++ netdev->watchdog_timeo = 5 * HZ; ++ netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO; ++ netdev->features |= AIROHA_HW_FEATURES; ++ netdev->vlan_features = AIROHA_HW_FEATURES; ++ netdev->dev.of_node = np; ++ SET_NETDEV_DEV(netdev, eth->dev); ++ ++ /* reserve hw queues for HTB offloading */ ++ err = netif_set_real_num_tx_queues(netdev, AIROHA_NUM_TX_RING); ++ if (err) ++ return err; ++ ++ err = of_get_ethdev_address(np, netdev); ++ if (err) { ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ eth_hw_addr_random(netdev); ++ dev_info(eth->dev, "generated random MAC address %pM\n", ++ netdev->dev_addr); ++ } ++ ++ dev = netdev_priv(netdev); ++ dev->dev = netdev; ++ dev->port = port; ++ port->dev = dev; ++ dev->eth = eth; ++ ++#if defined(CONFIG_PCS_AIROHA) ++ if (airhoa_is_phy_external(port)) { ++ err = airoha_setup_phylink(netdev); ++ if (err) ++ return err; ++ } ++#endif ++ ++ return 0; ++} ++ + static int airoha_alloc_gdm_port(struct airoha_eth *eth, + struct device_node *np) + { + const __be32 *id_ptr = of_get_property(np, "reg", NULL); + struct airoha_gdm_port *port; +- struct net_device *dev; + int err, p; + u32 id; + +@@ -3329,58 +3427,22 @@ static int airoha_alloc_gdm_port(struct + return -EINVAL; + } + +- dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), +- AIROHA_NUM_NETDEV_TX_RINGS, +- AIROHA_NUM_RX_RING); +- if (!dev) { +- dev_err(eth->dev, "alloc_etherdev failed\n"); ++ port = devm_kzalloc(eth->dev, sizeof(*port), GFP_KERNEL); ++ if (!port) + return -ENOMEM; +- } +- +- dev->netdev_ops = &airoha_netdev_ops; +- dev->ethtool_ops = &airoha_ethtool_ops; +- dev->max_mtu = AIROHA_MAX_MTU; +- dev->watchdog_timeo = 5 * HZ; +- dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO; +- dev->features |= AIROHA_HW_FEATURES; +- dev->vlan_features = AIROHA_HW_FEATURES; +- dev->dev.of_node = np; +- SET_NETDEV_DEV(dev, eth->dev); +- +- /* reserve hw queues for HTB offloading */ +- err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING); +- if (err) +- return err; + +- err = of_get_ethdev_address(np, dev); +- if (err) { +- if (err == -EPROBE_DEFER) +- return err; +- +- eth_hw_addr_random(dev); +- dev_info(eth->dev, "generated random MAC address %pM\n", +- dev->dev_addr); +- } +- +- port = netdev_priv(dev); + u64_stats_init(&port->stats.syncp); + spin_lock_init(&port->stats.lock); +- port->eth = eth; +- port->dev = dev; + port->id = id; + /* XXX: Read nbq from DTS */ + port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + eth->ports[p] = port; + +-#if defined(CONFIG_PCS_AIROHA) +- if (airhoa_is_phy_external(port)) { +- err = airoha_setup_phylink(dev); +- if (err) +- return err; +- } +-#endif ++ err = airoha_metadata_dst_alloc(port); ++ if (err) ++ return err; + +- return airoha_metadata_dst_alloc(port); ++ return airoha_alloc_gdm_device(eth, port, np); + } + + static int airoha_register_gdm_devices(struct airoha_eth *eth) +@@ -3394,7 +3456,7 @@ static int airoha_register_gdm_devices(s + if (!port) + continue; + +- err = register_netdev(port->dev); ++ err = register_netdev(port->dev->dev); + if (err) + return err; + } +@@ -3503,16 +3565,18 @@ error_napi_stop: + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; ++ struct airoha_gdm_dev *dev; + + if (!port) + continue; + +- if (port->dev->reg_state == NETREG_REGISTERED) { ++ dev = port->dev; ++ if (dev && dev->dev->reg_state == NETREG_REGISTERED) { + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) +- phylink_destroy(port->phylink); ++ phylink_destroy(dev->phylink); + #endif +- unregister_netdev(port->dev); ++ unregister_netdev(dev->dev); + } + airoha_metadata_dst_free(port); + } +@@ -3534,15 +3598,19 @@ static void airoha_remove(struct platfor + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; ++ struct airoha_gdm_dev *dev; + + if (!port) + continue; + + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) +- phylink_destroy(port->phylink); ++ phylink_destroy(dev->phylink); + #endif +- unregister_netdev(port->dev); ++ ++ dev = port->dev; ++ if (dev) ++ unregister_netdev(dev->dev); + airoha_metadata_dst_free(port); + } + airoha_hw_cleanup(eth); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -547,17 +547,22 @@ struct airoha_qdma { + struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; + }; + +-struct airoha_gdm_port { +- struct airoha_qdma *qdma; +- struct airoha_eth *eth; ++struct airoha_gdm_dev { ++ struct airoha_gdm_port *port; + struct net_device *dev; +- int id; +- int nbq; ++ struct airoha_eth *eth; + + #if defined(CONFIG_PCS_AIROHA) + struct phylink *phylink; + struct phylink_config phylink_config; + #endif ++}; ++ ++struct airoha_gdm_port { ++ struct airoha_qdma *qdma; ++ struct airoha_gdm_dev *dev; ++ int id; ++ int nbq; + + struct airoha_hw_stats stats; + +@@ -691,8 +696,8 @@ static inline bool airoha_qdma_is_lro_qu + } + + int airoha_get_fe_port(struct airoha_gdm_port *port); +-bool airoha_is_valid_gdm_port(struct airoha_eth *eth, +- struct airoha_gdm_port *port); ++bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, ++ struct airoha_gdm_dev *dev); + + void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, + u8 fport); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -297,12 +297,12 @@ static void airoha_ppe_foe_set_bridge_ad + + static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, + struct airoha_foe_entry *hwe, +- struct net_device *dev, int type, ++ struct net_device *netdev, int type, + struct airoha_flow_data *data, + int l4proto, u8 dsfield) + { + u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val; +- int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev); ++ int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev); + struct airoha_foe_mac_info_common *l2; + u8 smac_id = 0xf; + +@@ -318,10 +318,11 @@ static int airoha_ppe_foe_entry_prepare( + hwe->ib1 = val; + + val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f); +- if (dev) { ++ if (netdev) { + struct airoha_wdma_info info = {}; + +- if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) { ++ if (!airoha_ppe_get_wdma_info(netdev, data->eth.h_dest, ++ &info)) { + val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) | + FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, + FE_PSE_PORT_CDM4); +@@ -331,12 +332,14 @@ static int airoha_ppe_foe_entry_prepare( + FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID, + info.wcid); + } else { +- struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); + u8 pse_port, channel, priority; ++ struct airoha_gdm_port *port; + +- if (!airoha_is_valid_gdm_port(eth, port)) ++ if (!airoha_is_valid_gdm_dev(eth, dev)) + return -EINVAL; + ++ port = dev->port; + if (dsa_port >= 0 || eth->ports[1]) + pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 + : port->id; +@@ -1483,7 +1486,7 @@ void airoha_ppe_check_skb(struct airoha_ + void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) + { + struct airoha_eth *eth = port->qdma->eth; +- struct net_device *dev = port->dev; ++ struct net_device *dev = port->dev->dev; + const u8 *addr = dev->dev_addr; + u32 val; + diff --git a/target/linux/airoha/patches-6.12/920-02-net-airoha-Move-airoha_qdma-pointer-in-airoha_gdm_de.patch b/target/linux/airoha/patches-6.12/920-02-net-airoha-Move-airoha_qdma-pointer-in-airoha_gdm_de.patch new file mode 100644 index 00000000000..6b2ab1c70f6 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-02-net-airoha-Move-airoha_qdma-pointer-in-airoha_gdm_de.patch @@ -0,0 +1,495 @@ +From f62cea6483cc55360863d66300790a5fb9de5f7c Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Tue, 24 Feb 2026 18:43:05 +0100 +Subject: [PATCH 02/13] net: airoha: Move airoha_qdma pointer in airoha_gdm_dev + struct + +Move airoha_qdma pointer from airoha_gdm_port struct to airoha_gdm_dev +one since the QDMA block used depends on the particular net_device +WAN/LAN configuration and in the current codebase net_device pointer is +associated to airoha_gdm_dev struct. +This is a preliminary patch to support multiple net_devices connected +to the same GDM{3,4} port via an external hw arbiter. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 105 +++++++++++------------ + drivers/net/ethernet/airoha/airoha_eth.h | 9 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 17 ++-- + 3 files changed, 64 insertions(+), 67 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -80,9 +80,10 @@ static bool airhoa_is_phy_external(struc + } + #endif + +-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) ++static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) + { +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; + u32 val, reg; + + reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H +@@ -94,7 +95,7 @@ static void airoha_set_macaddr(struct ai + airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); + airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); + +- airoha_ppe_init_upd_mem(port); ++ airoha_ppe_init_upd_mem(dev); + } + + static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, +@@ -110,10 +111,10 @@ static void airoha_set_gdm_port_fwd_cfg( + FIELD_PREP(GDM_UCFQ_MASK, val)); + } + +-static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port, +- bool enable) ++static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable) + { +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; + u32 vip_port; + + vip_port = eth->soc->ops.get_vip_port(port, port->nbq); +@@ -1004,10 +1005,13 @@ static void airoha_qdma_wake_netdev_txqs + if (!port) + continue; + +- if (port->qdma != qdma) ++ dev = port->dev; ++ if (!dev) ++ continue; ++ ++ if (dev->qdma != qdma) + continue; + +- dev = port->dev; + for (j = 0; j < dev->dev->num_tx_queues; j++) { + if (airoha_qdma_get_txq(qdma, j) != qid) + continue; +@@ -1712,9 +1716,10 @@ static void airoha_qdma_stop_napi(struct + } + } + +-static void airoha_update_hw_stats(struct airoha_gdm_port *port) ++static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) + { +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; + u32 val, i = 0; + + spin_lock(&port->stats.lock); +@@ -1861,7 +1866,7 @@ static int airoha_dev_open(struct net_de + int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + u32 pse_port = FE_PSE_PORT_PPE1; + + #if defined(CONFIG_PCS_AIROHA) +@@ -1878,7 +1883,7 @@ static int airoha_dev_open(struct net_de + #endif + + netif_tx_start_all_queues(netdev); +- err = airoha_set_vip_for_gdm_port(port, true); ++ err = airoha_set_vip_for_gdm_port(dev, true); + if (err) + return err; + +@@ -1915,11 +1920,11 @@ static int airoha_dev_stop(struct net_de + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + int i; + + netif_tx_disable(netdev); +- airoha_set_vip_for_gdm_port(port, false); ++ airoha_set_vip_for_gdm_port(dev, false); + for (i = 0; i < netdev->num_tx_queues; i++) + netdev_tx_reset_subqueue(netdev, i); + +@@ -1952,21 +1957,21 @@ static int airoha_dev_stop(struct net_de + static int airoha_dev_set_macaddr(struct net_device *netdev, void *p) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + int err; + + err = eth_mac_addr(netdev, p); + if (err) + return err; + +- airoha_set_macaddr(port, netdev->dev_addr); ++ airoha_set_macaddr(dev, netdev->dev_addr); + + return 0; + } + +-static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) ++static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev) + { +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; + u32 val, pse_port, chan; + int i, src_port; + +@@ -2013,7 +2018,7 @@ static int airoha_set_gdm2_loopback(stru + __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + + for (i = 0; i < eth->soc->num_ppe; i++) +- airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX); ++ airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX); + + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { + u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); +@@ -2033,9 +2038,9 @@ static int airoha_dev_init(struct net_de + int i; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ +- port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; +- dev->dev->irq = port->qdma->irq_banks[0].irq; +- airoha_set_macaddr(port, netdev->dev_addr); ++ dev->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; ++ dev->dev->irq = dev->qdma->irq_banks[0].irq; ++ airoha_set_macaddr(dev, netdev->dev_addr); + + switch (port->id) { + case AIROHA_GDM3_IDX: +@@ -2044,7 +2049,7 @@ static int airoha_dev_init(struct net_de + if (!eth->ports[1]) { + int err; + +- err = airoha_set_gdm2_loopback(port); ++ err = airoha_set_gdm2_loopback(dev); + if (err) + return err; + } +@@ -2054,8 +2059,7 @@ static int airoha_dev_init(struct net_de + } + + for (i = 0; i < eth->soc->num_ppe; i++) +- airoha_ppe_set_cpu_port(port, i, +- airoha_get_fe_port(port)); ++ airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev)); + + return 0; + } +@@ -2067,7 +2071,7 @@ static void airoha_dev_get_stats64(struc + struct airoha_gdm_port *port = dev->port; + unsigned int start; + +- airoha_update_hw_stats(port); ++ airoha_update_hw_stats(dev); + do { + start = u64_stats_fetch_begin(&port->stats.syncp); + storage->rx_packets = port->stats.rx_ok_pkts; +@@ -2087,8 +2091,8 @@ static int airoha_dev_change_mtu(struct + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; +- struct airoha_eth *eth = port->qdma->eth; + u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; ++ struct airoha_eth *eth = dev->eth; + + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), + GDM_LONG_LEN_MASK, +@@ -2162,10 +2166,10 @@ static u32 airoha_get_dsa_tag(struct sk_ + #endif + } + +-int airoha_get_fe_port(struct airoha_gdm_port *port) ++int airoha_get_fe_port(struct airoha_gdm_dev *dev) + { +- struct airoha_qdma *qdma = port->qdma; +- struct airoha_eth *eth = qdma->eth; ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; + + switch (eth->soc->version) { + case 0x7583: +@@ -2183,8 +2187,7 @@ static int airoha_dev_set_features(struc + { + netdev_features_t diff = netdev->features ^ features; + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + struct airoha_eth *eth = qdma->eth; + int qdma_id = qdma - ð->qdma[0]; + int i; +@@ -2226,10 +2229,10 @@ static int airoha_dev_set_features(struc + if (!p) + continue; + +- if (p->qdma != qdma) ++ d = p->dev; ++ if (d->qdma != qdma) + continue; + +- d = p->dev; + if (d->dev == netdev) + continue; + +@@ -2246,8 +2249,7 @@ static netdev_tx_t airoha_dev_xmit(struc + struct net_device *netdev) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + u32 nr_frags, tag, msg0, msg1, len; + struct airoha_queue_entry *e; + struct netdev_queue *txq; +@@ -2285,7 +2287,7 @@ static netdev_tx_t airoha_dev_xmit(struc + } + } + +- fport = airoha_get_fe_port(port); ++ fport = airoha_get_fe_port(dev); + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + +@@ -2388,8 +2390,7 @@ static void airoha_ethtool_get_drvinfo(s + struct ethtool_drvinfo *info) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_eth *eth = dev->eth; + + strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); +@@ -2402,7 +2403,7 @@ static void airoha_ethtool_get_mac_stats + struct airoha_gdm_port *port = dev->port; + unsigned int start; + +- airoha_update_hw_stats(port); ++ airoha_update_hw_stats(dev); + do { + start = u64_stats_fetch_begin(&port->stats.syncp); + stats->FramesTransmittedOK = port->stats.tx_ok_pkts; +@@ -2442,7 +2443,7 @@ airoha_ethtool_get_rmon_stats(struct net + ARRAY_SIZE(hw_stats->rx_len) + 1); + + *ranges = airoha_ethtool_rmon_ranges; +- airoha_update_hw_stats(port); ++ airoha_update_hw_stats(dev); + do { + int i; + +@@ -2462,18 +2463,17 @@ static int airoha_qdma_set_chan_tx_sched + const u16 *weights, u8 n_weights) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + int i; + + for (i = 0; i < AIROHA_NUM_TX_RING; i++) +- airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel), ++ airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel), + TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); + + for (i = 0; i < n_weights; i++) { + u32 status; + int err; + +- airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG, ++ airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG, + TWRR_RW_CMD_MASK | + FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | + FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | +@@ -2481,13 +2481,12 @@ static int airoha_qdma_set_chan_tx_sched + err = read_poll_timeout(airoha_qdma_rr, status, + status & TWRR_RW_CMD_DONE, + USEC_PER_MSEC, 10 * USEC_PER_MSEC, +- true, port->qdma, +- REG_TXWRR_WEIGHT_CFG); ++ true, dev->qdma, REG_TXWRR_WEIGHT_CFG); + if (err) + return err; + } + +- airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), ++ airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3), + CHAN_QOS_MODE_MASK(channel), + __field_prep(CHAN_QOS_MODE_MASK(channel), mode)); + +@@ -2553,9 +2552,9 @@ static int airoha_qdma_get_tx_ets_stats( + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + +- u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, ++ u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma, + REG_CNTR_VAL(channel << 1)); +- u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, ++ u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma, + REG_CNTR_VAL((channel << 1) + 1)); + u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + + (fwd_tx_packets - port->fwd_tx_packets); +@@ -2819,17 +2818,16 @@ static int airoha_qdma_set_tx_rate_limit + u32 bucket_size) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + int i, err; + + for (i = 0; i <= TRTCM_PEAK_MODE; i++) { +- err = airoha_qdma_set_trtcm_config(port->qdma, channel, ++ err = airoha_qdma_set_trtcm_config(dev->qdma, channel, + REG_EGRESS_TRTCM_CFG, i, + !!rate, TRTCM_METER_MODE); + if (err) + return err; + +- err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel, ++ err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel, + REG_EGRESS_TRTCM_CFG, + i, rate, bucket_size); + if (err) +@@ -2879,11 +2877,11 @@ static int airoha_tc_htb_alloc_leaf_queu + return 0; + } + +-static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port, ++static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev, + u32 rate, u32 bucket_size, + enum trtcm_unit_type unit_type) + { +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + int i; + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +@@ -2962,7 +2960,6 @@ static int airoha_dev_tc_matchall(struct + { + enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + u32 rate = 0, bucket_size = 0; + + switch (f->command) { +@@ -2987,7 +2984,7 @@ static int airoha_dev_tc_matchall(struct + fallthrough; + } + case TC_CLSMATCHALL_DESTROY: +- return airoha_qdma_set_rx_meter(port, rate, bucket_size, ++ return airoha_qdma_set_rx_meter(dev, rate, bucket_size, + unit_type); + default: + return -EOPNOTSUPP; +@@ -2999,8 +2996,7 @@ static int airoha_dev_setup_tc_block_cb( + { + struct net_device *netdev = cb_priv; + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_eth *eth = dev->eth; + + if (!tc_can_offload(netdev)) + return -EOPNOTSUPP; +@@ -3235,7 +3231,7 @@ static void airoha_mac_link_up(struct ph + struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev, + phylink_config); + struct airoha_gdm_port *port = dev->port; +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + struct airoha_eth *eth = qdma->eth; + u32 frag_size_tx, frag_size_rx; + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -549,6 +549,7 @@ struct airoha_qdma { + + struct airoha_gdm_dev { + struct airoha_gdm_port *port; ++ struct airoha_qdma *qdma; + struct net_device *dev; + struct airoha_eth *eth; + +@@ -559,7 +560,6 @@ struct airoha_gdm_dev { + }; + + struct airoha_gdm_port { +- struct airoha_qdma *qdma; + struct airoha_gdm_dev *dev; + int id; + int nbq; +@@ -695,19 +695,18 @@ static inline bool airoha_qdma_is_lro_qu + return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid)); + } + +-int airoha_get_fe_port(struct airoha_gdm_port *port); ++int airoha_get_fe_port(struct airoha_gdm_dev *dev); + bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, + struct airoha_gdm_dev *dev); + +-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, +- u8 fport); ++void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); + bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); + void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, + u16 hash, bool rx_wlan); + int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); +-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); ++void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev); + u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -85,9 +85,9 @@ static u32 airoha_ppe_get_timestamp(stru + AIROHA_FOE_IB1_BIND_TIMESTAMP); + } + +-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport) ++void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) + { +- struct airoha_qdma *qdma = port->qdma; ++ struct airoha_qdma *qdma = dev->qdma; + struct airoha_eth *eth = qdma->eth; + u8 qdma_id = qdma - ð->qdma[0]; + u32 fe_cpu_port; +@@ -181,8 +181,8 @@ static void airoha_ppe_hw_init(struct ai + if (!port) + continue; + +- airoha_ppe_set_cpu_port(port, i, +- airoha_get_fe_port(port)); ++ airoha_ppe_set_cpu_port(port->dev, i, ++ airoha_get_fe_port(port->dev)); + } + } + } +@@ -1483,11 +1483,12 @@ void airoha_ppe_check_skb(struct airoha_ + airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan); + } + +-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) ++void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev) + { +- struct airoha_eth *eth = port->qdma->eth; +- struct net_device *dev = port->dev->dev; +- const u8 *addr = dev->dev_addr; ++ struct airoha_gdm_port *port = dev->port; ++ struct net_device *netdev = dev->dev; ++ struct airoha_eth *eth = dev->eth; ++ const u8 *addr = netdev->dev_addr; + u32 val; + + val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; diff --git a/target/linux/airoha/patches-6.12/920-03-net-airoha-Rely-on-airoha_gdm_dev-pointer-in-airoha_.patch b/target/linux/airoha/patches-6.12/920-03-net-airoha-Rely-on-airoha_gdm_dev-pointer-in-airoha_.patch new file mode 100644 index 00000000000..dfcd606c4d6 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-03-net-airoha-Rely-on-airoha_gdm_dev-pointer-in-airoha_.patch @@ -0,0 +1,73 @@ +From 32bfd008c19f9ad55514181d8cd02e14bf384475 Mon Sep 17 00:00:00 2001 +Message-ID: <32bfd008c19f9ad55514181d8cd02e14bf384475.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Sat, 13 Dec 2025 10:06:45 +0100 +Subject: [PATCH 03/13] net: airoha: Rely on airoha_gdm_dev pointer in + airoha_is_lan_gdm_port() + +Rename airoha_is_lan_gdm_port in airoha_is_lan_gdm_dev. Moreover, rely +on airoha_gdm_dev pointer in airoha_is_lan_gdm_dev() instead of +airoha_gdm_port one. +This is a preliminary patch to support multiple net_devices connected to +the same GDM{3,4} port via an external hw arbiter. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 6 ++---- + drivers/net/ethernet/airoha/airoha_eth.h | 4 +++- + drivers/net/ethernet/airoha/airoha_ppe.c | 2 +- + 3 files changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -82,12 +82,10 @@ static bool airhoa_is_phy_external(struc + + static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) + { +- struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + u32 val, reg; + +- reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H +- : REG_FE_WAN_MAC_H; ++ reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H; + val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; + airoha_fe_wr(eth, reg, val); + +@@ -2038,7 +2036,7 @@ static int airoha_dev_init(struct net_de + int i; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ +- dev->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; ++ dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; + dev->dev->irq = dev->qdma->irq_banks[0].irq; + airoha_set_macaddr(dev, netdev->dev_addr); + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -664,8 +664,10 @@ static inline u16 airoha_qdma_get_txq(st + return qid % ARRAY_SIZE(qdma->q_tx); + } + +-static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port) ++static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev) + { ++ struct airoha_gdm_port *port = dev->port; ++ + /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. + * GDM{2,3,4} can be used as wan port connected to an external + * phy module. +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -365,7 +365,7 @@ static int airoha_ppe_foe_entry_prepare( + /* For downlink traffic consume SRAM memory for hw + * forwarding descriptors queue. + */ +- if (airoha_is_lan_gdm_port(port)) ++ if (airoha_is_lan_gdm_dev(dev)) + val |= AIROHA_FOE_IB2_FAST_PATH; + if (dsa_port >= 0) + val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, diff --git a/target/linux/airoha/patches-6.12/920-04-net-airoha-Move-qos_sq_bmap-in-airoha_gdm_dev-struct.patch b/target/linux/airoha/patches-6.12/920-04-net-airoha-Move-qos_sq_bmap-in-airoha_gdm_dev-struct.patch new file mode 100644 index 00000000000..7e0077124ac --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-04-net-airoha-Move-qos_sq_bmap-in-airoha_gdm_dev-struct.patch @@ -0,0 +1,184 @@ +From 634d75285db77f3385aa85a1bf2b185396225100 Mon Sep 17 00:00:00 2001 +Message-ID: <634d75285db77f3385aa85a1bf2b185396225100.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Fri, 10 Apr 2026 14:35:32 +0200 +Subject: [PATCH 04/13] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct + +Since now multiple net_devices connected to different QDMA blocks can +share the same GDM port, qos_sq_bmap field can be overwritten with the +configuration obtained from a net_device connected to a different QDMA +block. In order to fix the issue move qos_sq_bmap field from +airoha_gdm_port struct to airoha_gdm_dev one. +Add qos_channel_map bitmap in airoha_qdma struct to track if a shared +QDMA channel is already in use by another net_device. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 58 +++++++++++++++--------- + drivers/net/ethernet/airoha/airoha_eth.h | 6 ++- + 2 files changed, 40 insertions(+), 24 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2835,30 +2835,40 @@ static int airoha_qdma_set_tx_rate_limit + return 0; + } + +-static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev, +- struct tc_htb_qopt_offload *opt) ++static int airoha_tc_htb_modify_queue(struct net_device *dev, ++ struct tc_htb_qopt_offload *opt) + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; + u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ +- int err, num_tx_queues = netdev->real_num_tx_queues; +- struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + + if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); + return -EINVAL; + } + +- err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate, +- opt->quantum); +- if (err) { ++ return airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum); ++} ++ ++static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev, ++ struct tc_htb_qopt_offload *opt) ++{ ++ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; ++ int err, num_tx_queues = netdev->real_num_tx_queues; ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_qdma *qdma = dev->qdma; ++ ++ /* Here we need to check the requested QDMA channel is not already ++ * in use by another net_device running on the same QDMA block. ++ */ ++ if (test_and_set_bit(channel, qdma->qos_channel_map)) { + NL_SET_ERR_MSG_MOD(opt->extack, +- "failed configuring htb offload"); +- return err; ++ "qdma qos channel already in use"); ++ return -EBUSY; + } + +- if (opt->command == TC_HTB_NODE_MODIFY) +- return 0; ++ err = airoha_tc_htb_modify_queue(netdev, opt); ++ if (err) ++ goto error; + + err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1); + if (err) { +@@ -2866,13 +2876,17 @@ static int airoha_tc_htb_alloc_leaf_queu + opt->quantum); + NL_SET_ERR_MSG_MOD(opt->extack, + "failed setting real_num_tx_queues"); +- return err; ++ goto error; + } + +- set_bit(channel, port->qos_sq_bmap); ++ set_bit(channel, dev->qos_sq_bmap); + opt->qid = AIROHA_NUM_TX_RING + channel; + + return 0; ++error: ++ clear_bit(channel, qdma->qos_channel_map); ++ ++ return err; + } + + static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev, +@@ -3053,11 +3067,13 @@ static int airoha_dev_setup_tc_block(str + static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; ++ struct airoha_qdma *qdma = dev->qdma; + + netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1); + airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0); +- clear_bit(queue, port->qos_sq_bmap); ++ ++ clear_bit(queue, qdma->qos_channel_map); ++ clear_bit(queue, dev->qos_sq_bmap); + } + + static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev, +@@ -3065,9 +3081,8 @@ static int airoha_tc_htb_delete_leaf_que + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + +- if (!test_bit(channel, port->qos_sq_bmap)) { ++ if (!test_bit(channel, dev->qos_sq_bmap)) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); + return -EINVAL; + } +@@ -3080,10 +3095,9 @@ static int airoha_tc_htb_delete_leaf_que + static int airoha_tc_htb_destroy(struct net_device *netdev) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + int q; + +- for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) ++ for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) + airoha_tc_remove_htb_queue(netdev, q); + + return 0; +@@ -3094,9 +3108,8 @@ static int airoha_tc_get_htb_get_leaf_qu + { + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + +- if (!test_bit(channel, port->qos_sq_bmap)) { ++ if (!test_bit(channel, dev->qos_sq_bmap)) { + NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); + return -EINVAL; + } +@@ -3115,6 +3128,7 @@ static int airoha_tc_setup_qdisc_htb(str + case TC_HTB_DESTROY: + return airoha_tc_htb_destroy(dev); + case TC_HTB_NODE_MODIFY: ++ return airoha_tc_htb_modify_queue(dev, opt); + case TC_HTB_LEAF_ALLOC_QUEUE: + return airoha_tc_htb_alloc_leaf_queue(dev, opt); + case TC_HTB_LEAF_DEL: +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -545,6 +545,8 @@ struct airoha_qdma { + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; + struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; ++ ++ DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS); + }; + + struct airoha_gdm_dev { +@@ -557,6 +559,8 @@ struct airoha_gdm_dev { + struct phylink *phylink; + struct phylink_config phylink_config; + #endif ++ ++ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); + }; + + struct airoha_gdm_port { +@@ -566,8 +570,6 @@ struct airoha_gdm_port { + + struct airoha_hw_stats stats; + +- DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); +- + /* qos stats counters */ + u64 cpu_tx_packets; + u64 fwd_tx_packets; diff --git a/target/linux/airoha/patches-6.12/920-05-net-airoha-Move-cpu-fwd-_tx_packets-in-airoha_gdm_de.patch b/target/linux/airoha/patches-6.12/920-05-net-airoha-Move-cpu-fwd-_tx_packets-in-airoha_gdm_de.patch new file mode 100644 index 00000000000..9522e36b71d --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-05-net-airoha-Move-cpu-fwd-_tx_packets-in-airoha_gdm_de.patch @@ -0,0 +1,73 @@ +From 00272dbf6a52241a21145631f22dc5f03891078b Mon Sep 17 00:00:00 2001 +Message-ID: <00272dbf6a52241a21145631f22dc5f03891078b.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Fri, 10 Apr 2026 14:47:08 +0200 +Subject: [PATCH 05/13] net: airoha: Move {cpu,fwd}_tx_packets in + airoha_gdm_dev struct + +Since now multiple net_devices connected to different QDMA blocks can +share the same GDM port, cpu_tx_packets and fwd_tx_packets fields can +be overwritten with the value from a different QDMA block. In order to +fix the issue move cpu_tx_packets and fwd_tx_packets fields from +airoha_gdm_port struct to airoha_gdm_dev one. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 16 +++++++--------- + drivers/net/ethernet/airoha/airoha_eth.h | 7 +++---- + 2 files changed, 10 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2548,19 +2548,17 @@ static int airoha_qdma_get_tx_ets_stats( + struct tc_ets_qopt_offload *opt) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; ++ struct airoha_qdma *qdma = dev->qdma; + +- u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma, +- REG_CNTR_VAL(channel << 1)); +- u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma, ++ u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1)); ++ u64 fwd_tx_packets = airoha_qdma_rr(qdma, + REG_CNTR_VAL((channel << 1) + 1)); +- u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + +- (fwd_tx_packets - port->fwd_tx_packets); ++ u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) + ++ (fwd_tx_packets - dev->fwd_tx_packets); + + _bstats_update(opt->stats.bstats, 0, tx_packets); +- +- port->cpu_tx_packets = cpu_tx_packets; +- port->fwd_tx_packets = fwd_tx_packets; ++ dev->cpu_tx_packets = cpu_tx_packets; ++ dev->fwd_tx_packets = fwd_tx_packets; + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -561,6 +561,9 @@ struct airoha_gdm_dev { + #endif + + DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); ++ /* qos stats counters */ ++ u64 cpu_tx_packets; ++ u64 fwd_tx_packets; + }; + + struct airoha_gdm_port { +@@ -570,10 +573,6 @@ struct airoha_gdm_port { + + struct airoha_hw_stats stats; + +- /* qos stats counters */ +- u64 cpu_tx_packets; +- u64 fwd_tx_packets; +- + struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS]; + }; + diff --git a/target/linux/airoha/patches-6.12/920-06-net-airoha-Support-multiple-net_devices-for-a-single.patch b/target/linux/airoha/patches-6.12/920-06-net-airoha-Support-multiple-net_devices-for-a-single.patch new file mode 100644 index 00000000000..22f4d2920dc --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-06-net-airoha-Support-multiple-net_devices-for-a-single.patch @@ -0,0 +1,715 @@ +From 8eb0a71bfbe92b6fbc668c5d9ebdcbf6523a89ad Mon Sep 17 00:00:00 2001 +Message-ID: <8eb0a71bfbe92b6fbc668c5d9ebdcbf6523a89ad.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Sat, 1 Nov 2025 11:27:48 +0100 +Subject: [PATCH 06/13] net: airoha: Support multiple net_devices for a single + FE GDM port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +EN7581 or AN7583 SoCs support connecting multiple external SerDes (e.g. +Ethernet or USB SerDes) to GDM3 or GDM4 ports via a hw arbiter that +manages the traffic in a TDM manner. As a result multiple net_devices can +connect to the same GDM{3,4} port and there is a theoretical "1:n" +relation between GDM ports and net_devices. + + ┌─────────────────────────────────┐ + │ │ ┌──────┐ + │ P1 GDM1 ├────►MT7530│ + │ │ └──────┘ + │ │ ETH0 (DSA conduit) + │ │ + │ PSE/FE │ + │ │ + │ │ + │ │ ┌─────┐ + │ P0 CDM1 ├────►QDMA0│ + │ P4 P9 GDM4 │ └─────┘ + └──┬─────────────────────────┬────┘ + │ │ + ┌──▼──┐ ┌────▼────┐ + │ PPE │ │ ARB │ + └─────┘ └─┬─────┬─┘ + │ │ + ┌──▼──┐┌─▼───┐ + │ ETH ││ USB │ + └─────┘└─────┘ + ETH1 ETH2 + +Introduce support for multiple net_devices connected to the same Frame +Engine (FE) GDM port (GDM3 or GDM4) via an external hw arbiter. +Please note GDM1 or GDM2 does not support the connection with the external +arbiter. +Add get_dev_from_sport callback since EN7581 and AN7583 have different +logics for the net_device type connected to GDM3 or GDM4. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 274 +++++++++++++++++------ + drivers/net/ethernet/airoha/airoha_eth.h | 10 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 13 +- + 3 files changed, 228 insertions(+), 69 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -115,7 +115,7 @@ static int airoha_set_vip_for_gdm_port(s + struct airoha_eth *eth = dev->eth; + u32 vip_port; + +- vip_port = eth->soc->ops.get_vip_port(port, port->nbq); ++ vip_port = eth->soc->ops.get_vip_port(port, dev->nbq); + if (enable) { + airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); + airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); +@@ -619,30 +619,26 @@ static int airoha_qdma_fill_rx_queue(str + return nframes; + } + +-static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, +- struct airoha_qdma_desc *desc) ++static struct airoha_gdm_dev * ++airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc) + { +- u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1)); ++ struct airoha_gdm_port *port; ++ u16 p, d; + +- sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); +- switch (sport) { +- case 0x18: +- port = 3; /* GDM4 */ +- break; +- case 0x16: +- port = 2; /* GDM3 */ +- break; +- case 0x10 ... 0x14: +- port = 0; /* GDM1 */ +- break; +- case 0x2 ... 0x4: +- port = sport - 1; +- break; +- default: +- return -EINVAL; +- } ++ if (eth->soc->ops.get_dev_from_sport(desc, &p, &d)) ++ return ERR_PTR(-ENODEV); ++ ++ if (p >= ARRAY_SIZE(eth->ports)) ++ return ERR_PTR(-ENODEV); ++ ++ port = eth->ports[p]; ++ if (!port) ++ return ERR_PTR(-ENODEV); ++ ++ if (d >= ARRAY_SIZE(port->devs)) ++ return ERR_PTR(-ENODEV); + +- return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; ++ return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV); + } + + static int airoha_qdma_lro_rx_process(struct airoha_queue *q, +@@ -736,9 +732,8 @@ static int airoha_qdma_rx_process(struct + struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + u32 hash, reason, msg1, desc_ctrl; +- struct airoha_gdm_port *port; +- struct net_device *netdev; +- int data_len, len, p; ++ struct airoha_gdm_dev *dev; ++ int data_len, len; + struct page *page; + + desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); +@@ -759,15 +754,10 @@ static int airoha_qdma_rx_process(struct + if (!len || data_len < len) + goto free_frag; + +- p = airoha_qdma_get_gdm_port(eth, desc); +- if (p < 0 || !eth->ports[p]) +- goto free_frag; +- +- port = eth->ports[p]; +- if (!port->dev) ++ dev = airoha_qdma_get_gdm_dev(eth, desc); ++ if (IS_ERR(dev)) + goto free_frag; + +- netdev = port->dev->dev; + if (!q->skb) { /* first buffer */ + q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM, + q->buf_size); +@@ -776,14 +766,14 @@ static int airoha_qdma_rx_process(struct + + skb_reserve(q->skb, AIROHA_RX_HEADROOM); + __skb_put(q->skb, len); +- q->skb->dev = netdev; ++ q->skb->dev = dev->dev; + q->skb->ip_summed = CHECKSUM_UNNECESSARY; + skb_record_rx_queue(q->skb, qid); + + if (airoha_qdma_lro_rx_process(q, desc) < 0) + goto free_frag; + +- q->skb->protocol = eth_type_trans(q->skb, netdev); ++ q->skb->protocol = eth_type_trans(q->skb, dev->dev); + skb_mark_for_recycle(q->skb); + } else { /* scattered frame */ + struct skb_shared_info *shinfo = skb_shinfo(q->skb); +@@ -800,7 +790,9 @@ static int airoha_qdma_rx_process(struct + if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl)) + continue; + +- if (netdev_uses_dsa(netdev)) { ++ if (netdev_uses_dsa(dev->dev)) { ++ struct airoha_gdm_port *port = dev->port; ++ + /* PPE module requires untagged packets to work + * properly and it provides DSA port index via the + * DMA descriptor. Report DSA tag to the DSA stack +@@ -997,24 +989,27 @@ static void airoha_qdma_wake_netdev_txqs + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; +- struct airoha_gdm_dev *dev; +- int j; ++ int d; + + if (!port) + continue; + +- dev = port->dev; +- if (!dev) +- continue; ++ for (d = 0; d < ARRAY_SIZE(port->devs); d++) { ++ struct airoha_gdm_dev *dev = port->devs[d]; ++ int j; + +- if (dev->qdma != qdma) +- continue; ++ if (!dev) ++ continue; + +- for (j = 0; j < dev->dev->num_tx_queues; j++) { +- if (airoha_qdma_get_txq(qdma, j) != qid) ++ if (dev->qdma != qdma) + continue; + +- netif_wake_subqueue(dev->dev, j); ++ for (j = 0; j < dev->dev->num_tx_queues; j++) { ++ if (airoha_qdma_get_txq(qdma, j) != qid) ++ continue; ++ ++ netif_wake_subqueue(dev->dev, j); ++ } + } + } + q->txq_stopped = false; +@@ -1903,11 +1898,9 @@ static int airoha_dev_open(struct net_de + GLOBAL_CFG_RX_DMA_EN_MASK); + atomic_inc(&qdma->users); + +- if (port->id == AIROHA_GDM2_IDX && +- airoha_ppe_is_enabled(qdma->eth, 1)) { +- /* For PPE2 always use secondary cpu port. */ ++ if (!airoha_is_lan_gdm_dev(dev) && ++ airoha_ppe_is_enabled(qdma->eth, 1)) + pse_port = FE_PSE_PORT_PPE2; +- } + airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), + pse_port); + +@@ -2002,7 +1995,7 @@ static int airoha_set_gdm2_loopback(stru + airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); + airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); + +- src_port = eth->soc->ops.get_sport(port, port->nbq); ++ src_port = eth->soc->ops.get_sport(port, dev->nbq); + if (src_port < 0) + return src_port; + +@@ -2019,7 +2012,7 @@ static int airoha_set_gdm2_loopback(stru + airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX); + + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { +- u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); ++ u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq); + + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, + __field_prep(mask, AIROHA_GDM2_IDX)); +@@ -2033,7 +2026,7 @@ static int airoha_dev_init(struct net_de + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; +- int i; ++ int ppe_id; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ + dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; +@@ -2056,8 +2049,8 @@ static int airoha_dev_init(struct net_de + break; + } + +- for (i = 0; i < eth->soc->num_ppe; i++) +- airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev)); ++ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); ++ airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); + + return 0; + } +@@ -2222,20 +2215,26 @@ static int airoha_dev_set_features(struc + } else { + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *p = eth->ports[i]; +- struct airoha_gdm_dev *d; ++ int j; + + if (!p) + continue; + +- d = p->dev; +- if (d->qdma != qdma) +- continue; ++ for (j = 0; j < ARRAY_SIZE(p->devs); j++) { ++ struct airoha_gdm_dev *d = p->devs[j]; + +- if (d->dev == netdev) +- continue; ++ if (!d) ++ continue; + +- if (d->dev->features & NETIF_F_LRO) +- return 0; ++ if (d->qdma != qdma) ++ continue; ++ ++ if (d->dev == netdev) ++ continue; ++ ++ if (d->dev->features & NETIF_F_LRO) ++ return 0; ++ } + } + airoha_fe_lro_disable(eth, qdma_id); + } +@@ -2286,7 +2285,8 @@ static netdev_tx_t airoha_dev_xmit(struc + } + + fport = airoha_get_fe_port(dev); +- msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | ++ msg1 = FIELD_PREP(QDMA_ETH_TXMSG_NBOQ_MASK, dev->nbq) | ++ FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + + q = &qdma->q_tx[qid]; +@@ -3222,12 +3222,15 @@ bool airoha_is_valid_gdm_dev(struct airo + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; ++ int j; + + if (!port) + continue; + +- if (port->dev == dev) +- return true; ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ if (port->devs[j] == dev) ++ return true; ++ } + } + + return false; +@@ -3351,10 +3354,11 @@ static int airoha_setup_phylink(struct n + + static int airoha_alloc_gdm_device(struct airoha_eth *eth, + struct airoha_gdm_port *port, +- struct device_node *np) ++ int nbq, struct device_node *np) + { +- struct airoha_gdm_dev *dev; + struct net_device *netdev; ++ struct airoha_gdm_dev *dev; ++ u8 index; + int err; + + netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev), +@@ -3372,7 +3376,6 @@ static int airoha_alloc_gdm_device(struc + netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO; + netdev->features |= AIROHA_HW_FEATURES; + netdev->vlan_features = AIROHA_HW_FEATURES; +- netdev->dev.of_node = np; + SET_NETDEV_DEV(netdev, eth->dev); + + /* reserve hw queues for HTB offloading */ +@@ -3390,11 +3393,25 @@ static int airoha_alloc_gdm_device(struc + netdev->dev_addr); + } + ++ /* Allowed nbq for EN7581 on GDM3 port are 4 and 5 for PCIE0 ++ * and PCIE1 respectively. ++ */ ++ index = nbq; ++ if (index && airoha_is_7581(eth) && port->id == AIROHA_GDM3_IDX) ++ index -= 4; ++ ++ if (index >= ARRAY_SIZE(port->devs) || port->devs[index]) { ++ dev_err(eth->dev, "invalid nbq id: %d\n", nbq); ++ return -EINVAL; ++ } ++ ++ netdev->dev.of_node = of_node_get(np); + dev = netdev_priv(netdev); + dev->dev = netdev; + dev->port = port; +- port->dev = dev; + dev->eth = eth; ++ dev->nbq = nbq; ++ port->devs[index] = dev; + + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { +@@ -3412,7 +3429,8 @@ static int airoha_alloc_gdm_port(struct + { + const __be32 *id_ptr = of_get_property(np, "reg", NULL); + struct airoha_gdm_port *port; +- int err, p; ++ struct device_node *node; ++ int err, nbq, p, d = 0; + u32 id; + + if (!id_ptr) { +@@ -3440,15 +3458,51 @@ static int airoha_alloc_gdm_port(struct + u64_stats_init(&port->stats.syncp); + spin_lock_init(&port->stats.lock); + port->id = id; +- /* XXX: Read nbq from DTS */ +- port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + eth->ports[p] = port; + + err = airoha_metadata_dst_alloc(port); + if (err) + return err; + +- return airoha_alloc_gdm_device(eth, port, np); ++ /* Default nbq value to ensure backward compatibility */ ++ nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; ++ ++ for_each_child_of_node(np, node) { ++ /* Multiple external serdes connected to the FE GDM port via an ++ * external arbiter. ++ */ ++ const __be32 *nbq_ptr; ++ ++ if (!of_device_is_compatible(node, "airoha,eth-port")) ++ continue; ++ ++ d++; ++ if (!of_device_is_available(node)) ++ continue; ++ ++ nbq_ptr = of_get_property(node, "reg", NULL); ++ if (!nbq_ptr) { ++ dev_err(eth->dev, "missing nbq id\n"); ++ of_node_put(node); ++ return -EINVAL; ++ } ++ ++ /* Verify the provided nbq parameter is valid */ ++ nbq = be32_to_cpup(nbq_ptr); ++ err = eth->soc->ops.get_sport(port, nbq); ++ if (err < 0) { ++ of_node_put(node); ++ return err; ++ } ++ ++ err = airoha_alloc_gdm_device(eth, port, nbq, node); ++ if (err) { ++ of_node_put(node); ++ return err; ++ } ++ } ++ ++ return !d ? airoha_alloc_gdm_device(eth, port, nbq, np) : 0; + } + + static int airoha_register_gdm_devices(struct airoha_eth *eth) +@@ -3457,14 +3511,22 @@ static int airoha_register_gdm_devices(s + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; +- int err; ++ int j; + + if (!port) + continue; + +- err = register_netdev(port->dev->dev); +- if (err) +- return err; ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ struct airoha_gdm_dev *dev = port->devs[j]; ++ int err; ++ ++ if (!dev) ++ continue; ++ ++ err = register_netdev(dev->dev); ++ if (err) ++ return err; ++ } + } + + set_bit(DEV_STATE_REGISTERED, ð->state); +@@ -3571,18 +3633,27 @@ error_napi_stop: + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; +- struct airoha_gdm_dev *dev; ++ int j; + + if (!port) + continue; + +- dev = port->dev; +- if (dev && dev->dev->reg_state == NETREG_REGISTERED) { ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ struct airoha_gdm_dev *dev = port->devs[j]; ++ struct net_device *netdev; ++ ++ if (!dev) ++ continue; ++ ++ netdev = dev->dev; ++ if (netdev->reg_state == NETREG_REGISTERED) { + #if defined(CONFIG_PCS_AIROHA) +- if (airhoa_is_phy_external(port)) +- phylink_destroy(dev->phylink); ++ if (airhoa_is_phy_external(port)) ++ phylink_destroy(dev->phylink); + #endif +- unregister_netdev(dev->dev); ++ unregister_netdev(netdev); ++ } ++ of_node_put(netdev->dev.of_node); + } + airoha_metadata_dst_free(port); + } +@@ -3604,19 +3675,26 @@ static void airoha_remove(struct platfor + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; +- struct airoha_gdm_dev *dev; ++ int j; + + if (!port) + continue; + ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ struct airoha_gdm_dev *dev = port->devs[j]; ++ struct net_device *netdev; ++ ++ if (!dev) ++ continue; ++ + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) + phylink_destroy(dev->phylink); + #endif +- +- dev = port->dev; +- if (dev) +- unregister_netdev(dev->dev); ++ netdev = dev->dev; ++ unregister_netdev(netdev); ++ of_node_put(netdev->dev.of_node); ++ } + airoha_metadata_dst_free(port); + } + airoha_hw_cleanup(eth); +@@ -3678,6 +3756,39 @@ static u32 airoha_en7581_get_vip_port(st + return 0; + } + ++static int airoha_en7581_get_dev_from_sport(struct airoha_qdma_desc *desc, ++ u16 *port, u16 *dev) ++{ ++ u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, ++ le32_to_cpu(READ_ONCE(desc->msg1))); ++ ++ *dev = 0; ++ switch (sport) { ++ case 0x10 ... 0x14: ++ *port = 0; /* GDM1 */ ++ break; ++ case 0x2 ... 0x4: ++ *port = sport - 1; ++ break; ++ case HSGMII_LAN_7581_PCIE1_SRCPORT: ++ *dev = 1; ++ fallthrough; ++ case HSGMII_LAN_7581_PCIE0_SRCPORT: ++ *port = 2; /* GDM3 */ ++ break; ++ case HSGMII_LAN_7581_USB_SRCPORT: ++ *dev = 1; ++ fallthrough; ++ case HSGMII_LAN_7581_ETH_SRCPORT: ++ *port = 3; /* GDM4 */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static const char * const an7583_xsi_rsts_names[] = { + "hsi0-mac", + "hsi1-mac", +@@ -3726,6 +3837,36 @@ static u32 airoha_an7583_get_vip_port(st + return 0; + } + ++static int airoha_an7583_get_dev_from_sport(struct airoha_qdma_desc *desc, ++ u16 *port, u16 *dev) ++{ ++ u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, ++ le32_to_cpu(READ_ONCE(desc->msg1))); ++ ++ *dev = 0; ++ switch (sport) { ++ case 0x10 ... 0x14: ++ *port = 0; /* GDM1 */ ++ break; ++ case 0x2 ... 0x4: ++ *port = sport - 1; ++ break; ++ case HSGMII_LAN_7583_ETH_SRCPORT: ++ *port = 2; /* GDM3 */ ++ break; ++ case HSGMII_LAN_7583_USB_SRCPORT: ++ *dev = 1; ++ fallthrough; ++ case HSGMII_LAN_7583_PCIE_SRCPORT: ++ *port = 3; /* GDM4 */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static const struct airoha_eth_soc_data en7581_soc_data = { + .version = 0x7581, + .xsi_rsts_names = en7581_xsi_rsts_names, +@@ -3734,6 +3875,7 @@ static const struct airoha_eth_soc_data + .ops = { + .get_sport = airoha_en7581_get_sport, + .get_vip_port = airoha_en7581_get_vip_port, ++ .get_dev_from_sport = airoha_en7581_get_dev_from_sport, + }, + }; + +@@ -3745,6 +3887,7 @@ static const struct airoha_eth_soc_data + .ops = { + .get_sport = airoha_an7583_get_sport, + .get_vip_port = airoha_an7583_get_vip_port, ++ .get_dev_from_sport = airoha_an7583_get_dev_from_sport, + }, + }; + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -17,6 +17,7 @@ + #include + + #define AIROHA_MAX_NUM_GDM_PORTS 4 ++#define AIROHA_MAX_NUM_GDM_DEVS 2 + #define AIROHA_MAX_NUM_QDMA 2 + #define AIROHA_MAX_NUM_IRQ_BANKS 4 + #define AIROHA_MAX_DSA_PORTS 7 +@@ -552,8 +553,8 @@ struct airoha_qdma { + struct airoha_gdm_dev { + struct airoha_gdm_port *port; + struct airoha_qdma *qdma; +- struct net_device *dev; + struct airoha_eth *eth; ++ struct net_device *dev; + + #if defined(CONFIG_PCS_AIROHA) + struct phylink *phylink; +@@ -564,12 +565,13 @@ struct airoha_gdm_dev { + /* qos stats counters */ + u64 cpu_tx_packets; + u64 fwd_tx_packets; ++ ++ int nbq; + }; + + struct airoha_gdm_port { +- struct airoha_gdm_dev *dev; ++ struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS]; + int id; +- int nbq; + + struct airoha_hw_stats stats; + +@@ -605,6 +607,8 @@ struct airoha_eth_soc_data { + struct { + int (*get_sport)(struct airoha_gdm_port *port, int nbq); + u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq); ++ int (*get_dev_from_sport)(struct airoha_qdma_desc *desc, ++ u16 *port, u16 *dev); + } ops; + }; + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -168,9 +168,7 @@ static void airoha_ppe_hw_init(struct ai + airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i), + PPE_FLOW_CFG_IP6_6RD_MASK); + +- for (p = 0; p < ARRAY_SIZE(eth->ports); p++) { +- struct airoha_gdm_port *port = eth->ports[p]; +- ++ for (p = 0; p < ARRAY_SIZE(eth->ports); p++) + airoha_fe_rmw(eth, REG_PPE_MTU(i, p), + FP0_EGRESS_MTU_MASK | + FP1_EGRESS_MTU_MASK, +@@ -178,11 +176,27 @@ static void airoha_ppe_hw_init(struct ai + AIROHA_MAX_MTU) | + FIELD_PREP(FP1_EGRESS_MTU_MASK, + AIROHA_MAX_MTU)); +- if (!port) ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ int j; ++ ++ if (!port) ++ continue; ++ ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ struct airoha_gdm_dev *dev = port->devs[j]; ++ int ppe_id; ++ u8 fport; ++ ++ if (!dev) + continue; + +- airoha_ppe_set_cpu_port(port->dev, i, +- airoha_get_fe_port(port->dev)); ++ ppe_id = !airoha_is_lan_gdm_dev(dev) && ++ airoha_ppe_is_enabled(eth, 1); ++ fport = airoha_get_fe_port(dev); ++ airoha_ppe_set_cpu_port(dev, ppe_id, fport); + } + } + } diff --git a/target/linux/airoha/patches-6.12/920-07-net-airoha-Do-not-stop-GDM-port-if-it-is-shared.patch b/target/linux/airoha/patches-6.12/920-07-net-airoha-Do-not-stop-GDM-port-if-it-is-shared.patch new file mode 100644 index 00000000000..eafefba314a --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-07-net-airoha-Do-not-stop-GDM-port-if-it-is-shared.patch @@ -0,0 +1,132 @@ +From de856a5b802cf030c8e242e98df3bc88446a4ea1 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Fri, 20 Mar 2026 11:09:40 +0100 +Subject: [PATCH 07/13] net: airoha: Do not stop GDM port if it is shared + +Theoretically, in the current codebase, two independent net_devices can +be connected to the same GDM port so we need to check the GDM port is not +used by any other running net_device before setting the forward +configuration to FE_PSE_PORT_DROP. +Moreover, always set in GDM_LONG_LEN_MASK field of REG_GDM_LEN_CFG +register the maximum MTU of all running net_devices connected to the same +GDM port. + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 59 +++++++++++++++++++----- + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + 2 files changed, 48 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1859,8 +1859,8 @@ static int airoha_dev_open(struct net_de + int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; ++ u32 cur_len, pse_port = FE_PSE_PORT_PPE1; + struct airoha_qdma *qdma = dev->qdma; +- u32 pse_port = FE_PSE_PORT_PPE1; + + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { +@@ -1888,10 +1888,20 @@ static int airoha_dev_open(struct net_de + airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + +- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), +- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +- FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++ cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id), ++ GDM_LONG_LEN_MASK); ++ if (!port->users || len > cur_len) { ++ /* Opening a sibling net_device with a larger MTU updates the ++ * MTU of already running devices. This is required to allow ++ * multiple net_devices with different MTUs to share the same ++ * GDM port. ++ */ ++ airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++ } ++ port->users++; + + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | +@@ -1907,6 +1917,30 @@ static int airoha_dev_open(struct net_de + return 0; + } + ++static void airoha_set_port_mtu(struct airoha_eth *eth, ++ struct airoha_gdm_port *port) ++{ ++ u32 len = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(port->devs); i++) { ++ struct airoha_gdm_dev *dev = port->devs[i]; ++ struct net_device *netdev; ++ ++ if (!dev) ++ continue; ++ ++ netdev = dev->dev; ++ if (netif_running(netdev)) ++ len = max_t(u32, len, netdev->mtu); ++ } ++ len += ETH_HLEN + ETH_FCS_LEN; ++ ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), ++ GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++} ++ + static int airoha_dev_stop(struct net_device *netdev) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +@@ -1919,8 +1953,12 @@ static int airoha_dev_stop(struct net_de + for (i = 0; i < netdev->num_tx_queues; i++) + netdev_tx_reset_subqueue(netdev, i); + +- airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), +- FE_PSE_PORT_DROP); ++ if (--port->users) ++ airoha_set_port_mtu(dev->eth, port); ++ else ++ airoha_set_gdm_port_fwd_cfg(qdma->eth, ++ REG_GDM_FWD_CFG(port->id), ++ FE_PSE_PORT_DROP); + + if (atomic_dec_and_test(&qdma->users)) { + airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, +@@ -2082,13 +2120,10 @@ static int airoha_dev_change_mtu(struct + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; +- u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; +- struct airoha_eth *eth = dev->eth; + +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), +- GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_LONG_LEN_MASK, len)); + WRITE_ONCE(netdev->mtu, mtu); ++ if (port->users) ++ airoha_set_port_mtu(dev->eth, port); + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -572,6 +572,7 @@ struct airoha_gdm_dev { + struct airoha_gdm_port { + struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS]; + int id; ++ int users; + + struct airoha_hw_stats stats; + diff --git a/target/linux/airoha/patches-6.12/920-08-net-airoha-Introduce-WAN-device-flag.patch b/target/linux/airoha/patches-6.12/920-08-net-airoha-Introduce-WAN-device-flag.patch new file mode 100644 index 00000000000..bb9145c2280 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-08-net-airoha-Introduce-WAN-device-flag.patch @@ -0,0 +1,174 @@ +From c4f3077948eda05a6b9d1a11304d82c3e0300151 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Fri, 3 Apr 2026 12:07:27 +0200 +Subject: [PATCH 08/13] net: airoha: Introduce WAN device flag + +Introduce WAN flag to specify if a given device is used to transmit/receive +WAN or LAN traffic. Current codebase supports specifying LAN/WAN device +configuration in ndo_init() callback during device bootstrap. +In order to consider setups where LAN configuration is used even for +GDM3/GDM4 devices, check airoha_is_lan_gdm_dev() to select pse_port in +airoha_ppe_foe_entry_prepare(). +Please note after this patch, it will be possible to specify multiple LAN +devices but just a single WAN one. Please note this change is not visible +to the user since airoha_eth driver currently supports just the internal +phy available via the MT7530 DSA switch and there are no WAN interfaces +officially supported since PCS/external phy is not merged mainline yet +(it will be posted with following patches). + +Tested-by: Xuegang Lu +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 72 +++++++++++++++++++----- + drivers/net/ethernet/airoha/airoha_eth.h | 13 ++--- + drivers/net/ethernet/airoha/airoha_ppe.c | 2 +- + 3 files changed, 65 insertions(+), 22 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2059,36 +2059,80 @@ static int airoha_set_gdm2_loopback(stru + return 0; + } + +-static int airoha_dev_init(struct net_device *netdev) ++static struct airoha_gdm_dev * ++airoha_get_wan_gdm_dev(struct airoha_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ int j; ++ ++ if (!port) ++ continue; ++ ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ struct airoha_gdm_dev *dev = port->devs[j]; ++ ++ if (dev && !airoha_is_lan_gdm_dev(dev)) ++ return dev; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev) + { +- struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + int ppe_id; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ + dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; + dev->dev->irq = dev->qdma->irq_banks[0].irq; +- airoha_set_macaddr(dev, netdev->dev_addr); ++ ++ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); ++ airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); ++} ++ ++static int airoha_dev_init(struct net_device *netdev) ++{ ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; + + switch (port->id) { + case AIROHA_GDM3_IDX: +- case AIROHA_GDM4_IDX: +- /* If GDM2 is active we can't enable loopback */ +- if (!eth->ports[1]) { +- int err; +- +- err = airoha_set_gdm2_loopback(dev); +- if (err) +- return err; +- } ++ case AIROHA_GDM4_IDX: { ++ struct airoha_eth *eth = dev->eth; ++ ++ /* GDM2 supports a single net_device */ ++ if (eth->ports[1] && eth->ports[1]->devs[0]) ++ break; ++ ++ if (airoha_get_wan_gdm_dev(eth)) ++ break; ++ ++ fallthrough; ++ } ++ case AIROHA_GDM2_IDX: ++ /* GDM2 is always used as wan */ ++ dev->flags |= AIROHA_PRIV_F_WAN; + break; + default: + break; + } + +- ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); +- airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); ++ airoha_dev_set_qdma(dev); ++ airoha_set_macaddr(dev, netdev->dev_addr); ++ ++ if (!airoha_is_lan_gdm_dev(dev) && ++ (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) { ++ int err; ++ ++ err = airoha_set_gdm2_loopback(dev); ++ if (err) ++ return err; ++ } + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -550,6 +550,10 @@ struct airoha_qdma { + DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS); + }; + ++enum airoha_priv_flags { ++ AIROHA_PRIV_F_WAN = BIT(0), ++}; ++ + struct airoha_gdm_dev { + struct airoha_gdm_port *port; + struct airoha_qdma *qdma; +@@ -566,6 +570,7 @@ struct airoha_gdm_dev { + u64 cpu_tx_packets; + u64 fwd_tx_packets; + ++ u32 flags; + int nbq; + }; + +@@ -672,13 +677,7 @@ static inline u16 airoha_qdma_get_txq(st + + static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev) + { +- struct airoha_gdm_port *port = dev->port; +- +- /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. +- * GDM{2,3,4} can be used as wan port connected to an external +- * phy module. +- */ +- return port->id == 1; ++ return !(dev->flags & AIROHA_PRIV_F_WAN); + } + + static inline bool airoha_is_7581(struct airoha_eth *eth) +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -354,7 +354,7 @@ static int airoha_ppe_foe_entry_prepare( + return -EINVAL; + + port = dev->port; +- if (dsa_port >= 0 || eth->ports[1]) ++ if (dsa_port >= 0 || airoha_is_lan_gdm_dev(dev)) + pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 + : port->id; + else diff --git a/target/linux/airoha/patches-6.12/920-09-net-airoha-Support-multiple-LAN-WAN-interfaces-for-h.patch b/target/linux/airoha/patches-6.12/920-09-net-airoha-Support-multiple-LAN-WAN-interfaces-for-h.patch new file mode 100644 index 00000000000..d60794e1b75 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-09-net-airoha-Support-multiple-LAN-WAN-interfaces-for-h.patch @@ -0,0 +1,161 @@ +From 144f0e6e55896625e3411aad02399a5ebb48d8f9 Mon Sep 17 00:00:00 2001 +Message-ID: <144f0e6e55896625e3411aad02399a5ebb48d8f9.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Mon, 13 Apr 2026 17:38:51 +0200 +Subject: [PATCH 09/13] net: airoha: Support multiple LAN/WAN interfaces for hw + MAC address configuration + +The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN +MAC addresses, used to determine whether received traffic is destined for +this host or should be forwarded to another device. +The SoC hardware design assumes all interfaces configured as LAN (or WAN) +share a common upper MAC address, which is programmed into the +REG_FE_{LAN,WAN}_MAC_H register. The lower bytes of 'local' addresses can +be expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX +registers. +Previously, only a single interface was considered when programming these +registers. Extend the logic to derive the correct minimum and maximum +values for REG_FE_MAC_LMIN/REG_FE_MAC_LMAX when two or more interfaces are +configured as LAN or WAN. + +Tested-by: Madhur Agrawal +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 77 ++++++++++++++++++++---- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 4 +- + 3 files changed, 68 insertions(+), 15 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -80,20 +80,74 @@ static bool airhoa_is_phy_external(struc + } + #endif + +-static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) ++static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) + { ++ u8 ref_addr[ETH_ALEN] __aligned(2); + struct airoha_eth *eth = dev->eth; +- u32 val, reg; ++ u32 reg, val, lmin, lmax; ++ int i; ++ ++ eth_zero_addr(ref_addr); ++ lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ lmax = lmin; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ struct airoha_gdm_port *port = eth->ports[i]; ++ int j; ++ ++ if (!port) ++ continue; ++ ++ for (j = 0; j < ARRAY_SIZE(port->devs); j++) { ++ struct airoha_gdm_dev *iter_dev; ++ struct net_device *netdev; ++ ++ iter_dev = port->devs[j]; ++ if (!iter_dev || iter_dev == dev) ++ continue; ++ ++ if (airoha_is_lan_gdm_dev(iter_dev) != ++ airoha_is_lan_gdm_dev(dev)) ++ continue; ++ ++ netdev = iter_dev->dev; ++ if (netdev->reg_state != NETREG_REGISTERED) ++ continue; ++ ++ ether_addr_copy(ref_addr, netdev->dev_addr); ++ val = (netdev->dev_addr[3] << 16) | ++ (netdev->dev_addr[4] << 8) | netdev->dev_addr[5]; ++ if (val < lmin) ++ lmin = val; ++ if (val > lmax) ++ lmax = val; ++ } ++ } ++ ++ if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) { ++ /* According to the HW design, hw mac address MSBs must be ++ * the same for each net_device with the same LAN/WAN ++ * configuration. ++ */ ++ dev_warn(eth->dev, ++ "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n", ++ dev->dev->name, ref_addr[0], ref_addr[1], ++ ref_addr[2]); ++ dev_warn(eth->dev, "FE hw forwarding won't work properly\n"); ++ ++ return -EINVAL; ++ } + + reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H; + val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; + airoha_fe_wr(eth, reg, val); + +- val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; +- airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); +- airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); ++ airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin); ++ airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax); + +- airoha_ppe_init_upd_mem(dev); ++ airoha_ppe_init_upd_mem(dev, addr); ++ ++ return 0; + } + + static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, +@@ -1986,13 +2040,18 @@ static int airoha_dev_stop(struct net_de + static int airoha_dev_set_macaddr(struct net_device *netdev, void *p) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct sockaddr *addr = p; + int err; + +- err = eth_mac_addr(netdev, p); ++ err = eth_prepare_mac_addr_change(netdev, p); ++ if (err) ++ return err; ++ ++ err = airoha_set_macaddr(dev, addr->sa_data); + if (err) + return err; + +- airoha_set_macaddr(dev, netdev->dev_addr); ++ eth_commit_mac_addr_change(netdev, p); + + return 0; + } +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -713,7 +713,7 @@ void airoha_ppe_check_skb(struct airoha_ + int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); +-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev); ++void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr); + u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -1497,12 +1497,10 @@ void airoha_ppe_check_skb(struct airoha_ + airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan); + } + +-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev) ++void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr) + { + struct airoha_gdm_port *port = dev->port; +- struct net_device *netdev = dev->dev; + struct airoha_eth *eth = dev->eth; +- const u8 *addr = netdev->dev_addr; + u32 val; + + val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; diff --git a/target/linux/airoha/patches-6.12/920-10-net-airoha-Rename-airoha_set_gdm2_loopback-in-airoha.patch b/target/linux/airoha/patches-6.12/920-10-net-airoha-Rename-airoha_set_gdm2_loopback-in-airoha.patch new file mode 100644 index 00000000000..aca2ae25764 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-10-net-airoha-Rename-airoha_set_gdm2_loopback-in-airoha.patch @@ -0,0 +1,37 @@ +From 917f959d54efd09719bd5047aa4b9543615e131e Mon Sep 17 00:00:00 2001 +Message-ID: <917f959d54efd09719bd5047aa4b9543615e131e.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Fri, 19 Dec 2025 23:12:32 +0100 +Subject: [PATCH 10/13] net: airoha: Rename airoha_set_gdm2_loopback in + airoha_enable_gdm2_loopback + +This is a preliminary patch in order to allow the user to select if the +configured device will be used as hw lan or wan. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2056,7 +2056,7 @@ static int airoha_dev_set_macaddr(struct + return 0; + } + +-static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev) ++static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) + { + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; +@@ -2188,7 +2188,7 @@ static int airoha_dev_init(struct net_de + (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) { + int err; + +- err = airoha_set_gdm2_loopback(dev); ++ err = airoha_enable_gdm2_loopback(dev); + if (err) + return err; + } diff --git a/target/linux/airoha/patches-6.12/920-12-net-airoha-Add-ethtool-priv_flags-callbacks.patch b/target/linux/airoha/patches-6.12/920-12-net-airoha-Add-ethtool-priv_flags-callbacks.patch new file mode 100644 index 00000000000..f55b97253f3 --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-12-net-airoha-Add-ethtool-priv_flags-callbacks.patch @@ -0,0 +1,246 @@ +From e928621a0bbd010b75624c77105ce31f2b8d2faf Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Sat, 13 Dec 2025 09:45:09 +0100 +Subject: [PATCH 12/13] net: airoha: Add ethtool priv_flags callbacks + +Introduce ethtool priv_flags callbacks in order to allow the user to +select if the configured device will be used as hw lan or wan and enable +or disable gdm2 loopback (used for hw QoS). + +Tested-by: Madhur Agrawal +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 173 ++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_regs.h | 1 + + 2 files changed, 174 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1916,6 +1916,11 @@ static int airoha_dev_open(struct net_de + u32 cur_len, pse_port = FE_PSE_PORT_PPE1; + struct airoha_qdma *qdma = dev->qdma; + ++ if (port->id == AIROHA_GDM2_IDX && airoha_is_lan_gdm_dev(dev)) { ++ /* GDM2 can be used just as wan */ ++ return -EBUSY; ++ } ++ + #if defined(CONFIG_PCS_AIROHA) + if (airhoa_is_phy_external(port)) { + err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0); +@@ -2118,6 +2123,45 @@ static int airoha_enable_gdm2_loopback(s + return 0; + } + ++static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev) ++{ ++ struct airoha_eth *eth = dev->eth; ++ int i, src_port; ++ u32 pse_port; ++ ++ src_port = eth->soc->ops.get_sport(dev->port, dev->nbq); ++ if (src_port < 0) ++ return src_port; ++ ++ airoha_fe_clear(eth, ++ REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)), ++ SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK)); ++ ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ FE_PSE_PORT_DROP); ++ airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX), ++ LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK); ++ pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 ++ : FE_PSE_PORT_PPE1; ++ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), ++ pse_port); ++ ++ airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK, ++ FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX)); ++ ++ for (i = 0; i < eth->soc->num_ppe; i++) ++ airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX), ++ DFT_CPORT_MASK(AIROHA_GDM2_IDX)); ++ ++ /* Enable VIP and IFC for GDM2 */ ++ airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); ++ airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); ++ ++ airoha_fe_wr(eth, REG_SRC_PORT_FC_MAP6, FC_MAP6_DEF_VALUE); ++ ++ return 0; ++} ++ + static struct airoha_gdm_dev * + airoha_get_wan_gdm_dev(struct airoha_eth *eth) + { +@@ -2522,6 +2566,80 @@ error: + return NETDEV_TX_OK; + } + ++struct airoha_ethool_priv_flags { ++ char name[ETH_GSTRING_LEN]; ++ int (*handler)(struct net_device *netdev, u32 flags); ++}; ++ ++static int airoha_dev_set_wan_flag(struct net_device *netdev, u32 flags) ++{ ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; ++ ++ if (!((dev->flags ^ flags) & AIROHA_PRIV_F_WAN)) ++ return 0; ++ ++ if (netif_running(netdev)) ++ return -EBUSY; ++ ++ if (flags & AIROHA_PRIV_F_WAN) { ++ struct airoha_gdm_dev *wan_dev; ++ ++ /* Verify the wan device is not already configured */ ++ wan_dev = airoha_get_wan_gdm_dev(eth); ++ if (wan_dev && wan_dev != dev) ++ return -EBUSY; ++ ++ switch (port->id) { ++ case AIROHA_GDM2_IDX: ++ dev->flags |= AIROHA_PRIV_F_WAN; ++ airoha_dev_set_qdma(dev); ++ break; ++ case AIROHA_GDM3_IDX: ++ case AIROHA_GDM4_IDX: { ++ int err; ++ ++ dev->flags |= AIROHA_PRIV_F_WAN; ++ airoha_dev_set_qdma(dev); ++ ++ err = airoha_enable_gdm2_loopback(dev); ++ if (err) { ++ dev->flags &= ~AIROHA_PRIV_F_WAN; ++ return err; ++ } ++ break; ++ } ++ default: ++ return -EOPNOTSUPP; ++ } ++ } else { ++ switch (port->id) { ++ case AIROHA_GDM3_IDX: ++ case AIROHA_GDM4_IDX: { ++ int err; ++ ++ err = airoha_disable_gdm2_loopback(dev); ++ if (err) ++ return err; ++ break; ++ } ++ default: ++ break; ++ } ++ ++ dev->flags &= ~AIROHA_PRIV_F_WAN; ++ airoha_dev_set_qdma(dev); ++ } ++ ++ return airoha_set_macaddr(dev, netdev->dev_addr); ++} ++ ++static const struct airoha_ethool_priv_flags airoha_eth_priv_flags[] = { ++ { "wan", airoha_dev_set_wan_flag }, ++}; ++#define AIROHA_PRIV_FLAGS_STR_LEN ARRAY_SIZE(airoha_eth_priv_flags) ++ + static void airoha_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) + { +@@ -2530,6 +2648,7 @@ static void airoha_ethtool_get_drvinfo(s + + strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); ++ info->n_priv_flags = AIROHA_PRIV_FLAGS_STR_LEN; + } + + static void airoha_ethtool_get_mac_stats(struct net_device *netdev, +@@ -2594,6 +2713,56 @@ airoha_ethtool_get_rmon_stats(struct net + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + ++static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags) ++{ ++ int i; ++ ++ for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++) { ++ int err; ++ ++ if (!airoha_eth_priv_flags[i].handler) ++ continue; ++ ++ err = airoha_eth_priv_flags[i].handler(netdev, flags); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static u32 airoha_ethtool_get_priv_flags(struct net_device *netdev) ++{ ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ ++ return dev->flags; ++} ++ ++static int airoha_ethtool_get_sset_count(struct net_device *netdev, int sset) ++{ ++ switch (sset) { ++ case ETH_SS_PRIV_FLAGS: ++ return AIROHA_PRIV_FLAGS_STR_LEN; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static void airoha_ethtool_get_strings(struct net_device *netdev, ++ u32 stringset, u8 *data) ++{ ++ int i; ++ ++ switch (stringset) { ++ case ETH_SS_PRIV_FLAGS: ++ for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++) ++ ethtool_puts(&data, airoha_eth_priv_flags[i].name); ++ break; ++ default: ++ break; ++ } ++} ++ + static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, + int channel, enum tx_sched_mode mode, + const u16 *weights, u8 n_weights) +@@ -3315,6 +3484,10 @@ static const struct ethtool_ops airoha_e + .get_rmon_stats = airoha_ethtool_get_rmon_stats, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .get_link = ethtool_op_get_link, ++ .set_priv_flags = airoha_ethtool_set_priv_flags, ++ .get_priv_flags = airoha_ethtool_get_priv_flags, ++ .get_sset_count = airoha_ethtool_get_sset_count, ++ .get_strings = airoha_ethtool_get_strings, + }; + + static void airoha_mac_config(struct phylink_config *config, unsigned int mode, +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -402,6 +402,7 @@ + + #define REG_SRC_PORT_FC_MAP6 0x2298 + #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) ++#define FC_MAP6_DEF_VALUE 0x1b1a1918 + + #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 + diff --git a/target/linux/airoha/patches-6.12/920-13-net-airoha-Rework-MTU-configuration.patch b/target/linux/airoha/patches-6.12/920-13-net-airoha-Rework-MTU-configuration.patch new file mode 100644 index 00000000000..072b50fe35c --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-13-net-airoha-Rework-MTU-configuration.patch @@ -0,0 +1,265 @@ +From 99e204d255c9b47a188dd79762b9406d9c5fed9b Mon Sep 17 00:00:00 2001 +Message-ID: <99e204d255c9b47a188dd79762b9406d9c5fed9b.1779348625.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Wed, 20 May 2026 17:16:39 +0200 +Subject: [PATCH 13/13] net: airoha: Rework MTU configuration + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 85 +++++++++++------------ + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_ppe.c | 8 +-- + drivers/net/ethernet/airoha/airoha_regs.h | 9 ++- + 4 files changed, 49 insertions(+), 54 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -185,10 +185,15 @@ static void airoha_fe_maccr_init(struct + { + int p; + +- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) ++ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { + airoha_fe_set(eth, REG_GDM_FWD_CFG(p), + GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK | + GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK); ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_RX_SIZE)); ++ } + + airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK, + FIELD_PREP(CDM_VLAN_MASK, 0x8100)); +@@ -1908,13 +1913,24 @@ static void airoha_update_hw_stats(struc + spin_unlock(&port->stats.lock); + } + ++static void airoha_dev_set_mtu(struct net_device *netdev) ++{ ++ struct airoha_gdm_dev *dev = netdev_priv(netdev); ++ ++ airoha_ppe_set_mtu(dev); ++ if (!airoha_is_lan_gdm_dev(dev)) ++ airoha_fe_rmw(dev->eth, REG_WAN_MTU0, ++ WAN_MTU0_MASK, ++ FIELD_PREP(WAN_MTU0_MASK, netdev->mtu)); ++} ++ + static int airoha_dev_open(struct net_device *netdev) + { +- int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; +- u32 cur_len, pse_port = FE_PSE_PORT_PPE1; + struct airoha_qdma *qdma = dev->qdma; ++ u32 pse_port = FE_PSE_PORT_PPE1; ++ int err; + + if (port->id == AIROHA_GDM2_IDX && airoha_is_lan_gdm_dev(dev)) { + /* GDM2 can be used just as wan */ +@@ -1947,19 +1963,7 @@ static int airoha_dev_open(struct net_de + airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + +- cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id), +- GDM_LONG_LEN_MASK); +- if (!port->users || len > cur_len) { +- /* Opening a sibling net_device with a larger MTU updates the +- * MTU of already running devices. This is required to allow +- * multiple net_devices with different MTUs to share the same +- * GDM port. +- */ +- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), +- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +- FIELD_PREP(GDM_LONG_LEN_MASK, len)); +- } ++ airoha_dev_set_mtu(netdev); + port->users++; + + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, +@@ -1976,30 +1980,6 @@ static int airoha_dev_open(struct net_de + return 0; + } + +-static void airoha_set_port_mtu(struct airoha_eth *eth, +- struct airoha_gdm_port *port) +-{ +- u32 len = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(port->devs); i++) { +- struct airoha_gdm_dev *dev = port->devs[i]; +- struct net_device *netdev; +- +- if (!dev) +- continue; +- +- netdev = dev->dev; +- if (netif_running(netdev)) +- len = max_t(u32, len, netdev->mtu); +- } +- len += ETH_HLEN + ETH_FCS_LEN; +- +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), +- GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_LONG_LEN_MASK, len)); +-} +- + static int airoha_dev_stop(struct net_device *netdev) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +@@ -2013,7 +1993,7 @@ static int airoha_dev_stop(struct net_de + netdev_tx_reset_subqueue(netdev, i); + + if (--port->users) +- airoha_set_port_mtu(dev->eth, port); ++ airoha_ppe_set_mtu(dev); + else + airoha_set_gdm_port_fwd_cfg(qdma->eth, + REG_GDM_FWD_CFG(port->id), +@@ -2083,10 +2063,6 @@ static int airoha_enable_gdm2_loopback(s + FIELD_PREP(LPBK_CHAN_MASK, chan) | + LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK | + LBK_CHAN_MODE_MASK | LPBK_EN_MASK); +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX), +- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +- FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); + /* Forward the traffic to the proper GDM port */ + pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 + : FE_PSE_PORT_GDM4; +@@ -2270,7 +2246,7 @@ static int airoha_dev_change_mtu(struct + + WRITE_ONCE(netdev->mtu, mtu); + if (port->users) +- airoha_set_port_mtu(dev->eth, port); ++ airoha_dev_set_mtu(netdev); + + return 0; + } +@@ -2613,6 +2589,7 @@ static int airoha_dev_set_wan_flag(struc + default: + return -EOPNOTSUPP; + } ++ airoha_dev_set_mtu(netdev); + } else { + switch (port->id) { + case AIROHA_GDM3_IDX: +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -23,6 +23,7 @@ + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_MTU 9220 ++#define AIROHA_MAX_RX_SIZE 16128 + #define AIROHA_MAX_PACKET_SIZE 2048 + #define AIROHA_NUM_QOS_CHANNELS 4 + #define AIROHA_NUM_QOS_QUEUES 8 +@@ -706,6 +707,7 @@ int airoha_get_fe_port(struct airoha_gdm + bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, + struct airoha_gdm_dev *dev); + ++void airoha_ppe_set_mtu(struct airoha_gdm_dev *dev); + void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); + bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); + void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -98,6 +98,35 @@ void airoha_ppe_set_cpu_port(struct airo + __field_prep(DFT_CPORT_MASK(fport), fe_cpu_port)); + } + ++void airoha_ppe_set_mtu(struct airoha_gdm_dev *dev) ++{ ++ struct airoha_gdm_port *port = dev->port; ++ struct airoha_eth *eth = dev->eth; ++ int i, ppe_id, index; ++ u32 mtu = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(port->devs); i++) { ++ struct airoha_gdm_dev *d = port->devs[i]; ++ struct net_device *netdev; ++ ++ if (!d) ++ continue; ++ ++ if (d->qdma != dev->qdma) ++ continue; ++ ++ netdev = d->dev; ++ if (netif_running(netdev)) ++ mtu = max_t(u32, mtu, netdev->mtu); ++ } ++ ++ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); ++ index = port->id == AIROHA_GDM4_IDX ? 7 : port->id; ++ airoha_fe_rmw(eth, REG_PPE_MTU(ppe_id, index), ++ FP_EGRESS_MTU_MASK(index), ++ __field_prep(FP_EGRESS_MTU_MASK(index), mtu)); ++} ++ + static void airoha_ppe_hw_init(struct airoha_ppe *ppe) + { + u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries; +@@ -116,8 +145,6 @@ static void airoha_ppe_hw_init(struct ai + PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries); + + for (i = 0; i < eth->soc->num_ppe; i++) { +- int p; +- + airoha_fe_wr(eth, REG_PPE_TB_BASE(i), + ppe->foe_dma + sram_tb_size); + +@@ -167,15 +194,6 @@ static void airoha_ppe_hw_init(struct ai + airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); + airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i), + PPE_FLOW_CFG_IP6_6RD_MASK); +- +- for (p = 0; p < ARRAY_SIZE(eth->ports); p++) +- airoha_fe_rmw(eth, REG_PPE_MTU(i, p), +- FP0_EGRESS_MTU_MASK | +- FP1_EGRESS_MTU_MASK, +- FIELD_PREP(FP0_EGRESS_MTU_MASK, +- AIROHA_MAX_MTU) | +- FIELD_PREP(FP1_EGRESS_MTU_MASK, +- AIROHA_MAX_MTU)); + } + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { +@@ -197,6 +215,7 @@ static void airoha_ppe_hw_init(struct ai + airoha_ppe_is_enabled(eth, 1); + fport = airoha_get_fe_port(dev); + airoha_ppe_set_cpu_port(dev, ppe_id, fport); ++ airoha_ppe_set_mtu(dev); + } + } + } +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -341,9 +341,8 @@ + #define PPE_SRAM_TABLE_EN_MASK BIT(0) + + #define REG_PPE_MTU_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304) +-#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + ((_n) << 2)) +-#define FP1_EGRESS_MTU_MASK GENMASK(29, 16) +-#define FP0_EGRESS_MTU_MASK GENMASK(13, 0) ++#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + (((_n) / 2) << 2)) ++#define FP_EGRESS_MTU_MASK(_n) GENMASK(13 + (((_n) % 2) << 4), ((_n) % 2) << 4) + + #define REG_PPE_RAM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c) + #define PPE_SRAM_CTRL_ACK_MASK BIT(31) +@@ -404,6 +403,10 @@ + #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) + #define FC_MAP6_DEF_VALUE 0x1b1a1918 + ++#define REG_WAN_MTU0 0x2300 ++#define WAN_MTU1_MASK GENMASK(29, 16) ++#define WAN_MTU0_MASK GENMASK(13, 0) ++ + #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 + + /* QDMA */ diff --git a/target/linux/airoha/patches-6.12/920-14-net-airoha-Better-handle-MIB-for-GDM-with-multiple-p.patch b/target/linux/airoha/patches-6.12/920-14-net-airoha-Better-handle-MIB-for-GDM-with-multiple-p.patch new file mode 100644 index 00000000000..25db5b6661e --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-14-net-airoha-Better-handle-MIB-for-GDM-with-multiple-p.patch @@ -0,0 +1,415 @@ +From 9652322e0b47eacfef497828f6476a2a3169fd13 Mon Sep 17 00:00:00 2001 +Message-ID: <9652322e0b47eacfef497828f6476a2a3169fd13.1779351672.git.lorenzo@kernel.org> +From: Christian Marangi +Date: Sat, 17 Jan 2026 14:46:12 +0100 +Subject: [PATCH] net: airoha: Better handle MIB for GDM with multiple port + attached + +In the context of a GDM that can have multiple port attached (GDM3/4) +the HW counter (MIB) are global for the GDM port. This cause duplicated +stats reported to the kernel for the related interface. + +The SoC supports a split MIB feature where each counter is tracked based +on the relevant HW channel (NBQ) to account for this scenario and +provide a way to select the related counter on accessing the MIB +registers. + +Enable this feature for GDM3 and GDM4 and configure the relevant HW +channel before updating the HW stats to report correct HW counter to the +kernel for the related interface. + +Also move the stats struct from port to dev since HW counter are +now specific to the network interface instead of the GDM port. + +Co-developed-by: Brown Huang +Signed-off-by: Brown Huang +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 191 +++++++++++++---------- + drivers/net/ethernet/airoha/airoha_eth.h | 7 +- + 2 files changed, 112 insertions(+), 86 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -612,6 +612,14 @@ static int airoha_fe_init(struct airoha_ + airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), + GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK); + ++ /* Enable split for MIB counters for GDM3 and GDM4 */ ++ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX), ++ FE_GDM_TX_MIB_SPLIT_EN_MASK | ++ FE_GDM_RX_MIB_SPLIT_EN_MASK); ++ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM4_IDX), ++ FE_GDM_TX_MIB_SPLIT_EN_MASK | ++ FE_GDM_RX_MIB_SPLIT_EN_MASK); ++ + airoha_fe_crsn_qsel_init(eth); + + airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK); +@@ -1768,149 +1776,169 @@ static void airoha_qdma_stop_napi(struct + } + } + +-static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) ++static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev) + { + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + u32 val, i = 0; + +- spin_lock(&port->stats.lock); +- u64_stats_update_begin(&port->stats.syncp); ++ u64_stats_update_begin(&dev->stats.syncp); ++ ++ /* Read relevant MIB for GDM with multiple port attached */ ++ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX) ++ airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id), ++ FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK, ++ FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) | ++ FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq)); + + /* TX */ + val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); +- port->stats.tx_ok_pkts += ((u64)val << 32); ++ dev->stats.tx_ok_pkts += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); +- port->stats.tx_ok_pkts += val; ++ dev->stats.tx_ok_pkts += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); +- port->stats.tx_ok_bytes += ((u64)val << 32); ++ dev->stats.tx_ok_bytes += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); +- port->stats.tx_ok_bytes += val; ++ dev->stats.tx_ok_bytes += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); +- port->stats.tx_drops += val; ++ dev->stats.tx_drops += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); +- port->stats.tx_broadcast += val; ++ dev->stats.tx_broadcast += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); +- port->stats.tx_multicast += val; ++ dev->stats.tx_multicast += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); +- port->stats.tx_len[i] += val; ++ dev->stats.tx_len[i] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); ++ dev->stats.tx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); ++ dev->stats.tx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); ++ dev->stats.tx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); ++ dev->stats.tx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); ++ dev->stats.tx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); +- port->stats.tx_len[i] += ((u64)val << 32); ++ dev->stats.tx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); +- port->stats.tx_len[i++] += val; ++ dev->stats.tx_len[i++] += val; + + /* RX */ + val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); +- port->stats.rx_ok_pkts += ((u64)val << 32); ++ dev->stats.rx_ok_pkts += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); +- port->stats.rx_ok_pkts += val; ++ dev->stats.rx_ok_pkts += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); +- port->stats.rx_ok_bytes += ((u64)val << 32); ++ dev->stats.rx_ok_bytes += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); +- port->stats.rx_ok_bytes += val; ++ dev->stats.rx_ok_bytes += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); +- port->stats.rx_drops += val; ++ dev->stats.rx_drops += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); +- port->stats.rx_broadcast += val; ++ dev->stats.rx_broadcast += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); +- port->stats.rx_multicast += val; ++ dev->stats.rx_multicast += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); +- port->stats.rx_errors += val; ++ dev->stats.rx_errors += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); +- port->stats.rx_crc_error += val; ++ dev->stats.rx_crc_error += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); +- port->stats.rx_over_errors += val; ++ dev->stats.rx_over_errors += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); +- port->stats.rx_fragment += val; ++ dev->stats.rx_fragment += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); +- port->stats.rx_jabber += val; ++ dev->stats.rx_jabber += val; + + i = 0; + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); +- port->stats.rx_len[i] += val; ++ dev->stats.rx_len[i] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); ++ dev->stats.rx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); ++ dev->stats.rx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); ++ dev->stats.rx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); ++ dev->stats.rx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); ++ dev->stats.rx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); +- port->stats.rx_len[i] += ((u64)val << 32); ++ dev->stats.rx_len[i] += ((u64)val << 32); + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; + + val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); +- port->stats.rx_len[i++] += val; ++ dev->stats.rx_len[i++] += val; ++ ++ u64_stats_update_end(&dev->stats.syncp); ++} ++ ++static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) ++{ ++ struct airoha_gdm_port *port = dev->port; ++ int i; ++ ++ spin_lock(&port->stats_lock); ++ ++ for (i = 0; i < ARRAY_SIZE(port->devs); i++) { ++ if (port->devs[i]) ++ airoha_dev_get_hw_stats(port->devs[i]); ++ } + +- /* reset mib counters */ +- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id), ++ /* Reset MIB counters */ ++ airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id), + FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); + +- u64_stats_update_end(&port->stats.syncp); +- spin_unlock(&port->stats.lock); ++ spin_unlock(&port->stats_lock); + } + + static void airoha_dev_set_mtu(struct net_device *netdev) +@@ -2220,23 +2248,22 @@ static void airoha_dev_get_stats64(struc + struct rtnl_link_stats64 *storage) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + unsigned int start; + + airoha_update_hw_stats(dev); + do { +- start = u64_stats_fetch_begin(&port->stats.syncp); +- storage->rx_packets = port->stats.rx_ok_pkts; +- storage->tx_packets = port->stats.tx_ok_pkts; +- storage->rx_bytes = port->stats.rx_ok_bytes; +- storage->tx_bytes = port->stats.tx_ok_bytes; +- storage->multicast = port->stats.rx_multicast; +- storage->rx_errors = port->stats.rx_errors; +- storage->rx_dropped = port->stats.rx_drops; +- storage->tx_dropped = port->stats.tx_drops; +- storage->rx_crc_errors = port->stats.rx_crc_error; +- storage->rx_over_errors = port->stats.rx_over_errors; +- } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++ start = u64_stats_fetch_begin(&dev->stats.syncp); ++ storage->rx_packets = dev->stats.rx_ok_pkts; ++ storage->tx_packets = dev->stats.tx_ok_pkts; ++ storage->rx_bytes = dev->stats.rx_ok_bytes; ++ storage->tx_bytes = dev->stats.tx_ok_bytes; ++ storage->multicast = dev->stats.rx_multicast; ++ storage->rx_errors = dev->stats.rx_errors; ++ storage->rx_dropped = dev->stats.rx_drops; ++ storage->tx_dropped = dev->stats.tx_drops; ++ storage->rx_crc_errors = dev->stats.rx_crc_error; ++ storage->rx_over_errors = dev->stats.rx_over_errors; ++ } while (u64_stats_fetch_retry(&dev->stats.syncp, start)); + } + + static int airoha_dev_change_mtu(struct net_device *netdev, int mtu) +@@ -2632,20 +2659,19 @@ static void airoha_ethtool_get_mac_stats + struct ethtool_eth_mac_stats *stats) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; + unsigned int start; + + airoha_update_hw_stats(dev); + do { +- start = u64_stats_fetch_begin(&port->stats.syncp); +- stats->FramesTransmittedOK = port->stats.tx_ok_pkts; +- stats->OctetsTransmittedOK = port->stats.tx_ok_bytes; +- stats->MulticastFramesXmittedOK = port->stats.tx_multicast; +- stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; +- stats->FramesReceivedOK = port->stats.rx_ok_pkts; +- stats->OctetsReceivedOK = port->stats.rx_ok_bytes; +- stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; +- } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++ start = u64_stats_fetch_begin(&dev->stats.syncp); ++ stats->FramesTransmittedOK = dev->stats.tx_ok_pkts; ++ stats->OctetsTransmittedOK = dev->stats.tx_ok_bytes; ++ stats->MulticastFramesXmittedOK = dev->stats.tx_multicast; ++ stats->BroadcastFramesXmittedOK = dev->stats.tx_broadcast; ++ stats->FramesReceivedOK = dev->stats.rx_ok_pkts; ++ stats->OctetsReceivedOK = dev->stats.rx_ok_bytes; ++ stats->BroadcastFramesReceivedOK = dev->stats.rx_broadcast; ++ } while (u64_stats_fetch_retry(&dev->stats.syncp, start)); + } + + static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { +@@ -2665,8 +2691,7 @@ airoha_ethtool_get_rmon_stats(struct net + const struct ethtool_rmon_hist_range **ranges) + { + struct airoha_gdm_dev *dev = netdev_priv(netdev); +- struct airoha_gdm_port *port = dev->port; +- struct airoha_hw_stats *hw_stats = &port->stats; ++ struct airoha_hw_stats *hw_stats = &dev->stats; + unsigned int start; + + BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != +@@ -2679,7 +2704,7 @@ airoha_ethtool_get_rmon_stats(struct net + do { + int i; + +- start = u64_stats_fetch_begin(&port->stats.syncp); ++ start = u64_stats_fetch_begin(&dev->stats.syncp); + stats->fragments = hw_stats->rx_fragment; + stats->jabbers = hw_stats->rx_jabber; + for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1; +@@ -2687,7 +2712,7 @@ airoha_ethtool_get_rmon_stats(struct net + stats->hist[i] = hw_stats->rx_len[i]; + stats->hist_tx[i] = hw_stats->tx_len[i]; + } +- } while (u64_stats_fetch_retry(&port->stats.syncp, start)); ++ } while (u64_stats_fetch_retry(&dev->stats.syncp, start)); + } + + static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags) +@@ -3695,6 +3720,7 @@ static int airoha_alloc_gdm_device(struc + + netdev->dev.of_node = of_node_get(np); + dev = netdev_priv(netdev); ++ u64_stats_init(&dev->stats.syncp); + dev->dev = netdev; + dev->port = port; + dev->eth = eth; +@@ -3743,9 +3769,8 @@ static int airoha_alloc_gdm_port(struct + if (!port) + return -ENOMEM; + +- u64_stats_init(&port->stats.syncp); +- spin_lock_init(&port->stats.lock); + port->id = id; ++ spin_lock_init(&port->stats_lock); + eth->ports[p] = port; + + err = airoha_metadata_dst_alloc(port); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -228,8 +228,6 @@ struct airoha_tx_irq_queue { + }; + + struct airoha_hw_stats { +- /* protect concurrent hw_stats accesses */ +- spinlock_t lock; + struct u64_stats_sync syncp; + + /* get_stats64 */ +@@ -573,6 +571,8 @@ struct airoha_gdm_dev { + + u32 flags; + int nbq; ++ ++ struct airoha_hw_stats stats; + }; + + struct airoha_gdm_port { +@@ -580,7 +580,8 @@ struct airoha_gdm_port { + int id; + int users; + +- struct airoha_hw_stats stats; ++ /* protect concurrent hw_stats accesses */ ++ spinlock_t stats_lock; + + struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS]; + }; diff --git a/target/linux/airoha/patches-6.12/920-15-net-airoha-fix-wrong-airoha_get_fe_port.patch b/target/linux/airoha/patches-6.12/920-15-net-airoha-fix-wrong-airoha_get_fe_port.patch new file mode 100644 index 00000000000..75a49306c2c --- /dev/null +++ b/target/linux/airoha/patches-6.12/920-15-net-airoha-fix-wrong-airoha_get_fe_port.patch @@ -0,0 +1,40 @@ +From 1e596dac503da44a9fcd3e2654a4963db3e2ee6a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 28 Jan 2026 02:38:24 +0100 +Subject: [PATCH] net: airoha: fix wrong airoha_get_fe_port() + +It seems the SDK where the airoha_get_fe_port() logic was taken was +actually wrong and the AN7583 SoC doesn't have such difference. Instead +it does follow the same port order of AN7581 SoC. + +Drop the switch case and apply the same condition on both AN7581 and +AN7583 SoC. + +Fixes: e4e5ce823bdd ("net: airoha: Add AN7583 SoC support") +Signed-off-by: Christian Marangi +--- + drivers/net/ethernet/airoha/airoha_eth.c | 14 ++------------ + 1 file changed, 2 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2345,17 +2345,9 @@ static u32 airoha_get_dsa_tag(struct sk_ + int airoha_get_fe_port(struct airoha_gdm_dev *dev) + { + struct airoha_gdm_port *port = dev->port; +- struct airoha_eth *eth = dev->eth; + +- switch (eth->soc->version) { +- case 0x7583: +- return port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 +- : port->id; +- case 0x7581: +- default: +- return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4 +- : port->id; +- } ++ return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4 ++ : port->id; + } + + static int airoha_dev_set_features(struct net_device *netdev,