From: Robert Marko Date: Sun, 2 Aug 2026 17:39:22 +0000 (+0200) Subject: qualcommax: drop 6.12 support X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=55a7d7c45f76db4e729b69fea7c7ac10eb98d61b;p=thirdparty%2Fopenwrt.git qualcommax: drop 6.12 support No point in keeping 6.12 on qualcommax anymore. Signed-off-by: Robert Marko --- diff --git a/target/linux/qualcommax/config-6.12 b/target/linux/qualcommax/config-6.12 deleted file mode 100644 index 8daaff38322..00000000000 --- a/target/linux/qualcommax/config-6.12 +++ /dev/null @@ -1,626 +0,0 @@ -CONFIG_64BIT=y -CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y -CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y -CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_ARCH_FORCE_MAX_ORDER=10 -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_KEEP_MEMBLOCK=y -CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y -CONFIG_ARCH_MMAP_RND_BITS=18 -CONFIG_ARCH_MMAP_RND_BITS_MAX=24 -CONFIG_ARCH_MMAP_RND_BITS_MIN=18 -CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 -CONFIG_ARCH_PKEY_BITS=3 -CONFIG_ARCH_PROC_KCORE_TEXT=y -CONFIG_ARCH_QCOM=y -CONFIG_ARCH_SPARSEMEM_ENABLE=y -CONFIG_ARCH_STACKWALK=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_WANTS_EXECMEM_LATE=y -CONFIG_ARCH_WANTS_NO_INSTR=y -CONFIG_ARCH_WANTS_THP_SWAP=y -CONFIG_ARM64=y -CONFIG_ARM64_4K_PAGES=y -CONFIG_ARM64_ERRATUM_1165522=y -CONFIG_ARM64_ERRATUM_1286807=y -CONFIG_ARM64_ERRATUM_2051678=y -CONFIG_ARM64_ERRATUM_2054223=y -CONFIG_ARM64_ERRATUM_2067961=y -CONFIG_ARM64_ERRATUM_2077057=y -CONFIG_ARM64_ERRATUM_2658417=y -CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y -CONFIG_ARM64_PA_BITS=48 -CONFIG_ARM64_PA_BITS_48=y -CONFIG_ARM64_PLATFORM_DEVICES=y -CONFIG_ARM64_PTR_AUTH=y -CONFIG_ARM64_PTR_AUTH_KERNEL=y -CONFIG_ARM64_SVE=y -CONFIG_ARM64_TAGGED_ADDR_ABI=y -CONFIG_ARM64_VA_BITS=39 -CONFIG_ARM64_VA_BITS_39=y -CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y -CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y -CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y -CONFIG_ARM_AMBA=y -CONFIG_ARM_ARCH_TIMER=y -CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y -CONFIG_ARM_GIC=y -CONFIG_ARM_GIC_V2M=y -CONFIG_ARM_GIC_V3=y -CONFIG_ARM_GIC_V3_ITS=y -# CONFIG_ARM_MHU_V2 is not set -# CONFIG_ARM_MHU_V3 is not set -CONFIG_ARM_PSCI_CPUIDLE=y -CONFIG_ARM_PSCI_FW=y -# CONFIG_ARM_QCOM_CPUFREQ_HW is not set -CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y -CONFIG_AT803X_PHY=y -CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y -CONFIG_AUXILIARY_BUS=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_SD=y -CONFIG_BLK_MQ_PCI=y -CONFIG_BLK_MQ_VIRTIO=y -CONFIG_BLK_PM=y -CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y -CONFIG_CC_HAVE_SHADOW_CALL_STACK=y -CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y -# CONFIG_CLK_QCM2290_GPUCC is not set -# CONFIG_CLK_X1E80100_CAMCC is not set -# CONFIG_CLK_X1E80100_DISPCC is not set -# CONFIG_CLK_X1E80100_GCC is not set -# CONFIG_CLK_X1E80100_GPUCC is not set -# CONFIG_CLK_X1E80100_TCSRCC is not set -CONFIG_CLONE_BACKWARDS=y -CONFIG_COMMON_CLK=y -CONFIG_COMMON_CLK_QCOM=y -CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 -# CONFIG_COMPAT_32BIT_TIME is not set -CONFIG_CONTEXT_TRACKING=y -CONFIG_CONTEXT_TRACKING_IDLE=y -CONFIG_COREDUMP=y -CONFIG_CPUFREQ_DT=y -CONFIG_CPUFREQ_DT_PLATDEV=y -CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y -CONFIG_CPU_FREQ_GOV_ATTR_SET=y -# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set -CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y -# CONFIG_CPU_FREQ_GOV_USERSPACE is not set -CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_THERMAL=y -CONFIG_CPU_IDLE=y -CONFIG_CPU_IDLE_GOV_MENU=y -CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y -CONFIG_CPU_LITTLE_ENDIAN=y -CONFIG_CPU_MITIGATIONS=y -CONFIG_CPU_PM=y -CONFIG_CPU_RMAP=y -CONFIG_CPU_THERMAL=y -CONFIG_CRC16=y -CONFIG_CRC8=y -CONFIG_CRYPTO_AUTHENC=y -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_QCOM_RNG=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_HASH_INFO=y -CONFIG_CRYPTO_HW=y -CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y -CONFIG_CRYPTO_LIB_DES=y -CONFIG_CRYPTO_LIB_GF128MUL=y -CONFIG_CRYPTO_LIB_SHA256=y -CONFIG_CRYPTO_LIB_UTILS=y -CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_RNG=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_XTS=y -CONFIG_CRYPTO_ZSTD=y -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_DEBUG_BUGVERBOSE=y -CONFIG_DEBUG_INFO=y -CONFIG_DEV_COREDUMP=y -CONFIG_DMADEVICES=y -CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y -CONFIG_DMA_DIRECT_REMAP=y -CONFIG_DMA_ENGINE=y -CONFIG_DMA_NEED_SYNC=y -CONFIG_DMA_OF=y -CONFIG_DMA_VIRTUAL_CHANNELS=y -CONFIG_DTC=y -CONFIG_DT_IDLE_STATES=y -CONFIG_EDAC_SUPPORT=y -CONFIG_EXCLUSIVE_SYSTEM_RAM=y -CONFIG_FIXED_PHY=y -CONFIG_FIX_EARLYCON_MEM=y -CONFIG_FRAME_POINTER=y -CONFIG_FS_IOMAP=y -CONFIG_FUNCTION_ALIGNMENT=4 -CONFIG_FUNCTION_ALIGNMENT_4B=y -CONFIG_FWNODE_MDIO=y -CONFIG_FWNODE_PCS=y -CONFIG_FW_LOADER_PAGED_BUF=y -CONFIG_FW_LOADER_SYSFS=y -CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y -CONFIG_GENERIC_ALLOCATOR=y -CONFIG_GENERIC_ARCH_TOPOLOGY=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_CPU_AUTOPROBE=y -CONFIG_GENERIC_CPU_DEVICES=y -CONFIG_GENERIC_CPU_VULNERABILITIES=y -CONFIG_GENERIC_CSUM=y -CONFIG_GENERIC_EARLY_IOREMAP=y -CONFIG_GENERIC_GETTIMEOFDAY=y -CONFIG_GENERIC_IDLE_POLL_SETUP=y -CONFIG_GENERIC_IOREMAP=y -CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y -CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD=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_WATCHDOG=y -# CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set -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_HWMON=y -CONFIG_HWSPINLOCK=y -CONFIG_HWSPINLOCK_QCOM=y -CONFIG_HW_RANDOM=y -CONFIG_I2C=y -CONFIG_I2C_BOARDINFO=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_HELPER_AUTO=y -# CONFIG_I2C_QCOM_CCI is not set -CONFIG_I2C_QUP=y -CONFIG_IIO=y -CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 -CONFIG_INITRAMFS_SOURCE="" -CONFIG_INTERCONNECT=y -CONFIG_INTERCONNECT_CLK=y -# CONFIG_INTERCONNECT_QCOM is not set -CONFIG_IPQ_APSS_6018=y -CONFIG_IPQ_APSS_PLL=y -# CONFIG_IPQ_CMN_PLL is not set -# CONFIG_IPQ_GCC_4019 is not set -# CONFIG_IPQ_GCC_5018 is not set -# CONFIG_IPQ_GCC_5332 is not set -# CONFIG_IPQ_GCC_6018 is not set -# CONFIG_IPQ_GCC_8074 is not set -# CONFIG_IPQ_GCC_9574 is not set -# CONFIG_IPQ_NSSCC_QCA8K is not set -CONFIG_IRQCHIP=y -CONFIG_IRQ_DOMAIN=y -CONFIG_IRQ_DOMAIN_HIERARCHY=y -CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS=y -CONFIG_IRQ_FORCED_THREADING=y -CONFIG_IRQ_MSI_LIB=y -CONFIG_IRQ_WORK=y -# CONFIG_KPSS_XCC is not set -CONFIG_LEDS_TLC591XX=y -CONFIG_LIBFDT=y -CONFIG_LOCK_DEBUGGING_SUPPORT=y -CONFIG_LOCK_SPIN_ON_OWNER=y -CONFIG_LRU_GEN_WALKS_MMU=y -CONFIG_LZO_COMPRESS=y -CONFIG_LZO_DECOMPRESS=y -CONFIG_MAILBOX=y -# CONFIG_MAILBOX_TEST is not set -CONFIG_MDIO_BUS=y -CONFIG_MDIO_DEVICE=y -CONFIG_MDIO_DEVRES=y -CONFIG_MDIO_IPQ4019=y -# CONFIG_MFD_QCOM_RPM is not set -CONFIG_MFD_SYSCON=y -CONFIG_MIGRATION=y -# CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY is not set -CONFIG_MMC=y -CONFIG_MMC_BLOCK=y -CONFIG_MMC_BLOCK_MINORS=32 -CONFIG_MMC_CQHCI=y -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_IO_ACCESSORS=y -CONFIG_MMC_SDHCI_MSM=y -CONFIG_MMC_SDHCI_PLTFM=y -CONFIG_MMU_LAZY_TLB_REFCOUNT=y -CONFIG_MODULES_USE_ELF_RELA=y -# CONFIG_MSM_GCC_8916 is not set -# CONFIG_MSM_GCC_8917 is not set -# CONFIG_MSM_GCC_8939 is not set -# CONFIG_MSM_GCC_8976 is not set -# CONFIG_MSM_GCC_8994 is not set -# CONFIG_MSM_GCC_8996 is not set -# CONFIG_MSM_GCC_8998 is not set -# CONFIG_MSM_GPUCC_8998 is not set -# CONFIG_MSM_MMCC_8996 is not set -# CONFIG_MSM_MMCC_8998 is not set -CONFIG_MTD_NAND_CORE=y -CONFIG_MTD_NAND_ECC=y -CONFIG_MTD_NAND_ECC_SW_HAMMING=y -CONFIG_MTD_NAND_QCOM=y -CONFIG_MTD_QCOMSMEM_PARTS=y -CONFIG_MTD_RAW_NAND=y -CONFIG_MTD_SPI_NOR=y -CONFIG_MTD_UBI=y -CONFIG_MTD_UBI_BEB_LIMIT=20 -CONFIG_MTD_UBI_BLOCK=y -CONFIG_MTD_UBI_WL_THRESHOLD=4096 -CONFIG_MUTEX_SPIN_ON_OWNER=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEED_SG_DMA_LENGTH=y -CONFIG_NET_DEVLINK=y -CONFIG_NET_DSA=y -CONFIG_NET_DSA_TAG_OOB=y -CONFIG_NET_EGRESS=y -CONFIG_NET_FLOW_LIMIT=y -CONFIG_NET_INGRESS=y -CONFIG_NET_SELFTESTS=y -CONFIG_NET_XGRESS=y -CONFIG_NLS=y -CONFIG_NO_HZ_COMMON=y -CONFIG_NO_HZ_IDLE=y -CONFIG_NR_CPUS=4 -# CONFIG_NSM is not set -CONFIG_NVMEM=y -CONFIG_NVMEM_LAYOUTS=y -CONFIG_NVMEM_LAYOUT_ASCII_ENV=y -CONFIG_NVMEM_LAYOUT_U_BOOT_ENV=y -CONFIG_NVMEM_QCOM_QFPROM=y -# CONFIG_NVMEM_QCOM_SEC_QFPROM is not set -CONFIG_NVMEM_SYSFS=y -CONFIG_OF=y -CONFIG_OF_ADDRESS=y -CONFIG_OF_EARLY_FLATTREE=y -CONFIG_OF_FLATTREE=y -CONFIG_OF_GPIO=y -CONFIG_OF_IRQ=y -CONFIG_OF_KOBJ=y -CONFIG_OF_MDIO=y -CONFIG_PADATA=y -CONFIG_PAGE_POOL=y -CONFIG_PAGE_SIZE_LESS_THAN_256KB=y -CONFIG_PAGE_SIZE_LESS_THAN_64KB=y -CONFIG_PARTITION_PERCPU=y -CONFIG_PCI=y -CONFIG_PCIEAER=y -CONFIG_PCIEASPM=y -CONFIG_PCIEASPM_DEFAULT=y -# CONFIG_PCIEASPM_PERFORMANCE is not set -# CONFIG_PCIEASPM_POWERSAVE is not set -# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set -CONFIG_PCIEPORTBUS=y -CONFIG_PCIE_DW=y -CONFIG_PCIE_DW_HOST=y -CONFIG_PCIE_PME=y -CONFIG_PCIE_QCOM=y -CONFIG_PCIE_QCOM_COMMON=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCI_DOMAINS_GENERIC=y -CONFIG_PCI_MSI=y -CONFIG_PCS_QCA_UNIPHY=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_QCOM_APQ8064_SATA is not set -# CONFIG_PHY_QCOM_EDP is not set -# CONFIG_PHY_QCOM_EUSB2_REPEATER is not set -# CONFIG_PHY_QCOM_IPQ4019_USB is not set -# CONFIG_PHY_QCOM_IPQ806X_SATA is not set -# CONFIG_PHY_QCOM_IPQ806X_USB is not set -# CONFIG_PHY_QCOM_M31_USB is not set -# CONFIG_PHY_QCOM_PCIE2 is not set -CONFIG_PHY_QCOM_QMP=y -CONFIG_PHY_QCOM_QMP_COMBO=y -CONFIG_PHY_QCOM_QMP_PCIE=y -CONFIG_PHY_QCOM_QMP_PCIE_8996=y -CONFIG_PHY_QCOM_QMP_UFS=y -CONFIG_PHY_QCOM_QMP_USB=y -# CONFIG_PHY_QCOM_QMP_USB_LEGACY is not set -CONFIG_PHY_QCOM_QUSB2=y -# CONFIG_PHY_QCOM_SGMII_ETH is not set -# CONFIG_PHY_QCOM_SNPS_EUSB2 is not set -# CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP is not set -# CONFIG_PHY_QCOM_USB_HS_28NM is not set -# CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2 is not set -# CONFIG_PHY_QCOM_USB_SS is not set -CONFIG_PINCTRL=y -CONFIG_PINCTRL_AW9523=y -# CONFIG_PINCTRL_IPQ5018 is not set -# CONFIG_PINCTRL_IPQ5332 is not set -# CONFIG_PINCTRL_IPQ6018 is not set -# CONFIG_PINCTRL_IPQ8074 is not set -# CONFIG_PINCTRL_IPQ9574 is not set -CONFIG_PINCTRL_MSM=y -# CONFIG_PINCTRL_MSM8916 is not set -# CONFIG_PINCTRL_MSM8976 is not set -# CONFIG_PINCTRL_MSM8994 is not set -# CONFIG_PINCTRL_MSM8996 is not set -# CONFIG_PINCTRL_MSM8998 is not set -# CONFIG_PINCTRL_QCM2290 is not set -# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set -# CONFIG_PINCTRL_QCS404 is not set -# CONFIG_PINCTRL_QDU1000 is not set -# CONFIG_PINCTRL_SA8775P is not set -# CONFIG_PINCTRL_SC7180 is not set -# CONFIG_PINCTRL_SC8280XP is not set -# CONFIG_PINCTRL_SDM660 is not set -# CONFIG_PINCTRL_SDM670 is not set -# CONFIG_PINCTRL_SDM845 is not set -# CONFIG_PINCTRL_SDX75 is not set -# CONFIG_PINCTRL_SM4450 is not set -# CONFIG_PINCTRL_SM6350 is not set -# CONFIG_PINCTRL_SM6375 is not set -# CONFIG_PINCTRL_SM7150 is not set -# CONFIG_PINCTRL_SM8150 is not set -# CONFIG_PINCTRL_SM8250 is not set -# CONFIG_PINCTRL_SM8450 is not set -# CONFIG_PINCTRL_SM8550 is not set -# CONFIG_PINCTRL_SM8650 is not set -# CONFIG_PINCTRL_X1E80100 is not set -CONFIG_PM=y -CONFIG_PM_CLK=y -CONFIG_PM_OPP=y -CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y -CONFIG_POWER_RESET=y -CONFIG_POWER_RESET_GPIO=y -# CONFIG_POWER_RESET_MSM is not set -CONFIG_POWER_SUPPLY=y -CONFIG_PRINTK_TIME=y -CONFIG_PTP_1588_CLOCK_OPTIONAL=y -CONFIG_QCA807X_PHY=y -CONFIG_QCA808X_PHY=y -# CONFIG_QCM_DISPCC_2290 is not set -# CONFIG_QCM_GCC_2290 is not set -# CONFIG_QCOM_80211AX_PPE is not set -# CONFIG_QCOM_A53PLL is not set -# CONFIG_QCOM_AOSS_QMP is not set -CONFIG_QCOM_APCS_IPC=y -# CONFIG_QCOM_APM is not set -# CONFIG_QCOM_APR is not set -CONFIG_QCOM_BAM_DMA=y -# CONFIG_QCOM_CLK_APCC_MSM8996 is not set -# CONFIG_QCOM_CLK_APCS_MSM8916 is not set -# CONFIG_QCOM_COMMAND_DB is not set -# CONFIG_QCOM_CPR is not set -# CONFIG_QCOM_CPUCP_MBOX is not set -# CONFIG_QCOM_EBI2 is not set -# CONFIG_QCOM_EDMA is not set -# CONFIG_QCOM_FASTRPC is not set -# CONFIG_QCOM_GENI_SE is not set -# CONFIG_QCOM_GSBI is not set -# CONFIG_QCOM_HFPLL is not set -# CONFIG_QCOM_ICC_BWMON is not set -# CONFIG_QCOM_IPA is not set -# CONFIG_QCOM_IPCC is not set -# CONFIG_QCOM_LLCC is not set -CONFIG_QCOM_MDT_LOADER=y -# CONFIG_QCOM_MPM is not set -CONFIG_QCOM_NET_PHYLIB=y -# CONFIG_QCOM_OCMEM is not set -# CONFIG_QCOM_PDC is not set -CONFIG_QCOM_PIL_INFO=y -# CONFIG_QCOM_Q6V5_ADSP is not set -CONFIG_QCOM_Q6V5_COMMON=y -# CONFIG_QCOM_Q6V5_MPD is not set -# CONFIG_QCOM_Q6V5_MSS is not set -# CONFIG_QCOM_Q6V5_PAS is not set -CONFIG_QCOM_Q6V5_WCSS=y -# CONFIG_QCOM_Q6V5_WCSS_SEC is not set -# CONFIG_QCOM_QSEECOM is not set -# CONFIG_QCOM_RAMP_CTRL is not set -# CONFIG_QCOM_RMTFS_MEM is not set -# CONFIG_QCOM_RPMH is not set -# CONFIG_QCOM_RPM_MASTER_STATS is not set -CONFIG_QCOM_RPROC_COMMON=y -CONFIG_QCOM_SCM=y -# CONFIG_QCOM_SMD_RPM is not set -CONFIG_QCOM_SMEM=y -CONFIG_QCOM_SMEM_STATE=y -CONFIG_QCOM_SMP2P=y -# CONFIG_QCOM_SMSM is not set -CONFIG_QCOM_SOCINFO=y -# CONFIG_QCOM_SPM is not set -# CONFIG_QCOM_STATS is not set -# CONFIG_QCOM_SYSMON is not set -# CONFIG_QCOM_TMEL_QMP_MAILBOX is not set -CONFIG_QCOM_TSENS=y -CONFIG_QCOM_TZMEM=y -CONFIG_QCOM_TZMEM_MODE_GENERIC=y -# CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE is not set -# CONFIG_QCOM_WCNSS_CTRL is not set -# CONFIG_QCOM_WCNSS_PIL is not set -CONFIG_QCOM_WDT=y -# CONFIG_QCS_GCC_404 is not set -# CONFIG_QCS_Q6SSTOP_404 is not set -# CONFIG_QCS_TURING_404 is not set -# CONFIG_QDU_ECPRICC_1000 is not set -# CONFIG_QDU_GCC_1000 is not set -CONFIG_QUEUED_RWLOCKS=y -CONFIG_QUEUED_SPINLOCKS=y -CONFIG_RANDSTRUCT_NONE=y -CONFIG_RAS=y -CONFIG_RATIONAL=y -CONFIG_REGMAP=y -CONFIG_REGMAP_I2C=y -CONFIG_REGMAP_MMIO=y -CONFIG_REGULATOR=y -# CONFIG_REGULATOR_CPR3 is not set -CONFIG_REGULATOR_FIXED_VOLTAGE=y -# CONFIG_REGULATOR_VQMMC_IPQ4019 is not set -CONFIG_RELOCATABLE=y -CONFIG_REMOTEPROC=y -CONFIG_REMOTEPROC_CDEV=y -CONFIG_RESET_CONTROLLER=y -# CONFIG_RESET_QCOM_AOSS is not set -# CONFIG_RESET_QCOM_PDC is not set -CONFIG_RFS_ACCEL=y -CONFIG_RODATA_FULL_DEFAULT_ENABLED=y -CONFIG_RPMSG=y -CONFIG_RPMSG_CHAR=y -# CONFIG_RPMSG_CTRL is not set -# CONFIG_RPMSG_NS is not set -CONFIG_RPMSG_QCOM_GLINK=y -CONFIG_RPMSG_QCOM_GLINK_RPM=y -CONFIG_RPMSG_QCOM_GLINK_SMEM=y -CONFIG_RPMSG_QCOM_SMD=y -# CONFIG_RPMSG_TTY is not set -CONFIG_RPS=y -CONFIG_RTC_CLASS=y -CONFIG_RTC_I2C_AND_SPI=y -CONFIG_RWSEM_SPIN_ON_OWNER=y -# CONFIG_SA_GCC_8775P is not set -# CONFIG_SA_GPUCC_8775P is not set -# CONFIG_SCHED_CORE is not set -CONFIG_SCHED_HW_PRESSURE=y -CONFIG_SCHED_MC=y -CONFIG_SCHED_SMT=y -CONFIG_SCSI=y -CONFIG_SCSI_COMMON=y -# CONFIG_SCSI_LOWLEVEL is not set -# CONFIG_SCSI_PROC_FS is not set -# CONFIG_SC_CAMCC_7280 is not set -# CONFIG_SC_CAMCC_8280XP is not set -# CONFIG_SC_DISPCC_7180 is not set -# CONFIG_SC_DISPCC_8280XP is not set -# CONFIG_SC_GCC_7180 is not set -# CONFIG_SC_GCC_8280XP is not set -# CONFIG_SC_GPUCC_7180 is not set -# CONFIG_SC_LPASSCC_7280 is not set -# CONFIG_SC_LPASSCC_8280XP is not set -# CONFIG_SC_LPASS_CORECC_7180 is not set -# CONFIG_SC_LPASS_CORECC_7280 is not set -# CONFIG_SC_VIDEOCC_7180 is not set -# CONFIG_SDM_CAMCC_845 is not set -# CONFIG_SDM_DISPCC_845 is not set -# CONFIG_SDM_GCC_660 is not set -# CONFIG_SDM_GCC_845 is not set -# CONFIG_SDM_GPUCC_845 is not set -# CONFIG_SDM_LPASSCC_845 is not set -# CONFIG_SDM_VIDEOCC_845 is not set -# CONFIG_SDX_GCC_75 is not set -CONFIG_SERIAL_8250_FSL=y -CONFIG_SERIAL_MCTRL_GPIO=y -CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y -CONFIG_SGL_ALLOC=y -CONFIG_SG_POOL=y -CONFIG_SKB_EXTENSIONS=y -CONFIG_SMP=y -# CONFIG_SM_CAMCC_4450 is not set -# CONFIG_SM_CAMCC_6350 is not set -# CONFIG_SM_CAMCC_7150 is not set -# CONFIG_SM_CAMCC_8150 is not set -# CONFIG_SM_CAMCC_8450 is not set -# CONFIG_SM_CAMCC_8550 is not set -# CONFIG_SM_CAMCC_8650 is not set -# CONFIG_SM_GCC_4450 is not set -# CONFIG_SM_GCC_7150 is not set -# CONFIG_SM_GCC_8150 is not set -# CONFIG_SM_GCC_8250 is not set -# CONFIG_SM_GCC_8450 is not set -# CONFIG_SM_GCC_8550 is not set -# CONFIG_SM_GCC_8650 is not set -# CONFIG_SM_GPUCC_4450 is not set -# CONFIG_SM_GPUCC_6115 is not set -# CONFIG_SM_GPUCC_6125 is not set -# CONFIG_SM_GPUCC_6350 is not set -# CONFIG_SM_GPUCC_6375 is not set -# CONFIG_SM_GPUCC_8150 is not set -# CONFIG_SM_GPUCC_8250 is not set -# CONFIG_SM_GPUCC_8350 is not set -# CONFIG_SM_GPUCC_8450 is not set -# CONFIG_SM_GPUCC_8550 is not set -# CONFIG_SM_GPUCC_8650 is not set -# CONFIG_SM_TCSRCC_8550 is not set -# CONFIG_SM_TCSRCC_8650 is not set -# CONFIG_SM_VIDEOCC_7150 is not set -# CONFIG_SM_VIDEOCC_8150 is not set -# CONFIG_SM_VIDEOCC_8250 is not set -# CONFIG_SM_VIDEOCC_8350 is not set -# CONFIG_SM_VIDEOCC_8450 is not set -CONFIG_SOCK_RX_QUEUE_MAPPING=y -CONFIG_SOC_BUS=y -CONFIG_SOFTIRQ_ON_OWN_STACK=y -CONFIG_SPARSEMEM=y -CONFIG_SPARSEMEM_EXTREME=y -CONFIG_SPARSEMEM_VMEMMAP=y -CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -CONFIG_SPARSE_IRQ=y -CONFIG_SPI=y -CONFIG_SPI_MASTER=y -CONFIG_SPI_MEM=y -# CONFIG_SPI_QPIC_SNAND is not set -CONFIG_SPI_QUP=y -CONFIG_SPLIT_PMD_PTLOCKS=y -CONFIG_SPLIT_PTE_PTLOCKS=y -CONFIG_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_HWMON=y -CONFIG_THERMAL_OF=y -CONFIG_THREAD_INFO_IN_TASK=y -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TIMER_OF=y -CONFIG_TIMER_PROBE=y -CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y -CONFIG_TREE_RCU=y -CONFIG_TREE_SRCU=y -CONFIG_UBIFS_FS=y -CONFIG_UBIFS_FS_ADVANCED_COMPR=y -# CONFIG_UCLAMP_TASK is not set -CONFIG_UNMAP_KERNEL_AT_EL0=y -CONFIG_USB=y -CONFIG_USB_COMMON=y -CONFIG_USB_SUPPORT=y -CONFIG_USER_STACKTRACE_SUPPORT=y -CONFIG_VDSO_GETRANDOM=y -CONFIG_VIRTIO=y -CONFIG_VIRTIO_ANCHOR=y -# CONFIG_VIRTIO_BLK is not set -# CONFIG_VIRTIO_DEBUG is not set -# CONFIG_VIRTIO_NET is not set -CONFIG_VMAP_STACK=y -CONFIG_WANT_DEV_COREDUMP=y -CONFIG_WATCHDOG_CORE=y -CONFIG_WATCHDOG_SYSFS=y -CONFIG_XPS=y -CONFIG_XXHASH=y -CONFIG_ZLIB_DEFLATE=y -CONFIG_ZLIB_INFLATE=y -CONFIG_ZONE_DMA32=y -CONFIG_ZSTD_COMMON=y -CONFIG_ZSTD_COMPRESS=y -CONFIG_ZSTD_DECOMPRESS=y diff --git a/target/linux/qualcommax/patches-6.12/0036-v6.13-arm64-dts-qcom-ipq-change-labels-to-lower-case.patch b/target/linux/qualcommax/patches-6.12/0036-v6.13-arm64-dts-qcom-ipq-change-labels-to-lower-case.patch deleted file mode 100644 index 9dadee21548..00000000000 --- a/target/linux/qualcommax/patches-6.12/0036-v6.13-arm64-dts-qcom-ipq-change-labels-to-lower-case.patch +++ /dev/null @@ -1,381 +0,0 @@ -From 6f8c1ed25809181c187a59b1caaa1521756924bf Mon Sep 17 00:00:00 2001 -From: Krzysztof Kozlowski -Date: Tue, 22 Oct 2024 17:47:26 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq: change labels to lower-case - -DTS coding style expects labels to be lowercase. No functional impact. -Verified with comparing decompiled DTB (dtx_diff and fdtdump+diff). - -Signed-off-by: Krzysztof Kozlowski -Link: https://lore.kernel.org/r/20241022-dts-qcom-label-v3-1-0505bc7d2c56@linaro.org -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 10 +++--- - arch/arm64/boot/dts/qcom/ipq5332.dtsi | 18 +++++----- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 26 +++++++------- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 18 +++++----- - arch/arm64/boot/dts/qcom/ipq9574.dtsi | 50 +++++++++++++-------------- - 5 files changed, 61 insertions(+), 61 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -31,27 +31,27 @@ - #address-cells = <1>; - #size-cells = <0>; - -- CPU0: cpu@0 { -+ cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; - }; - -- CPU1: cpu@1 { -+ cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; - }; - -- L2_0: l2-cache { -+ l2_0: l2-cache { - compatible = "cache"; - cache-level = <2>; - cache-size = <0x80000>; ---- a/arch/arm64/boot/dts/qcom/ipq5332.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5332.dtsi -@@ -31,47 +31,47 @@ - #address-cells = <1>; - #size-cells = <0>; - -- CPU0: cpu@0 { -+ cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; - }; - -- CPU1: cpu@1 { -+ cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; - }; - -- CPU2: cpu@2 { -+ cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x2>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; - }; - -- CPU3: cpu@3 { -+ cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x3>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; - }; - -- L2_0: l2-cache { -+ l2_0: l2-cache { - compatible = "cache"; - cache-level = <2>; - cache-unified; ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -34,12 +34,12 @@ - #address-cells = <1>; - #size-cells = <0>; - -- CPU0: cpu@0 { -+ cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -47,12 +47,12 @@ - #cooling-cells = <2>; - }; - -- CPU1: cpu@1 { -+ cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - enable-method = "psci"; - reg = <0x1>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -60,12 +60,12 @@ - #cooling-cells = <2>; - }; - -- CPU2: cpu@2 { -+ cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - enable-method = "psci"; - reg = <0x2>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -73,12 +73,12 @@ - #cooling-cells = <2>; - }; - -- CPU3: cpu@3 { -+ cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - enable-method = "psci"; - reg = <0x3>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -86,7 +86,7 @@ - #cooling-cells = <2>; - }; - -- L2_0: l2-cache { -+ l2_0: l2-cache { - compatible = "cache"; - cache-level = <2>; - cache-unified; -@@ -1015,10 +1015,10 @@ - cooling-maps { - map0 { - trip = <&cpu_alert>; -- cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -32,39 +32,39 @@ - #address-cells = <1>; - #size-cells = <0>; - -- CPU0: cpu@0 { -+ cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x0>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - enable-method = "psci"; - }; - -- CPU1: cpu@1 { -+ cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - enable-method = "psci"; - reg = <0x1>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - }; - -- CPU2: cpu@2 { -+ cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - enable-method = "psci"; - reg = <0x2>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - }; - -- CPU3: cpu@3 { -+ cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - enable-method = "psci"; - reg = <0x3>; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - }; - -- L2_0: l2-cache { -+ l2_0: l2-cache { - compatible = "cache"; - cache-level = <2>; - cache-unified; ---- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi -@@ -34,12 +34,12 @@ - #address-cells = <1>; - #size-cells = <0>; - -- CPU0: cpu@0 { -+ cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a73"; - reg = <0x0>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -47,12 +47,12 @@ - #cooling-cells = <2>; - }; - -- CPU1: cpu@1 { -+ cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a73"; - reg = <0x1>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -60,12 +60,12 @@ - #cooling-cells = <2>; - }; - -- CPU2: cpu@2 { -+ cpu2: cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a73"; - reg = <0x2>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -73,12 +73,12 @@ - #cooling-cells = <2>; - }; - -- CPU3: cpu@3 { -+ cpu3: cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a73"; - reg = <0x3>; - enable-method = "psci"; -- next-level-cache = <&L2_0>; -+ next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -@@ -86,7 +86,7 @@ - #cooling-cells = <2>; - }; - -- L2_0: l2-cache { -+ l2_0: l2-cache { - compatible = "cache"; - cache-level = <2>; - cache-unified; -@@ -865,10 +865,10 @@ - cooling-maps { - map0 { - trip = <&cpu0_alert>; -- cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; -@@ -893,10 +893,10 @@ - cooling-maps { - map0 { - trip = <&cpu1_alert>; -- cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; -@@ -921,10 +921,10 @@ - cooling-maps { - map0 { - trip = <&cpu2_alert>; -- cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; -@@ -949,10 +949,10 @@ - cooling-maps { - map0 { - trip = <&cpu3_alert>; -- cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -- <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; diff --git a/target/linux/qualcommax/patches-6.12/0037-v6.16-arm64-dts-qcom-ipq6018-add-1.2GHz-CPU-Frequency.patch b/target/linux/qualcommax/patches-6.12/0037-v6.16-arm64-dts-qcom-ipq6018-add-1.2GHz-CPU-Frequency.patch deleted file mode 100644 index 5a5a7019abe..00000000000 --- a/target/linux/qualcommax/patches-6.12/0037-v6.16-arm64-dts-qcom-ipq6018-add-1.2GHz-CPU-Frequency.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 144230e5840c09984ad743c3df9de5fb443159a9 Mon Sep 17 00:00:00 2001 -From: Chukun Pan -Date: Mon, 10 Feb 2025 15:01:18 +0800 -Subject: [PATCH] arm64: dts: qcom: ipq6018: add 1.2GHz CPU Frequency - -The final version of IPQ6000 (SoC id: IPQ6000, SBL version: -BOOT.XF.0.3-00086-IPQ60xxLZB-1) has a max design frequency -of 1.2GHz, so add this CPU frequency. - -Signed-off-by: Chukun Pan -Reviewed-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20250210070122.208842-2-amadeus@jmu.edu.cn -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -119,6 +119,13 @@ - clock-latency-ns = <200000>; - }; - -+ opp-1200000000 { -+ opp-hz = /bits/ 64 <1200000000>; -+ opp-microvolt = <850000>; -+ opp-supported-hw = <0x4>; -+ clock-latency-ns = <200000>; -+ }; -+ - opp-1320000000 { - opp-hz = /bits/ 64 <1320000000>; - opp-microvolt = <862500>; diff --git a/target/linux/qualcommax/patches-6.12/0038-v6.16-arm64-dts-qcom-ipq6018-add-1.5GHz-CPU-Frequency.patch b/target/linux/qualcommax/patches-6.12/0038-v6.16-arm64-dts-qcom-ipq6018-add-1.5GHz-CPU-Frequency.patch deleted file mode 100644 index f87356c35b8..00000000000 --- a/target/linux/qualcommax/patches-6.12/0038-v6.16-arm64-dts-qcom-ipq6018-add-1.5GHz-CPU-Frequency.patch +++ /dev/null @@ -1,33 +0,0 @@ -From a96e765a7b3f64429f7eec3471a2093355ab041e Mon Sep 17 00:00:00 2001 -From: Chukun Pan -Date: Mon, 10 Feb 2025 15:01:19 +0800 -Subject: [PATCH] arm64: dts: qcom: ipq6018: add 1.5GHz CPU Frequency - -The early version of IPQ6000 (SoC id: IPQ6018, SBL version: -BOOT.XF.0.3-00077-IPQ60xxLZB-2) and IPQ6005 SoCs can reach -a max frequency of 1.5GHz, so add this CPU frequency. - -Signed-off-by: Chukun Pan -Reviewed-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20250210070122.208842-3-amadeus@jmu.edu.cn -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -140,6 +140,13 @@ - clock-latency-ns = <200000>; - }; - -+ opp-1512000000 { -+ opp-hz = /bits/ 64 <1512000000>; -+ opp-microvolt = <937500>; -+ opp-supported-hw = <0x2>; -+ clock-latency-ns = <200000>; -+ }; -+ - opp-1608000000 { - opp-hz = /bits/ 64 <1608000000>; - opp-microvolt = <987500>; diff --git a/target/linux/qualcommax/patches-6.12/0039-v6.16-arm64-dts-qcom-ipq6018-move-mp5496-regulator-out-of-.patch b/target/linux/qualcommax/patches-6.12/0039-v6.16-arm64-dts-qcom-ipq6018-move-mp5496-regulator-out-of-.patch deleted file mode 100644 index da39ae54c12..00000000000 --- a/target/linux/qualcommax/patches-6.12/0039-v6.16-arm64-dts-qcom-ipq6018-move-mp5496-regulator-out-of-.patch +++ /dev/null @@ -1,120 +0,0 @@ -From 0c4c0f14b7d704bcb728d018a74788771dc9286b Mon Sep 17 00:00:00 2001 -From: Chukun Pan -Date: Mon, 10 Feb 2025 15:01:20 +0800 -Subject: [PATCH] arm64: dts: qcom: ipq6018: move mp5496 regulator out of soc dtsi - -Some IPQ60xx SoCs don't come with the mp5496 pmic chip. The mp5496 -pmic was never part of the IPQ60xx SoC, it's optional, so we moved -it out of the soc dtsi. - -Signed-off-by: Chukun Pan -Reviewed-by: Dmitry Baryshkov -Link: https://lore.kernel.org/r/20250210070122.208842-4-amadeus@jmu.edu.cn -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq6018-cp01-c1.dts | 2 +- - arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi | 35 ++++++++++++++++++++++++++++ - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 14 ----------- - 3 files changed, 36 insertions(+), 15 deletions(-) - create mode 100644 arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi - ---- a/arch/arm64/boot/dts/qcom/ipq6018-cp01-c1.dts -+++ b/arch/arm64/boot/dts/qcom/ipq6018-cp01-c1.dts -@@ -7,7 +7,7 @@ - - /dts-v1/; - --#include "ipq6018.dtsi" -+#include "ipq6018-mp5496.dtsi" - - / { - model = "Qualcomm Technologies, Inc. IPQ6018/AP-CP01-C1"; ---- /dev/null -+++ b/arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi -@@ -0,0 +1,35 @@ -+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) -+/* -+ * ipq6018-mp5496.dtsi describes common properties (e.g. regulators) that -+ * apply to most devices that make use of the IPQ6018 SoC and MP5496 PMIC. -+ */ -+ -+#include "ipq6018.dtsi" -+ -+&cpu0 { -+ cpu-supply = <&ipq6018_s2>; -+}; -+ -+&cpu1 { -+ cpu-supply = <&ipq6018_s2>; -+}; -+ -+&cpu2 { -+ cpu-supply = <&ipq6018_s2>; -+}; -+ -+&cpu3 { -+ cpu-supply = <&ipq6018_s2>; -+}; -+ -+&rpm_requests { -+ regulators { -+ compatible = "qcom,rpm-mp5496-regulators"; -+ -+ ipq6018_s2: s2 { -+ regulator-min-microvolt = <725000>; -+ regulator-max-microvolt = <1062500>; -+ regulator-always-on; -+ }; -+ }; -+}; ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -43,7 +43,6 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -- cpu-supply = <&ipq6018_s2>; - #cooling-cells = <2>; - }; - -@@ -56,7 +55,6 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -- cpu-supply = <&ipq6018_s2>; - #cooling-cells = <2>; - }; - -@@ -69,7 +67,6 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -- cpu-supply = <&ipq6018_s2>; - #cooling-cells = <2>; - }; - -@@ -82,7 +79,6 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - operating-points-v2 = <&cpu_opp_table>; -- cpu-supply = <&ipq6018_s2>; - #cooling-cells = <2>; - }; - -@@ -184,16 +180,6 @@ - rpm_requests: rpm-requests { - compatible = "qcom,rpm-ipq6018", "qcom,glink-smd-rpm"; - qcom,glink-channels = "rpm_requests"; -- -- regulators { -- compatible = "qcom,rpm-mp5496-regulators"; -- -- ipq6018_s2: s2 { -- regulator-min-microvolt = <725000>; -- regulator-max-microvolt = <1062500>; -- regulator-always-on; -- }; -- }; - }; - }; - }; diff --git a/target/linux/qualcommax/patches-6.12/0040-v6.16-arm64-dts-qcom-ipq6018-rename-labels-of-mp5496-regul.patch b/target/linux/qualcommax/patches-6.12/0040-v6.16-arm64-dts-qcom-ipq6018-rename-labels-of-mp5496-regul.patch deleted file mode 100644 index 120ce53f8c1..00000000000 --- a/target/linux/qualcommax/patches-6.12/0040-v6.16-arm64-dts-qcom-ipq6018-rename-labels-of-mp5496-regul.patch +++ /dev/null @@ -1,50 +0,0 @@ -From e60f872c2dc4c1d9227977c8714373fe6328699c Mon Sep 17 00:00:00 2001 -From: Chukun Pan -Date: Mon, 10 Feb 2025 15:01:21 +0800 -Subject: [PATCH] arm64: dts: qcom: ipq6018: rename labels of mp5496 regulator - -Change the labels of mp5496 regulator from ipq6018 to mp5496. - -Suggested-by: Konrad Dybcio -Suggested-by: Dmitry Baryshkov -Signed-off-by: Chukun Pan -Link: https://lore.kernel.org/r/20250210070122.208842-5-amadeus@jmu.edu.cn -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi -@@ -7,26 +7,26 @@ - #include "ipq6018.dtsi" - - &cpu0 { -- cpu-supply = <&ipq6018_s2>; -+ cpu-supply = <&mp5496_s2>; - }; - - &cpu1 { -- cpu-supply = <&ipq6018_s2>; -+ cpu-supply = <&mp5496_s2>; - }; - - &cpu2 { -- cpu-supply = <&ipq6018_s2>; -+ cpu-supply = <&mp5496_s2>; - }; - - &cpu3 { -- cpu-supply = <&ipq6018_s2>; -+ cpu-supply = <&mp5496_s2>; - }; - - &rpm_requests { - regulators { - compatible = "qcom,rpm-mp5496-regulators"; - -- ipq6018_s2: s2 { -+ mp5496_s2: s2 { - regulator-min-microvolt = <725000>; - regulator-max-microvolt = <1062500>; - regulator-always-on; diff --git a/target/linux/qualcommax/patches-6.12/0041-v6.16-arm64-dts-qcom-ipq6018-add-LDOA2-regulator.patch b/target/linux/qualcommax/patches-6.12/0041-v6.16-arm64-dts-qcom-ipq6018-add-LDOA2-regulator.patch deleted file mode 100644 index ce98c62f831..00000000000 --- a/target/linux/qualcommax/patches-6.12/0041-v6.16-arm64-dts-qcom-ipq6018-add-LDOA2-regulator.patch +++ /dev/null @@ -1,33 +0,0 @@ -From a566fb9ba8ffecb56c50729390a9ea076f5c9532 Mon Sep 17 00:00:00 2001 -From: Chukun Pan -Date: Mon, 10 Feb 2025 15:01:22 +0800 -Subject: [PATCH] arm64: dts: qcom: ipq6018: add LDOA2 regulator - -Add LDOA2 regulator from MP5496 to support SDCC voltage scaling. - -Suggested-by: Robert Marko -Signed-off-by: Chukun Pan -Reviewed-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20250210070122.208842-6-amadeus@jmu.edu.cn -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi | 9 +++++++++ - 1 file changed, 9 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018-mp5496.dtsi -@@ -31,5 +31,14 @@ - regulator-max-microvolt = <1062500>; - regulator-always-on; - }; -+ -+ mp5496_l2: l2 { -+ regulator-min-microvolt = <1800000>; -+ regulator-max-microvolt = <3300000>; -+ }; - }; - }; -+ -+&sdhc { -+ vqmmc-supply = <&mp5496_l2>; -+}; diff --git a/target/linux/qualcommax/patches-6.12/0045-v6.16-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch b/target/linux/qualcommax/patches-6.12/0045-v6.16-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch deleted file mode 100644 index 622b3888bb0..00000000000 --- a/target/linux/qualcommax/patches-6.12/0045-v6.16-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch +++ /dev/null @@ -1,340 +0,0 @@ -From 74badb8b0b146668cc6c03eb58e2a814f9463d02 Mon Sep 17 00:00:00 2001 -From: Nitheesh Sekar -Date: Thu, 20 Feb 2025 15:12:46 +0530 -Subject: [PATCH] phy: qcom: Introduce PCIe UNIPHY 28LP driver - -Add Qualcomm PCIe UNIPHY 28LP driver support present in Qualcomm IPQ5332 -SoC and the phy init sequence. - -Reviewed-by: Dmitry Baryshkov -Signed-off-by: Nitheesh Sekar -Signed-off-by: Varadarajan Narayanan -Link: https://lore.kernel.org/r/20250220094251.230936-3-quic_varada@quicinc.com -Signed-off-by: Vinod Koul ---- - drivers/phy/qualcomm/Kconfig | 12 + - drivers/phy/qualcomm/Makefile | 1 + - .../phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c | 286 ++++++++++++++++++ - 3 files changed, 299 insertions(+) - create mode 100644 drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c - ---- a/drivers/phy/qualcomm/Kconfig -+++ b/drivers/phy/qualcomm/Kconfig -@@ -154,6 +154,18 @@ config PHY_QCOM_M31_USB - management. This driver is required even for peripheral only or - host only mode configurations. - -+config PHY_QCOM_UNIPHY_PCIE_28LP -+ bool "PCIE UNIPHY 28LP PHY driver" -+ depends on ARCH_QCOM -+ depends on HAS_IOMEM -+ depends on OF -+ select GENERIC_PHY -+ help -+ Enable this to support the PCIe UNIPHY 28LP phy transceiver that -+ is used with PCIe controllers on Qualcomm IPQ5332 chips. It -+ handles PHY initialization, clock management required after -+ resetting the hardware and power management. -+ - config PHY_QCOM_USB_HS - tristate "Qualcomm USB HS PHY module" - depends on USB_ULPI_BUS ---- a/drivers/phy/qualcomm/Makefile -+++ b/drivers/phy/qualcomm/Makefile -@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) += - obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o - obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o - obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o -+obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) += phy-qcom-uniphy-pcie-28lp.o - obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o - obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o - obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o ---- /dev/null -+++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c -@@ -0,0 +1,286 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright (c) 2025, The Linux Foundation. All rights reserved. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define RST_ASSERT_DELAY_MIN_US 100 -+#define RST_ASSERT_DELAY_MAX_US 150 -+#define PIPE_CLK_DELAY_MIN_US 5000 -+#define PIPE_CLK_DELAY_MAX_US 5100 -+#define CLK_EN_DELAY_MIN_US 30 -+#define CLK_EN_DELAY_MAX_US 50 -+#define CDR_CTRL_REG_1 0x80 -+#define CDR_CTRL_REG_2 0x84 -+#define CDR_CTRL_REG_3 0x88 -+#define CDR_CTRL_REG_4 0x8c -+#define CDR_CTRL_REG_5 0x90 -+#define CDR_CTRL_REG_6 0x94 -+#define CDR_CTRL_REG_7 0x98 -+#define SSCG_CTRL_REG_1 0x9c -+#define SSCG_CTRL_REG_2 0xa0 -+#define SSCG_CTRL_REG_3 0xa4 -+#define SSCG_CTRL_REG_4 0xa8 -+#define SSCG_CTRL_REG_5 0xac -+#define SSCG_CTRL_REG_6 0xb0 -+#define PCS_INTERNAL_CONTROL_2 0x2d8 -+ -+#define PHY_CFG_PLLCFG 0x220 -+#define PHY_CFG_EIOS_DTCT_REG 0x3e4 -+#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME 0x3e8 -+ -+enum qcom_uniphy_pcie_type { -+ PHY_TYPE_PCIE = 1, -+ PHY_TYPE_PCIE_GEN2, -+ PHY_TYPE_PCIE_GEN3, -+}; -+ -+struct qcom_uniphy_pcie_regs { -+ u32 offset; -+ u32 val; -+}; -+ -+struct qcom_uniphy_pcie_data { -+ int lane_offset; /* offset between the lane register bases */ -+ u32 phy_type; -+ const struct qcom_uniphy_pcie_regs *init_seq; -+ u32 init_seq_num; -+ u32 pipe_clk_rate; -+}; -+ -+struct qcom_uniphy_pcie { -+ struct phy phy; -+ struct device *dev; -+ const struct qcom_uniphy_pcie_data *data; -+ struct clk_bulk_data *clks; -+ int num_clks; -+ struct reset_control *resets; -+ void __iomem *base; -+ int lanes; -+}; -+ -+#define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy) -+ -+static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = { -+ { -+ .offset = PHY_CFG_PLLCFG, -+ .val = 0x30, -+ }, { -+ .offset = PHY_CFG_EIOS_DTCT_REG, -+ .val = 0x53ef, -+ }, { -+ .offset = PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME, -+ .val = 0xcf, -+ }, -+}; -+ -+static const struct qcom_uniphy_pcie_data ipq5332_data = { -+ .lane_offset = 0x800, -+ .phy_type = PHY_TYPE_PCIE_GEN3, -+ .init_seq = ipq5332_regs, -+ .init_seq_num = ARRAY_SIZE(ipq5332_regs), -+ .pipe_clk_rate = 250 * MEGA, -+}; -+ -+static void qcom_uniphy_pcie_init(struct qcom_uniphy_pcie *phy) -+{ -+ const struct qcom_uniphy_pcie_data *data = phy->data; -+ const struct qcom_uniphy_pcie_regs *init_seq; -+ void __iomem *base = phy->base; -+ int lane, i; -+ -+ for (lane = 0; lane < phy->lanes; lane++) { -+ init_seq = data->init_seq; -+ -+ for (i = 0; i < data->init_seq_num; i++) -+ writel(init_seq[i].val, base + init_seq[i].offset); -+ -+ base += data->lane_offset; -+ } -+} -+ -+static int qcom_uniphy_pcie_power_off(struct phy *x) -+{ -+ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x); -+ -+ clk_bulk_disable_unprepare(phy->num_clks, phy->clks); -+ -+ return reset_control_assert(phy->resets); -+} -+ -+static int qcom_uniphy_pcie_power_on(struct phy *x) -+{ -+ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x); -+ int ret; -+ -+ ret = reset_control_assert(phy->resets); -+ if (ret) { -+ dev_err(phy->dev, "reset assert failed (%d)\n", ret); -+ return ret; -+ } -+ -+ usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US); -+ -+ ret = reset_control_deassert(phy->resets); -+ if (ret) { -+ dev_err(phy->dev, "reset deassert failed (%d)\n", ret); -+ return ret; -+ } -+ -+ usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US); -+ -+ ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks); -+ if (ret) { -+ dev_err(phy->dev, "clk prepare and enable failed %d\n", ret); -+ return ret; -+ } -+ -+ usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US); -+ -+ qcom_uniphy_pcie_init(phy); -+ -+ return 0; -+} -+ -+static inline int qcom_uniphy_pcie_get_resources(struct platform_device *pdev, -+ struct qcom_uniphy_pcie *phy) -+{ -+ struct resource *res; -+ -+ phy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); -+ if (IS_ERR(phy->base)) -+ return PTR_ERR(phy->base); -+ -+ phy->num_clks = devm_clk_bulk_get_all(phy->dev, &phy->clks); -+ if (phy->num_clks < 0) -+ return phy->num_clks; -+ -+ phy->resets = devm_reset_control_array_get_exclusive(phy->dev); -+ if (IS_ERR(phy->resets)) -+ return PTR_ERR(phy->resets); -+ -+ return 0; -+} -+ -+/* -+ * Register a fixed rate pipe clock. -+ * -+ * The _pipe_clksrc generated by PHY goes to the GCC that gate -+ * controls it. The _pipe_clk coming out of the GCC is requested -+ * by the PHY driver for its operations. -+ * We register the _pipe_clksrc here. The gcc driver takes care -+ * of assigning this _pipe_clksrc as parent to _pipe_clk. -+ * Below picture shows this relationship. -+ * -+ * +---------------+ -+ * | PHY block |<<---------------------------------------+ -+ * | | | -+ * | +-------+ | +-----+ | -+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ -+ * clk | +-------+ | +-----+ -+ * +---------------+ -+ */ -+static inline int phy_pipe_clk_register(struct qcom_uniphy_pcie *phy, int id) -+{ -+ const struct qcom_uniphy_pcie_data *data = phy->data; -+ struct clk_hw *hw; -+ char name[64]; -+ -+ snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id); -+ hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0, -+ data->pipe_clk_rate); -+ if (IS_ERR(hw)) -+ return dev_err_probe(phy->dev, PTR_ERR(hw), -+ "Unable to register %s\n", name); -+ -+ return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw); -+} -+ -+static const struct of_device_id qcom_uniphy_pcie_id_table[] = { -+ { -+ .compatible = "qcom,ipq5332-uniphy-pcie-phy", -+ .data = &ipq5332_data, -+ }, { -+ /* Sentinel */ -+ }, -+}; -+MODULE_DEVICE_TABLE(of, qcom_uniphy_pcie_id_table); -+ -+static const struct phy_ops pcie_ops = { -+ .power_on = qcom_uniphy_pcie_power_on, -+ .power_off = qcom_uniphy_pcie_power_off, -+ .owner = THIS_MODULE, -+}; -+ -+static int qcom_uniphy_pcie_probe(struct platform_device *pdev) -+{ -+ struct phy_provider *phy_provider; -+ struct device *dev = &pdev->dev; -+ struct qcom_uniphy_pcie *phy; -+ struct phy *generic_phy; -+ int ret; -+ -+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); -+ if (!phy) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, phy); -+ phy->dev = &pdev->dev; -+ -+ phy->data = of_device_get_match_data(dev); -+ if (!phy->data) -+ return -EINVAL; -+ -+ ret = of_property_read_u32(dev_of_node(dev), "num-lanes", &phy->lanes); -+ if (ret) -+ return dev_err_probe(dev, ret, "Couldn't read num-lanes\n"); -+ -+ ret = qcom_uniphy_pcie_get_resources(pdev, phy); -+ if (ret < 0) -+ return dev_err_probe(&pdev->dev, ret, -+ "failed to get resources: %d\n", ret); -+ -+ generic_phy = devm_phy_create(phy->dev, NULL, &pcie_ops); -+ if (IS_ERR(generic_phy)) -+ return PTR_ERR(generic_phy); -+ -+ phy_set_drvdata(generic_phy, phy); -+ -+ ret = phy_pipe_clk_register(phy, generic_phy->id); -+ if (ret) -+ dev_err(&pdev->dev, "failed to register phy pipe clk\n"); -+ -+ phy_provider = devm_of_phy_provider_register(phy->dev, -+ of_phy_simple_xlate); -+ if (IS_ERR(phy_provider)) -+ return PTR_ERR(phy_provider); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_uniphy_pcie_driver = { -+ .probe = qcom_uniphy_pcie_probe, -+ .driver = { -+ .name = "qcom-uniphy-pcie", -+ .of_match_table = qcom_uniphy_pcie_id_table, -+ }, -+}; -+ -+module_platform_driver(qcom_uniphy_pcie_driver); -+ -+MODULE_DESCRIPTION("PCIE QCOM UNIPHY driver"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/qualcommax/patches-6.12/0046-v6.16-phy-qualcomm-qcom-uniphy-pcie-28LP-add-support-for-I.patch b/target/linux/qualcommax/patches-6.12/0046-v6.16-phy-qualcomm-qcom-uniphy-pcie-28LP-add-support-for-I.patch deleted file mode 100644 index 4c727fcc611..00000000000 --- a/target/linux/qualcommax/patches-6.12/0046-v6.16-phy-qualcomm-qcom-uniphy-pcie-28LP-add-support-for-I.patch +++ /dev/null @@ -1,86 +0,0 @@ -From dfc820d2f8a8ea90bbc02269b5362e3678e58cac Mon Sep 17 00:00:00 2001 -From: Nitheesh Sekar -Date: Wed, 26 Mar 2025 12:10:56 +0400 -Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie 28LP add support for IPQ5018 - -The Qualcomm UNIPHY PCIe PHY 28LP is found on both IPQ5332 and IPQ5018. -Adding the PHY init sequence, pipe clock rate, and compatible for IPQ5018. - -Signed-off-by: Nitheesh Sekar -Signed-off-by: Sricharan Ramabadhran -Reviewed-by: Dmitry Baryshkov -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250326-ipq5018-pcie-v7-2-e1828fef06c9@outlook.com -Signed-off-by: Vinod Koul ---- - .../phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c | 45 +++++++++++++++++++ - 1 file changed, 45 insertions(+) - ---- a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c -+++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c -@@ -75,6 +75,40 @@ struct qcom_uniphy_pcie { - - #define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy) - -+static const struct qcom_uniphy_pcie_regs ipq5018_regs[] = { -+ { -+ .offset = SSCG_CTRL_REG_4, -+ .val = 0x1cb9, -+ }, { -+ .offset = SSCG_CTRL_REG_5, -+ .val = 0x023a, -+ }, { -+ .offset = SSCG_CTRL_REG_3, -+ .val = 0xd360, -+ }, { -+ .offset = SSCG_CTRL_REG_1, -+ .val = 0x1, -+ }, { -+ .offset = SSCG_CTRL_REG_2, -+ .val = 0xeb, -+ }, { -+ .offset = CDR_CTRL_REG_4, -+ .val = 0x3f9, -+ }, { -+ .offset = CDR_CTRL_REG_5, -+ .val = 0x1c9, -+ }, { -+ .offset = CDR_CTRL_REG_2, -+ .val = 0x419, -+ }, { -+ .offset = CDR_CTRL_REG_1, -+ .val = 0x200, -+ }, { -+ .offset = PCS_INTERNAL_CONTROL_2, -+ .val = 0xf101, -+ }, -+}; -+ - static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = { - { - .offset = PHY_CFG_PLLCFG, -@@ -88,6 +122,14 @@ static const struct qcom_uniphy_pcie_reg - }, - }; - -+static const struct qcom_uniphy_pcie_data ipq5018_data = { -+ .lane_offset = 0x800, -+ .phy_type = PHY_TYPE_PCIE_GEN2, -+ .init_seq = ipq5018_regs, -+ .init_seq_num = ARRAY_SIZE(ipq5018_regs), -+ .pipe_clk_rate = 125 * MEGA, -+}; -+ - static const struct qcom_uniphy_pcie_data ipq5332_data = { - .lane_offset = 0x800, - .phy_type = PHY_TYPE_PCIE_GEN3, -@@ -212,6 +254,9 @@ static inline int phy_pipe_clk_register( - - static const struct of_device_id qcom_uniphy_pcie_id_table[] = { - { -+ .compatible = "qcom,ipq5018-uniphy-pcie-phy", -+ .data = &ipq5018_data, -+ }, { - .compatible = "qcom,ipq5332-uniphy-pcie-phy", - .data = &ipq5332_data, - }, { diff --git a/target/linux/qualcommax/patches-6.12/0047-v6.16-PCI-qcom-Add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.12/0047-v6.16-PCI-qcom-Add-support-for-IPQ5018.patch deleted file mode 100644 index de1277aade9..00000000000 --- a/target/linux/qualcommax/patches-6.12/0047-v6.16-PCI-qcom-Add-support-for-IPQ5018.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 3e5127469a8d41153fb30031a271788f52dd17ec Mon Sep 17 00:00:00 2001 -From: Nitheesh Sekar -Date: Wed, 26 Mar 2025 12:10:58 +0400 -Subject: [PATCH] PCI: qcom: Add support for IPQ5018 - -Add IPQ5018 platform with is based on Qcom IP rev. 2.9.0 and Synopsys IP -rev. 5.00a. - -The platform itself has two PCIe Gen2 controllers: one single-lane and -one dual-lane. So add the IPQ5018 compatible and re-use 2_9_0 ops. - -Signed-off-by: Nitheesh Sekar -Signed-off-by: Sricharan R -Signed-off-by: George Moussalem -Signed-off-by: Manivannan Sadhasivam -Reviewed-by: Manivannan Sadhasivam -Link: https://patch.msgid.link/20250326-ipq5018-pcie-v7-4-e1828fef06c9@outlook.com ---- - drivers/pci/controller/dwc/pcie-qcom.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/pci/controller/dwc/pcie-qcom.c -+++ b/drivers/pci/controller/dwc/pcie-qcom.c -@@ -1838,6 +1838,7 @@ static const struct of_device_id qcom_pc - { .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 }, - { .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 }, - { .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 }, -+ { .compatible = "qcom,pcie-ipq5018", .data = &cfg_2_9_0 }, - { .compatible = "qcom,pcie-ipq6018", .data = &cfg_2_9_0 }, - { .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 }, - { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 }, diff --git a/target/linux/qualcommax/patches-6.12/0048-v6.16-arm64-dts-qcom-ipq5018-Add-PCIe-related-nodes.patch b/target/linux/qualcommax/patches-6.12/0048-v6.16-arm64-dts-qcom-ipq5018-Add-PCIe-related-nodes.patch deleted file mode 100644 index a7fceeba653..00000000000 --- a/target/linux/qualcommax/patches-6.12/0048-v6.16-arm64-dts-qcom-ipq5018-Add-PCIe-related-nodes.patch +++ /dev/null @@ -1,286 +0,0 @@ -From 18a5bf00a02ca54d51266b861518f2844c4f08d7 Mon Sep 17 00:00:00 2001 -From: Nitheesh Sekar -Date: Wed, 14 May 2025 09:52:13 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PCIe related nodes - -Add phy and controller nodes for a 2-lane Gen2 and -a 1-lane Gen2 PCIe bus. IPQ5018 has 8 MSI SPI interrupts and -one global interrupt. - -NOTE: the PCIe controller supports gen3, yet the phy is limited to gen2. - -Signed-off-by: Nitheesh Sekar -Signed-off-by: Sricharan R -Reviewed-by: Manivannan Sadhasivam -Reviewed-by: Dmitry Baryshkov -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250514-ipq5018-pcie-v10-1-5b42a8eff7ea@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 240 +++++++++++++++++++++++++- - 1 file changed, 238 insertions(+), 2 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -147,6 +147,40 @@ - status = "disabled"; - }; - -+ pcie1_phy: phy@7e000 { -+ compatible = "qcom,ipq5018-uniphy-pcie-phy"; -+ reg = <0x0007e000 0x800>; -+ -+ clocks = <&gcc GCC_PCIE1_PIPE_CLK>; -+ -+ resets = <&gcc GCC_PCIE1_PHY_BCR>, -+ <&gcc GCC_PCIE1PHY_PHY_BCR>; -+ -+ #clock-cells = <0>; -+ #phy-cells = <0>; -+ -+ num-lanes = <1>; -+ -+ status = "disabled"; -+ }; -+ -+ pcie0_phy: phy@86000 { -+ compatible = "qcom,ipq5018-uniphy-pcie-phy"; -+ reg = <0x00086000 0x1000>; -+ -+ clocks = <&gcc GCC_PCIE0_PIPE_CLK>; -+ -+ resets = <&gcc GCC_PCIE0_PHY_BCR>, -+ <&gcc GCC_PCIE0PHY_PHY_BCR>; -+ -+ #clock-cells = <0>; -+ #phy-cells = <0>; -+ -+ num-lanes = <2>; -+ -+ status = "disabled"; -+ }; -+ - tlmm: pinctrl@1000000 { - compatible = "qcom,ipq5018-tlmm"; - reg = <0x01000000 0x300000>; -@@ -170,8 +204,8 @@ - reg = <0x01800000 0x80000>; - clocks = <&xo_board_clk>, - <&sleep_clk>, -- <0>, -- <0>, -+ <&pcie0_phy>, -+ <&pcie1_phy>, - <0>, - <0>, - <0>, -@@ -387,6 +421,208 @@ - status = "disabled"; - }; - }; -+ -+ pcie1: pcie@80000000 { -+ compatible = "qcom,pcie-ipq5018"; -+ reg = <0x80000000 0xf1d>, -+ <0x80000f20 0xa8>, -+ <0x80001000 0x1000>, -+ <0x00078000 0x3000>, -+ <0x80100000 0x1000>, -+ <0x0007b000 0x1000>; -+ reg-names = "dbi", -+ "elbi", -+ "atu", -+ "parf", -+ "config", -+ "mhi"; -+ device_type = "pci"; -+ linux,pci-domain = <1>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <1>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ /* The controller supports Gen3, but the connected PHY is Gen2-capable */ -+ max-link-speed = <2>; -+ -+ phys = <&pcie1_phy>; -+ phy-names ="pciephy"; -+ -+ ranges = <0x01000000 0 0x00000000 0x80200000 0 0x00100000>, -+ <0x02000000 0 0x80300000 0x80300000 0 0x10000000>; -+ -+ msi-map = <0x0 &v2m0 0x0 0xff8>; -+ -+ interrupts = , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ ; -+ interrupt-names = "msi0", -+ "msi1", -+ "msi2", -+ "msi3", -+ "msi4", -+ "msi5", -+ "msi6", -+ "msi7", -+ "global"; -+ -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 0 142 IRQ_TYPE_LEVEL_HIGH>, -+ <0 0 0 2 &intc 0 0 143 IRQ_TYPE_LEVEL_HIGH>, -+ <0 0 0 3 &intc 0 0 144 IRQ_TYPE_LEVEL_HIGH>, -+ <0 0 0 4 &intc 0 0 145 IRQ_TYPE_LEVEL_HIGH>; -+ -+ clocks = <&gcc GCC_SYS_NOC_PCIE1_AXI_CLK>, -+ <&gcc GCC_PCIE1_AXI_M_CLK>, -+ <&gcc GCC_PCIE1_AXI_S_CLK>, -+ <&gcc GCC_PCIE1_AHB_CLK>, -+ <&gcc GCC_PCIE1_AUX_CLK>, -+ <&gcc GCC_PCIE1_AXI_S_BRIDGE_CLK>; -+ clock-names = "iface", -+ "axi_m", -+ "axi_s", -+ "ahb", -+ "aux", -+ "axi_bridge"; -+ -+ resets = <&gcc GCC_PCIE1_PIPE_ARES>, -+ <&gcc GCC_PCIE1_SLEEP_ARES>, -+ <&gcc GCC_PCIE1_CORE_STICKY_ARES>, -+ <&gcc GCC_PCIE1_AXI_MASTER_ARES>, -+ <&gcc GCC_PCIE1_AXI_SLAVE_ARES>, -+ <&gcc GCC_PCIE1_AHB_ARES>, -+ <&gcc GCC_PCIE1_AXI_MASTER_STICKY_ARES>, -+ <&gcc GCC_PCIE1_AXI_SLAVE_STICKY_ARES>; -+ reset-names = "pipe", -+ "sleep", -+ "sticky", -+ "axi_m", -+ "axi_s", -+ "ahb", -+ "axi_m_sticky", -+ "axi_s_sticky"; -+ -+ status = "disabled"; -+ -+ pcie@0 { -+ device_type = "pci"; -+ reg = <0x0 0x0 0x0 0x0 0x0>; -+ bus-range = <0x01 0xff>; -+ -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges; -+ }; -+ }; -+ -+ pcie0: pcie@a0000000 { -+ compatible = "qcom,pcie-ipq5018"; -+ reg = <0xa0000000 0xf1d>, -+ <0xa0000f20 0xa8>, -+ <0xa0001000 0x1000>, -+ <0x00080000 0x3000>, -+ <0xa0100000 0x1000>, -+ <0x00083000 0x1000>; -+ reg-names = "dbi", -+ "elbi", -+ "atu", -+ "parf", -+ "config", -+ "mhi"; -+ device_type = "pci"; -+ linux,pci-domain = <0>; -+ bus-range = <0x00 0xff>; -+ num-lanes = <2>; -+ #address-cells = <3>; -+ #size-cells = <2>; -+ -+ /* The controller supports Gen3, but the connected PHY is Gen2-capable */ -+ max-link-speed = <2>; -+ -+ phys = <&pcie0_phy>; -+ phy-names ="pciephy"; -+ -+ ranges = <0x01000000 0 0x00000000 0xa0200000 0 0x00100000>, -+ <0x02000000 0 0xa0300000 0xa0300000 0 0x10000000>; -+ -+ msi-map = <0x0 &v2m0 0x0 0xff8>; -+ -+ interrupts = , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ ; -+ interrupt-names = "msi0", -+ "msi1", -+ "msi2", -+ "msi3", -+ "msi4", -+ "msi5", -+ "msi6", -+ "msi7", -+ "global"; -+ -+ #interrupt-cells = <1>; -+ interrupt-map-mask = <0 0 0 0x7>; -+ interrupt-map = <0 0 0 1 &intc 0 0 75 IRQ_TYPE_LEVEL_HIGH>, -+ <0 0 0 2 &intc 0 0 78 IRQ_TYPE_LEVEL_HIGH>, -+ <0 0 0 3 &intc 0 0 79 IRQ_TYPE_LEVEL_HIGH>, -+ <0 0 0 4 &intc 0 0 83 IRQ_TYPE_LEVEL_HIGH>; -+ -+ clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>, -+ <&gcc GCC_PCIE0_AXI_M_CLK>, -+ <&gcc GCC_PCIE0_AXI_S_CLK>, -+ <&gcc GCC_PCIE0_AHB_CLK>, -+ <&gcc GCC_PCIE0_AUX_CLK>, -+ <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>; -+ clock-names = "iface", -+ "axi_m", -+ "axi_s", -+ "ahb", -+ "aux", -+ "axi_bridge"; -+ -+ resets = <&gcc GCC_PCIE0_PIPE_ARES>, -+ <&gcc GCC_PCIE0_SLEEP_ARES>, -+ <&gcc GCC_PCIE0_CORE_STICKY_ARES>, -+ <&gcc GCC_PCIE0_AXI_MASTER_ARES>, -+ <&gcc GCC_PCIE0_AXI_SLAVE_ARES>, -+ <&gcc GCC_PCIE0_AHB_ARES>, -+ <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>, -+ <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>; -+ reset-names = "pipe", -+ "sleep", -+ "sticky", -+ "axi_m", -+ "axi_s", -+ "ahb", -+ "axi_m_sticky", -+ "axi_s_sticky"; -+ -+ status = "disabled"; -+ -+ pcie@0 { -+ device_type = "pci"; -+ reg = <0x0 0x0 0x0 0x0 0x0>; -+ bus-range = <0x01 0xff>; -+ -+ #address-cells = <3>; -+ #size-cells = <2>; -+ ranges; -+ }; -+ }; - }; - - timer { diff --git a/target/linux/qualcommax/patches-6.12/0049-v6.16-arm64-dts-qcom-ipq5018-enable-the-download-mode-supp.patch b/target/linux/qualcommax/patches-6.12/0049-v6.16-arm64-dts-qcom-ipq5018-enable-the-download-mode-supp.patch deleted file mode 100644 index 08200516a3f..00000000000 --- a/target/linux/qualcommax/patches-6.12/0049-v6.16-arm64-dts-qcom-ipq5018-enable-the-download-mode-supp.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 43fefd6c71291b5793e7c4052b6e3e54d1d87715 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 12 May 2025 18:36:47 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the download mode support - -Enable support for download mode to collect RAM dumps in case of a -system crash, allowing post mortem analysis. - -Signed-off-by: George Moussalem -Reviewed-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20250512-ipq5018-syscon-v1-2-eb1ad2414c3c@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -79,6 +79,7 @@ - firmware { - scm { - compatible = "qcom,scm-ipq5018", "qcom,scm"; -+ qcom,dload-mode = <&tcsr 0x6100>; - qcom,sdi-enabled; - }; - }; -@@ -221,6 +222,11 @@ - #hwlock-cells = <1>; - }; - -+ tcsr: syscon@1937000 { -+ compatible = "qcom,tcsr-ipq5018", "syscon"; -+ reg = <0x01937000 0x21000>; -+ }; -+ - sdhc_1: mmc@7804000 { - compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; - reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.12/0050-v6.15-thermal-drivers-tsens-Add-TSENS-enable-and-calibrati.patch b/target/linux/qualcommax/patches-6.12/0050-v6.15-thermal-drivers-tsens-Add-TSENS-enable-and-calibrati.patch deleted file mode 100644 index bf675556ad0..00000000000 --- a/target/linux/qualcommax/patches-6.12/0050-v6.15-thermal-drivers-tsens-Add-TSENS-enable-and-calibrati.patch +++ /dev/null @@ -1,297 +0,0 @@ -From ff0cf0ab9073727a67f9902dba77a758654ae895 Mon Sep 17 00:00:00 2001 -From: Praveenkumar I -Date: Mon, 10 Feb 2025 17:34:32 +0530 -Subject: [PATCH] thermal/drivers/tsens: Add TSENS enable and calibration - support for V2 - -SoCs without RPM need to enable sensors and calibrate them from the kernel. -The IPQ5332 and IPQ5424 use the tsens v2.3.3 IP and do not have RPM. -Therefore, add a new calibration function for V2, as the tsens.c calib -function only supports V1. Also add new feature_config, ops and data for -IPQ5332, IPQ5424. - -Although the TSENS IP supports 16 sensors, not all are used. The hw_id -is used to enable the relevant sensors. - -Reviewed-by: Dmitry Baryshkov -Signed-off-by: Praveenkumar I -Signed-off-by: Manikanta Mylavarapu -Link: https://lore.kernel.org/r/20250210120436.821684-3-quic_mmanikan@quicinc.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens-v2.c | 178 ++++++++++++++++++++++++++++++++ - drivers/thermal/qcom/tsens.c | 8 +- - drivers/thermal/qcom/tsens.h | 3 + - 3 files changed, 188 insertions(+), 1 deletion(-) - ---- a/drivers/thermal/qcom/tsens-v2.c -+++ b/drivers/thermal/qcom/tsens-v2.c -@@ -4,13 +4,32 @@ - * Copyright (c) 2018, Linaro Limited - */ - -+#include - #include -+#include - #include - #include "tsens.h" - - /* ----- SROT ------ */ - #define SROT_HW_VER_OFF 0x0000 - #define SROT_CTRL_OFF 0x0004 -+#define SROT_MEASURE_PERIOD 0x0008 -+#define SROT_Sn_CONVERSION 0x0060 -+#define V2_SHIFT_DEFAULT 0x0003 -+#define V2_SLOPE_DEFAULT 0x0cd0 -+#define V2_CZERO_DEFAULT 0x016a -+#define ONE_PT_SLOPE 0x0cd0 -+#define TWO_PT_SHIFTED_GAIN 921600 -+#define ONE_PT_CZERO_CONST 94 -+#define SW_RST_DEASSERT 0x0 -+#define SW_RST_ASSERT 0x1 -+#define MEASURE_PERIOD_2mSEC 0x1 -+#define RESULT_FORMAT_TEMP 0x1 -+#define TSENS_ENABLE 0x1 -+#define SENSOR_CONVERSION(n) (((n) * 4) + SROT_Sn_CONVERSION) -+#define CONVERSION_SHIFT_MASK GENMASK(24, 23) -+#define CONVERSION_SLOPE_MASK GENMASK(22, 10) -+#define CONVERSION_CZERO_MASK GENMASK(9, 0) - - /* ----- TM ------ */ - #define TM_INT_EN_OFF 0x0004 -@@ -50,6 +69,17 @@ static struct tsens_features ipq8074_fea - .trip_max_temp = 204000, - }; - -+static struct tsens_features ipq5332_feat = { -+ .ver_major = VER_2_X_NO_RPM, -+ .crit_int = 1, -+ .combo_int = 1, -+ .adc = 0, -+ .srot_split = 1, -+ .max_sensors = 16, -+ .trip_min_temp = 0, -+ .trip_max_temp = 204000, -+}; -+ - static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { - /* ----- SROT ------ */ - /* VERSION */ -@@ -59,6 +89,10 @@ static const struct reg_field tsens_v2_r - /* CTRL_OFF */ - [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), - [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), -+ [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 18), -+ [CODE_OR_TEMP] = REG_FIELD(SROT_CTRL_OFF, 21, 21), -+ -+ [MAIN_MEASURE_PERIOD] = REG_FIELD(SROT_MEASURE_PERIOD, 0, 7), - - /* ----- TM ------ */ - /* INTERRUPT ENABLE */ -@@ -104,6 +138,128 @@ static const struct reg_field tsens_v2_r - [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), - }; - -+static int tsens_v2_calibrate_sensor(struct device *dev, struct tsens_sensor *sensor, -+ struct regmap *map, u32 mode, u32 base0, u32 base1) -+{ -+ u32 shift = V2_SHIFT_DEFAULT; -+ u32 slope = V2_SLOPE_DEFAULT; -+ u32 czero = V2_CZERO_DEFAULT; -+ char name[20]; -+ u32 val; -+ int ret; -+ -+ /* Read offset value */ -+ ret = snprintf(name, sizeof(name), "tsens_sens%d_off", sensor->hw_id); -+ if (ret < 0) -+ return ret; -+ -+ ret = nvmem_cell_read_variable_le_u32(dev, name, &sensor->offset); -+ if (ret) -+ return ret; -+ -+ /* Based on calib mode, program SHIFT, SLOPE and CZERO */ -+ switch (mode) { -+ case TWO_PT_CALIB: -+ slope = (TWO_PT_SHIFTED_GAIN / (base1 - base0)); -+ -+ czero = (base0 + sensor->offset - ((base1 - base0) / 3)); -+ -+ break; -+ case ONE_PT_CALIB2: -+ czero = base0 + sensor->offset - ONE_PT_CZERO_CONST; -+ -+ slope = ONE_PT_SLOPE; -+ -+ break; -+ default: -+ dev_dbg(dev, "calibrationless mode\n"); -+ } -+ -+ val = FIELD_PREP(CONVERSION_SHIFT_MASK, shift) | -+ FIELD_PREP(CONVERSION_SLOPE_MASK, slope) | -+ FIELD_PREP(CONVERSION_CZERO_MASK, czero); -+ -+ regmap_write(map, SENSOR_CONVERSION(sensor->hw_id), val); -+ -+ return 0; -+} -+ -+static int tsens_v2_calibration(struct tsens_priv *priv) -+{ -+ struct device *dev = priv->dev; -+ u32 mode, base0, base1; -+ int i, ret; -+ -+ if (priv->num_sensors > MAX_SENSORS) -+ return -EINVAL; -+ -+ ret = nvmem_cell_read_variable_le_u32(priv->dev, "mode", &mode); -+ if (ret == -ENOENT) -+ dev_warn(priv->dev, "Calibration data not present in DT\n"); -+ if (ret < 0) -+ return ret; -+ -+ dev_dbg(priv->dev, "calibration mode is %d\n", mode); -+ -+ ret = nvmem_cell_read_variable_le_u32(priv->dev, "base0", &base0); -+ if (ret < 0) -+ return ret; -+ -+ ret = nvmem_cell_read_variable_le_u32(priv->dev, "base1", &base1); -+ if (ret < 0) -+ return ret; -+ -+ /* Calibrate each sensor */ -+ for (i = 0; i < priv->num_sensors; i++) { -+ ret = tsens_v2_calibrate_sensor(dev, &priv->sensor[i], priv->srot_map, -+ mode, base0, base1); -+ if (ret < 0) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int __init init_tsens_v2_no_rpm(struct tsens_priv *priv) -+{ -+ struct device *dev = priv->dev; -+ int i, ret; -+ u32 val = 0; -+ -+ ret = init_common(priv); -+ if (ret < 0) -+ return ret; -+ -+ priv->rf[CODE_OR_TEMP] = devm_regmap_field_alloc(dev, priv->srot_map, -+ priv->fields[CODE_OR_TEMP]); -+ if (IS_ERR(priv->rf[CODE_OR_TEMP])) -+ return PTR_ERR(priv->rf[CODE_OR_TEMP]); -+ -+ priv->rf[MAIN_MEASURE_PERIOD] = devm_regmap_field_alloc(dev, priv->srot_map, -+ priv->fields[MAIN_MEASURE_PERIOD]); -+ if (IS_ERR(priv->rf[MAIN_MEASURE_PERIOD])) -+ return PTR_ERR(priv->rf[MAIN_MEASURE_PERIOD]); -+ -+ regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_ASSERT); -+ -+ regmap_field_write(priv->rf[MAIN_MEASURE_PERIOD], MEASURE_PERIOD_2mSEC); -+ -+ /* Enable available sensors */ -+ for (i = 0; i < priv->num_sensors; i++) -+ val |= 1 << priv->sensor[i].hw_id; -+ -+ regmap_field_write(priv->rf[SENSOR_EN], val); -+ -+ /* Select temperature format, unit is deci-Celsius */ -+ regmap_field_write(priv->rf[CODE_OR_TEMP], RESULT_FORMAT_TEMP); -+ -+ regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_DEASSERT); -+ -+ regmap_field_write(priv->rf[TSENS_EN], TSENS_ENABLE); -+ -+ return 0; -+} -+ - static const struct tsens_ops ops_generic_v2 = { - .init = init_common, - .get_temp = get_temp_tsens_valid, -@@ -122,6 +278,28 @@ struct tsens_plat_data data_ipq8074 = { - .fields = tsens_v2_regfields, - }; - -+static const struct tsens_ops ops_ipq5332 = { -+ .init = init_tsens_v2_no_rpm, -+ .get_temp = get_temp_tsens_valid, -+ .calibrate = tsens_v2_calibration, -+}; -+ -+const struct tsens_plat_data data_ipq5332 = { -+ .num_sensors = 5, -+ .ops = &ops_ipq5332, -+ .hw_ids = (unsigned int []){11, 12, 13, 14, 15}, -+ .feat = &ipq5332_feat, -+ .fields = tsens_v2_regfields, -+}; -+ -+const struct tsens_plat_data data_ipq5424 = { -+ .num_sensors = 7, -+ .ops = &ops_ipq5332, -+ .hw_ids = (unsigned int []){9, 10, 11, 12, 13, 14, 15}, -+ .feat = &ipq5332_feat, -+ .fields = tsens_v2_regfields, -+}; -+ - /* Kept around for backward compatibility with old msm8996.dtsi */ - struct tsens_plat_data data_8996 = { - .num_sensors = 13, ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -975,7 +975,7 @@ int __init init_common(struct tsens_priv - ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); - if (ret) - goto err_put_device; -- if (!enabled) { -+ if (!enabled && (tsens_version(priv) != VER_2_X_NO_RPM)) { - dev_err(dev, "%s: device not enabled\n", __func__); - ret = -ENODEV; - goto err_put_device; -@@ -1102,6 +1102,12 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t - - static const struct of_device_id tsens_table[] = { - { -+ .compatible = "qcom,ipq5332-tsens", -+ .data = &data_ipq5332, -+ }, { -+ .compatible = "qcom,ipq5424-tsens", -+ .data = &data_ipq5424, -+ }, { - .compatible = "qcom,ipq8064-tsens", - .data = &data_8960, - }, { ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -35,6 +35,7 @@ enum tsens_ver { - VER_0_1, - VER_1_X, - VER_2_X, -+ VER_2_X_NO_RPM, - }; - - enum tsens_irq_type { -@@ -168,6 +169,7 @@ enum regfield_ids { - TSENS_SW_RST, - SENSOR_EN, - CODE_OR_TEMP, -+ MAIN_MEASURE_PERIOD, - - /* ----- TM ------ */ - /* TRDY */ -@@ -651,5 +653,6 @@ extern struct tsens_plat_data data_tsens - - /* TSENS v2 targets */ - extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; -+extern const struct tsens_plat_data data_ipq5332, data_ipq5424; - - #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/qualcommax/patches-6.12/0051-v6.16-thermal-drivers-qcom-tsens-Update-conditions-to-stri.patch b/target/linux/qualcommax/patches-6.12/0051-v6.16-thermal-drivers-qcom-tsens-Update-conditions-to-stri.patch deleted file mode 100644 index 7de3472a65e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0051-v6.16-thermal-drivers-qcom-tsens-Update-conditions-to-stri.patch +++ /dev/null @@ -1,69 +0,0 @@ -From e3f90f167a49902cda2408f7e91cca0dcfd5040a Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Fri, 28 Feb 2025 09:11:36 +0400 -Subject: [PATCH] thermal/drivers/qcom/tsens: Update conditions to strictly - evaluate for IP v2+ - -TSENS v2.0+ leverage features not available to prior versions such as -updated interrupts init routine, masked interrupts, and watchdog. -Currently, the checks in place evaluate whether the IP version is greater -than v1 which invalidates when updates to v1 or v1 minor versions are -implemented. As such, update the conditional statements to strictly -evaluate whether the version is greater than or equal to v2 (inclusive). - -Signed-off-by: George Moussalem -Reviewed-by: Dmitry Baryshkov -Reviewed-by: Amit Kucheria -Link: https://lore.kernel.org/r/DS7PR19MB8883434CAA053648E22AA8AC9DCC2@DS7PR19MB8883.namprd19.prod.outlook.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -447,7 +447,7 @@ static void tsens_set_interrupt(struct t - dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__, - irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW", - enable ? "en" : "dis"); -- if (tsens_version(priv) > VER_1_X) -+ if (tsens_version(priv) >= VER_2_X) - tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); - else - tsens_set_interrupt_v1(priv, hw_id, irq_type, enable); -@@ -499,7 +499,7 @@ static int tsens_read_irq_state(struct t - ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); - if (ret) - return ret; -- if (tsens_version(priv) > VER_1_X) { -+ if (tsens_version(priv) >= VER_2_X) { - ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); - if (ret) - return ret; -@@ -543,7 +543,7 @@ static int tsens_read_irq_state(struct t - - static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) - { -- if (ver > VER_1_X) -+ if (ver >= VER_2_X) - return mask & (1 << hw_id); - - /* v1, v0.1 don't have a irq mask register */ -@@ -733,7 +733,7 @@ static int tsens_set_trips(struct therma - static int tsens_enable_irq(struct tsens_priv *priv) - { - int ret; -- int val = tsens_version(priv) > VER_1_X ? 7 : 1; -+ int val = tsens_version(priv) >= VER_2_X ? 7 : 1; - - ret = regmap_field_write(priv->rf[INT_EN], val); - if (ret < 0) -@@ -1040,7 +1040,7 @@ int __init init_common(struct tsens_priv - } - } - -- if (tsens_version(priv) > VER_1_X && ver_minor > 2) { -+ if (tsens_version(priv) >= VER_2_X && ver_minor > 2) { - /* Watchdog is present only on v2.3+ */ - priv->feat->has_watchdog = 1; - for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) { diff --git a/target/linux/qualcommax/patches-6.12/0052-v6.16-thermal-drivers-qcom-tsens-Add-support-for-tsens-v1-.patch b/target/linux/qualcommax/patches-6.12/0052-v6.16-thermal-drivers-qcom-tsens-Add-support-for-tsens-v1-.patch deleted file mode 100644 index 0ed4f17501b..00000000000 --- a/target/linux/qualcommax/patches-6.12/0052-v6.16-thermal-drivers-qcom-tsens-Add-support-for-tsens-v1-.patch +++ /dev/null @@ -1,121 +0,0 @@ -From 19f9b02ebc8fe3babefbe76a37c74c5f4c174de1 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Fri, 28 Feb 2025 09:11:37 +0400 -Subject: [PATCH] thermal/drivers/qcom/tsens: Add support for tsens v1 without - RPM - -Adding generic support for SoCs with tsens v1.0 IP with no RPM. -Due to lack of RPM, tsens has to be reset and enabled in the driver -init. SoCs can have support for more sensors than those which will -actually be enabled. As such, init will only enable those explicitly -added to the hw_ids array. - -Co-developed-by: Sricharan Ramabadhran -Signed-off-by: Sricharan Ramabadhran -Signed-off-by: George Moussalem -Reviewed-by: Dmitry Baryshkov -Link: https://lore.kernel.org/r/DS7PR19MB8883C5D7974C7735E23923769DCC2@DS7PR19MB8883.namprd19.prod.outlook.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens-v1.c | 48 +++++++++++++++++++++++++++++++++ - drivers/thermal/qcom/tsens.c | 14 +++++++--- - drivers/thermal/qcom/tsens.h | 1 + - 3 files changed, 59 insertions(+), 4 deletions(-) - ---- a/drivers/thermal/qcom/tsens-v1.c -+++ b/drivers/thermal/qcom/tsens-v1.c -@@ -79,6 +79,17 @@ static struct tsens_features tsens_v1_fe - .trip_max_temp = 120000, - }; - -+static struct tsens_features tsens_v1_no_rpm_feat = { -+ .ver_major = VER_1_X_NO_RPM, -+ .crit_int = 0, -+ .combo_int = 0, -+ .adc = 1, -+ .srot_split = 1, -+ .max_sensors = 11, -+ .trip_min_temp = -40000, -+ .trip_max_temp = 120000, -+}; -+ - static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = { - /* ----- SROT ------ */ - /* VERSION */ -@@ -150,6 +161,43 @@ static int __init init_8956(struct tsens - return init_common(priv); - } - -+static int __init init_tsens_v1_no_rpm(struct tsens_priv *priv) -+{ -+ int i, ret; -+ u32 mask = 0; -+ -+ ret = init_common(priv); -+ if (ret < 0) { -+ dev_err(priv->dev, "Init common failed %d\n", ret); -+ return ret; -+ } -+ -+ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 1); -+ if (ret) { -+ dev_err(priv->dev, "Reset failed\n"); -+ return ret; -+ } -+ -+ for (i = 0; i < priv->num_sensors; i++) -+ mask |= BIT(priv->sensor[i].hw_id); -+ -+ ret = regmap_field_update_bits(priv->rf[SENSOR_EN], mask, mask); -+ if (ret) { -+ dev_err(priv->dev, "Sensor Enable failed\n"); -+ return ret; -+ } -+ -+ ret = regmap_field_write(priv->rf[TSENS_EN], 1); -+ if (ret) { -+ dev_err(priv->dev, "Enable failed\n"); -+ return ret; -+ } -+ -+ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 0); -+ -+ return ret; -+} -+ - static const struct tsens_ops ops_generic_v1 = { - .init = init_common, - .calibrate = calibrate_v1, ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -975,10 +975,16 @@ int __init init_common(struct tsens_priv - ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); - if (ret) - goto err_put_device; -- if (!enabled && (tsens_version(priv) != VER_2_X_NO_RPM)) { -- dev_err(dev, "%s: device not enabled\n", __func__); -- ret = -ENODEV; -- goto err_put_device; -+ if (!enabled) { -+ switch (tsens_version(priv)) { -+ case VER_1_X_NO_RPM: -+ case VER_2_X_NO_RPM: -+ break; -+ default: -+ dev_err(dev, "%s: device not enabled\n", __func__); -+ ret = -ENODEV; -+ goto err_put_device; -+ } - } - - priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -34,6 +34,7 @@ enum tsens_ver { - VER_0 = 0, - VER_0_1, - VER_1_X, -+ VER_1_X_NO_RPM, - VER_2_X, - VER_2_X_NO_RPM, - }; diff --git a/target/linux/qualcommax/patches-6.12/0053-v6.16-thermal-drivers-qcom-tsens-Add-support-for-IPQ5018-t.patch b/target/linux/qualcommax/patches-6.12/0053-v6.16-thermal-drivers-qcom-tsens-Add-support-for-IPQ5018-t.patch deleted file mode 100644 index 1c09ffc70ed..00000000000 --- a/target/linux/qualcommax/patches-6.12/0053-v6.16-thermal-drivers-qcom-tsens-Add-support-for-IPQ5018-t.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 04b31cc53fe0df0e87a37d18a3c0363d7dee218f Mon Sep 17 00:00:00 2001 -From: Sricharan Ramabadhran -Date: Fri, 28 Feb 2025 09:11:38 +0400 -Subject: [PATCH] thermal/drivers/qcom/tsens: Add support for IPQ5018 tsens - -IPQ5018 has tsens IP V1.0, 5 sensors of which 4 are in use and 1 -interrupt. The IP does not have a RPM, hence use init routine for -tsens v1.0 without RPM which does not early enable. - -Reviewed-by: Dmitry Baryshkov -Signed-off-by: Sricharan Ramabadhran -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/DS7PR19MB8883BD0E36C08DD1D03CE1CB9DCC2@DS7PR19MB8883.namprd19.prod.outlook.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens-v1.c | 14 ++++++++++++++ - drivers/thermal/qcom/tsens.c | 3 +++ - drivers/thermal/qcom/tsens.h | 3 +++ - 3 files changed, 20 insertions(+) - ---- a/drivers/thermal/qcom/tsens-v1.c -+++ b/drivers/thermal/qcom/tsens-v1.c -@@ -242,3 +242,17 @@ struct tsens_plat_data data_8976 = { - .feat = &tsens_v1_feat, - .fields = tsens_v1_regfields, - }; -+ -+const struct tsens_ops ops_ipq5018 = { -+ .init = init_tsens_v1_no_rpm, -+ .calibrate = tsens_calibrate_common, -+ .get_temp = get_temp_tsens_valid, -+}; -+ -+const struct tsens_plat_data data_ipq5018 = { -+ .num_sensors = 5, -+ .ops = &ops_ipq5018, -+ .hw_ids = (unsigned int []){0, 1, 2, 3, 4}, -+ .feat = &tsens_v1_no_rpm_feat, -+ .fields = tsens_v1_regfields, -+}; ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -1108,6 +1108,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t - - static const struct of_device_id tsens_table[] = { - { -+ .compatible = "qcom,ipq5018-tsens", -+ .data = &data_ipq5018, -+ }, { - .compatible = "qcom,ipq5332-tsens", - .data = &data_ipq5332, - }, { ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -652,6 +652,9 @@ extern struct tsens_plat_data data_8226, - /* TSENS v1 targets */ - extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956; - -+/* TSENS v1 with no RPM targets */ -+extern const struct tsens_plat_data data_ipq5018; -+ - /* TSENS v2 targets */ - extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; - extern const struct tsens_plat_data data_ipq5332, data_ipq5424; diff --git a/target/linux/qualcommax/patches-6.12/0054-v6.18-arm64-dts-qcom-ipq5018-Add-tsens-node.patch b/target/linux/qualcommax/patches-6.12/0054-v6.18-arm64-dts-qcom-ipq5018-Add-tsens-node.patch deleted file mode 100644 index 81e9302bd87..00000000000 --- a/target/linux/qualcommax/patches-6.12/0054-v6.18-arm64-dts-qcom-ipq5018-Add-tsens-node.patch +++ /dev/null @@ -1,240 +0,0 @@ -From 450a80623e3b8bb5dae59e0d56046fc3d0a88f3b Mon Sep 17 00:00:00 2001 -From: Sricharan Ramabadhran -Date: Thu, 12 Jun 2025 10:46:14 +0400 -Subject: arm64: dts: qcom: ipq5018: Add tsens node - -IPQ5018 has tsens V1.0 IP with 5 sensors, though 4 are in use. -There is no RPM, so tsens has to be manually enabled. Adding the tsens -and nvmem nodes and adding 4 thermal sensors (zones). The critical trip -temperature is set to 120'C with an action to reboot. - -In addition, adding a cooling device to the CPU thermal zone which uses -CPU frequency scaling. - -Reviewed-by: Dmitry Baryshkov -Signed-off-by: Sricharan Ramabadhran -Signed-off-by: George Moussalem -Reviewed-by: Konrad Dybcio -[bjorn: Added tsens-v1 fallback compatible, per binding] -Link: https://lore.kernel.org/r/20250612-ipq5018-tsens-v13-2-a210f3683240@outlook.com ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 178 ++++++++++++++++++++++++++++++++++ - 1 file changed, 178 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - - / { - interrupt-parent = <&intc>; -@@ -39,6 +40,7 @@ - next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; -+ #cooling-cells = <2>; - }; - - cpu1: cpu@1 { -@@ -49,6 +51,7 @@ - next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - operating-points-v2 = <&cpu_opp_table>; -+ #cooling-cells = <2>; - }; - - l2_0: l2-cache { -@@ -182,6 +185,117 @@ - status = "disabled"; - }; - -+ qfprom: qfprom@a0000 { -+ compatible = "qcom,ipq5018-qfprom", "qcom,qfprom"; -+ reg = <0x000a0000 0x1000>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ tsens_mode: mode@249 { -+ reg = <0x249 0x1>; -+ bits = <0 3>; -+ }; -+ -+ tsens_base1: base1@249 { -+ reg = <0x249 0x2>; -+ bits = <3 8>; -+ }; -+ -+ tsens_base2: base2@24a { -+ reg = <0x24a 0x2>; -+ bits = <3 8>; -+ }; -+ -+ tsens_s0_p1: s0-p1@24b { -+ reg = <0x24b 0x2>; -+ bits = <2 6>; -+ }; -+ -+ tsens_s0_p2: s0-p2@24c { -+ reg = <0x24c 0x1>; -+ bits = <1 6>; -+ }; -+ -+ tsens_s1_p1: s1-p1@24c { -+ reg = <0x24c 0x2>; -+ bits = <7 6>; -+ }; -+ -+ tsens_s1_p2: s1-p2@24d { -+ reg = <0x24d 0x2>; -+ bits = <5 6>; -+ }; -+ -+ tsens_s2_p1: s2-p1@24e { -+ reg = <0x24e 0x2>; -+ bits = <3 6>; -+ }; -+ -+ tsens_s2_p2: s2-p2@24f { -+ reg = <0x24f 0x1>; -+ bits = <1 6>; -+ }; -+ -+ tsens_s3_p1: s3-p1@24f { -+ reg = <0x24f 0x2>; -+ bits = <7 6>; -+ }; -+ -+ tsens_s3_p2: s3-p2@250 { -+ reg = <0x250 0x2>; -+ bits = <5 6>; -+ }; -+ -+ tsens_s4_p1: s4-p1@251 { -+ reg = <0x251 0x2>; -+ bits = <3 6>; -+ }; -+ -+ tsens_s4_p2: s4-p2@254 { -+ reg = <0x254 0x1>; -+ bits = <0 6>; -+ }; -+ }; -+ -+ tsens: thermal-sensor@4a9000 { -+ compatible = "qcom,ipq5018-tsens", "qcom,tsens-v1"; -+ reg = <0x004a9000 0x1000>, -+ <0x004a8000 0x1000>; -+ -+ nvmem-cells = <&tsens_mode>, -+ <&tsens_base1>, -+ <&tsens_base2>, -+ <&tsens_s0_p1>, -+ <&tsens_s0_p2>, -+ <&tsens_s1_p1>, -+ <&tsens_s1_p2>, -+ <&tsens_s2_p1>, -+ <&tsens_s2_p2>, -+ <&tsens_s3_p1>, -+ <&tsens_s3_p2>, -+ <&tsens_s4_p1>, -+ <&tsens_s4_p2>; -+ -+ nvmem-cell-names = "mode", -+ "base1", -+ "base2", -+ "s0_p1", -+ "s0_p2", -+ "s1_p1", -+ "s1_p2", -+ "s2_p1", -+ "s2_p2", -+ "s3_p1", -+ "s3_p2", -+ "s4_p1", -+ "s4_p2"; -+ -+ interrupts = ; -+ interrupt-names = "uplow"; -+ #qcom,sensors = <5>; -+ #thermal-sensor-cells = <1>; -+ }; -+ - tlmm: pinctrl@1000000 { - compatible = "qcom,ipq5018-tlmm"; - reg = <0x01000000 0x300000>; -@@ -630,6 +744,70 @@ - }; - }; - }; -+ -+ thermal-zones { -+ cpu-thermal { -+ thermal-sensors = <&tsens 2>; -+ -+ trips { -+ cpu-critical { -+ temperature = <120000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ -+ cpu_alert: cpu-passive { -+ temperature = <100000>; -+ hysteresis = <1000>; -+ type = "passive"; -+ }; -+ }; -+ -+ cooling-maps { -+ map0 { -+ trip = <&cpu_alert>; -+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, -+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; -+ }; -+ }; -+ }; -+ -+ gephy-thermal { -+ thermal-sensors = <&tsens 4>; -+ -+ trips { -+ gephy-critical { -+ temperature = <120000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ -+ top-glue-thermal { -+ thermal-sensors = <&tsens 3>; -+ -+ trips { -+ top-glue-critical { -+ temperature = <120000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ -+ ubi32-thermal { -+ thermal-sensors = <&tsens 1>; -+ -+ trips { -+ ubi32-critical { -+ temperature = <120000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; -+ }; -+ }; - - timer { - compatible = "arm,armv8-timer"; diff --git a/target/linux/qualcommax/patches-6.12/0056-v6.17-clk-qcom-gcc-ipq5018-fix-GE-PHY-reset.patch b/target/linux/qualcommax/patches-6.12/0056-v6.17-clk-qcom-gcc-ipq5018-fix-GE-PHY-reset.patch deleted file mode 100644 index 41399e419d3..00000000000 --- a/target/linux/qualcommax/patches-6.12/0056-v6.17-clk-qcom-gcc-ipq5018-fix-GE-PHY-reset.patch +++ /dev/null @@ -1,33 +0,0 @@ -From f6a4a55ae5d99f865e106916a9295548e381de47 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 30 Jun 2025 16:35:00 +0400 -Subject: clk: qcom: gcc-ipq5018: fix GE PHY reset - -The MISC reset is supposed to trigger a resets across the MDC, DSP, and -RX & TX clocks of the IPQ5018 internal GE PHY. So let's set the bitmask -of the reset definition accordingly in the GCC as per the downstream -driver. - -Link: https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/00743c3e82fa87cba4460e7a2ba32f473a9ce932 - -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250630-ipq5018-ge-phy-v6-1-01be06378c15@outlook.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/gcc-ipq5018.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -(limited to 'drivers/clk/qcom/gcc-ipq5018.c') - ---- a/drivers/clk/qcom/gcc-ipq5018.c -+++ b/drivers/clk/qcom/gcc-ipq5018.c -@@ -3661,7 +3661,7 @@ static const struct qcom_reset_map gcc_i - [GCC_WCSS_AXI_S_ARES] = { 0x59008, 6 }, - [GCC_WCSS_Q6_BCR] = { 0x18004, 0 }, - [GCC_WCSSAON_RESET] = { 0x59010, 0}, -- [GCC_GEPHY_MISC_ARES] = { 0x56004, 0 }, -+ [GCC_GEPHY_MISC_ARES] = { 0x56004, .bitmask = GENMASK(3, 0) }, - }; - - static const struct of_device_id gcc_ipq5018_match_table[] = { diff --git a/target/linux/qualcommax/patches-6.12/0057-v6.17-net-phy-qcom-at803x-Add-Qualcomm-IPQ5018-Internal-PHY-support.patch b/target/linux/qualcommax/patches-6.12/0057-v6.17-net-phy-qcom-at803x-Add-Qualcomm-IPQ5018-Internal-PHY-support.patch deleted file mode 100644 index 006f0b35f5e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0057-v6.17-net-phy-qcom-at803x-Add-Qualcomm-IPQ5018-Internal-PHY-support.patch +++ /dev/null @@ -1,243 +0,0 @@ -From d46502279a11b48ede1d8bf65a229c8231bf0602 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Fri, 13 Jun 2025 05:55:08 +0400 -Subject: net: phy: qcom: at803x: Add Qualcomm IPQ5018 Internal PHY support - -The IPQ5018 SoC contains a single internal Gigabit Ethernet PHY which -provides an MDI interface directly to an RJ45 connector or an external -switch over a PHY to PHY link. - -The PHY supports 10BASE-T/100BASE-TX/1000BASE-T link modes in SGMII -interface mode, CDT, auto-negotiation and 802.3az EEE. - -Let's add support for this PHY in the at803x driver as it falls within -the Qualcomm Atheros OUI. - -Reviewed-by: Andrew Lunn -Signed-off-by: George Moussalem -Link: https://patch.msgid.link/20250613-ipq5018-ge-phy-v5-2-9af06e34ea6b@outlook.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/phy/qcom/at803x.c | 167 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 167 insertions(+) - -(limited to 'drivers/net/phy/qcom/at803x.c') - ---- a/drivers/net/phy/qcom/at803x.c -+++ b/drivers/net/phy/qcom/at803x.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -93,6 +94,8 @@ - #define ATH8035_PHY_ID 0x004dd072 - #define AT8030_PHY_ID_MASK 0xffffffef - -+#define IPQ5018_PHY_ID 0x004dd0c0 -+ - #define QCA9561_PHY_ID 0x004dd042 - - #define AT803X_PAGE_FIBER 0 -@@ -105,6 +108,48 @@ - /* disable hibernation mode */ - #define AT803X_DISABLE_HIBERNATION_MODE BIT(2) - -+#define IPQ5018_PHY_FIFO_CONTROL 0x19 -+#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0) -+ -+#define IPQ5018_PHY_DEBUG_EDAC 0x4380 -+#define IPQ5018_PHY_MMD1_MDAC 0x8100 -+#define IPQ5018_PHY_DAC_MASK GENMASK(15, 8) -+ -+/* MDAC and EDAC values for short cable length */ -+#define IPQ5018_PHY_DEBUG_EDAC_VAL 0x10 -+#define IPQ5018_PHY_MMD1_MDAC_VAL 0x10 -+ -+#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000 -+#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001 -+#define IPQ5018_PHY_PCS_EEE_TX_TIMER 0x8008 -+#define IPQ5018_PHY_PCS_EEE_RX_TIMER 0x8009 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3 0x8074 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4 0x8075 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5 0x8076 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6 0x8077 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7 0x8078 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9 0x807a -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13 0x807e -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL14 0x807f -+ -+#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1 -+#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6 -+#define IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL 0x7880 -+#define IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL 0xc8 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL 0xc040 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL 0xa060 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL 0xc040 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL 0xa060 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL 0xc24c -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL 0xc060 -+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL 0xb060 -+#define IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL 0x90b0 -+ -+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1 -+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7, 4) -+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50 -+#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080 -+ - MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); - MODULE_AUTHOR("Matus Ujhelyi"); - MODULE_LICENSE("GPL"); -@@ -130,6 +175,11 @@ struct at803x_context { - u16 led_control; - }; - -+struct ipq5018_priv { -+ struct reset_control *rst; -+ bool set_short_cable_dac; -+}; -+ - static int at803x_write_page(struct phy_device *phydev, int page) - { - int mask; -@@ -960,6 +1010,109 @@ static int at8035_probe(struct phy_devic - return at8035_parse_dt(phydev); - } - -+static int ipq5018_cable_test_start(struct phy_device *phydev) -+{ -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL4, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL5, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL6, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL7, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL9, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL13, -+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3, -+ IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL); -+ -+ /* we do all the (time consuming) work later */ -+ return 0; -+} -+ -+static int ipq5018_config_init(struct phy_device *phydev) -+{ -+ struct ipq5018_priv *priv = phydev->priv; -+ u16 val; -+ -+ /* -+ * set LDO efuse: first temporarily store ANA_DAC_FILTER value from -+ * debug register as it will be reset once the ANA_LDO_EFUSE register -+ * is written to -+ */ -+ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER); -+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE, -+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK, -+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT); -+ at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val); -+ -+ /* set 8023AZ EEE TX and RX timer values */ -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER, -+ IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER, -+ IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL); -+ -+ /* set MSE threshold values */ -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1, -+ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2, -+ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL); -+ -+ /* PHY DAC values are optional and only set in a PHY to PHY link architecture */ -+ if (priv->set_short_cable_dac) { -+ /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */ -+ phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, -+ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL); -+ -+ /* setting EDAC (Error-detection and Correction) in debug register */ -+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC, -+ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL); -+ } -+ -+ return 0; -+} -+ -+static void ipq5018_link_change_notify(struct phy_device *phydev) -+{ -+ /* -+ * Reset the FIFO buffer upon link disconnects to clear any residual data -+ * which may cause issues with the FIFO which it cannot recover from. -+ */ -+ mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr, -+ IPQ5018_PHY_FIFO_CONTROL, IPQ5018_PHY_FIFO_RESET, -+ phydev->link ? IPQ5018_PHY_FIFO_RESET : 0); -+} -+ -+static int ipq5018_probe(struct phy_device *phydev) -+{ -+ struct device *dev = &phydev->mdio.dev; -+ struct ipq5018_priv *priv; -+ int ret; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->set_short_cable_dac = of_property_read_bool(dev->of_node, -+ "qcom,dac-preset-short-cable"); -+ -+ priv->rst = devm_reset_control_array_get_exclusive(dev); -+ if (IS_ERR(priv->rst)) -+ return dev_err_probe(dev, PTR_ERR(priv->rst), -+ "failed to acquire reset\n"); -+ -+ ret = reset_control_reset(priv->rst); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to reset\n"); -+ -+ phydev->priv = priv; -+ -+ return 0; -+} -+ - static struct phy_driver at803x_driver[] = { - { - /* Qualcomm Atheros AR8035 */ -@@ -1052,6 +1205,19 @@ static struct phy_driver at803x_driver[] - .soft_reset = genphy_soft_reset, - .config_aneg = at803x_config_aneg, - }, { -+ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID), -+ .name = "Qualcomm Atheros IPQ5018 internal PHY", -+ .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST, -+ .probe = ipq5018_probe, -+ .config_init = ipq5018_config_init, -+ .link_change_notify = ipq5018_link_change_notify, -+ .read_status = at803x_read_status, -+ .config_intr = at803x_config_intr, -+ .handle_interrupt = at803x_handle_interrupt, -+ .cable_test_start = ipq5018_cable_test_start, -+ .cable_test_get_status = qca808x_cable_test_get_status, -+ .soft_reset = genphy_soft_reset, -+}, { - /* Qualcomm Atheros QCA9561 */ - PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), - .name = "Qualcomm Atheros QCA9561 built-in PHY", -@@ -1077,6 +1243,7 @@ static const struct mdio_device_id __may - { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, - { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, - { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, -+ { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) }, - { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, - { } - }; diff --git a/target/linux/qualcommax/patches-6.12/0058-v6.18-arm64-dts-qcom-ipq5018-Add-MDIO-buses.patch b/target/linux/qualcommax/patches-6.12/0058-v6.18-arm64-dts-qcom-ipq5018-Add-MDIO-buses.patch deleted file mode 100644 index 59f6bc2cc47..00000000000 --- a/target/linux/qualcommax/patches-6.12/0058-v6.18-arm64-dts-qcom-ipq5018-Add-MDIO-buses.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 1e2261a669a9596ba435c6fe524e026bac0f0e2f Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 30 Jun 2025 16:35:01 +0400 -Subject: arm64: dts: qcom: ipq5018: Add MDIO buses - -IPQ5018 contains two mdio buses of which one bus is used to control the -SoC's internal GE PHY, while the other bus is connected to external PHYs -or switches. - -There's already support for IPQ5018 in the mdio-ipq4019 driver, so let's -simply add the mdio nodes for them. - -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250630-ipq5018-ge-phy-v6-2-01be06378c15@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -185,6 +185,30 @@ - status = "disabled"; - }; - -+ mdio0: mdio@88000 { -+ compatible = "qcom,ipq5018-mdio"; -+ reg = <0x00088000 0x64>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ clocks = <&gcc GCC_MDIO0_AHB_CLK>; -+ clock-names = "gcc_mdio_ahb_clk"; -+ -+ status = "disabled"; -+ }; -+ -+ mdio1: mdio@90000 { -+ compatible = "qcom,ipq5018-mdio"; -+ reg = <0x00090000 0x64>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ clocks = <&gcc GCC_MDIO1_AHB_CLK>; -+ clock-names = "gcc_mdio_ahb_clk"; -+ -+ status = "disabled"; -+ }; -+ - qfprom: qfprom@a0000 { - compatible = "qcom,ipq5018-qfprom", "qcom,qfprom"; - reg = <0x000a0000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.12/0059-v6.18-arm64-dts-qcom-ipq5018-Add-GE-PHY-to-internal-mdio-bus.patch b/target/linux/qualcommax/patches-6.12/0059-v6.18-arm64-dts-qcom-ipq5018-Add-GE-PHY-to-internal-mdio-bus.patch deleted file mode 100644 index e8d96b8e0a9..00000000000 --- a/target/linux/qualcommax/patches-6.12/0059-v6.18-arm64-dts-qcom-ipq5018-Add-GE-PHY-to-internal-mdio-bus.patch +++ /dev/null @@ -1,82 +0,0 @@ -From f5f2b835e316df29b89e28ed7e467df473932e8d Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 30 Jun 2025 16:35:02 +0400 -Subject: arm64: dts: qcom: ipq5018: Add GE PHY to internal mdio bus - -The IPQ5018 SoC contains an internal GE PHY, always at phy address 7. -As such, let's add the GE PHY node to the SoC dtsi. - -The LDO controller found in the SoC must be enabled to provide constant -low voltages to the PHY. The mdio-ipq4019 driver already has support -for this, so adding the appropriate TCSR register offset. - -In addition, the GE PHY outputs both the RX and TX clocks to the GCC -which gate controls them and routes them back to the PHY itself. -So let's create two DT fixed clocks and register them in the GCC node. - -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250630-ipq5018-ge-phy-v6-3-01be06378c15@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 26 +++++++++++++++++++++++--- - 1 file changed, 23 insertions(+), 3 deletions(-) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -17,6 +17,18 @@ - #size-cells = <2>; - - clocks { -+ gephy_rx_clk: gephy-rx-clk { -+ compatible = "fixed-clock"; -+ clock-frequency = <125000000>; -+ #clock-cells = <0>; -+ }; -+ -+ gephy_tx_clk: gephy-tx-clk { -+ compatible = "fixed-clock"; -+ clock-frequency = <125000000>; -+ #clock-cells = <0>; -+ }; -+ - sleep_clk: sleep-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; -@@ -187,7 +199,8 @@ - - mdio0: mdio@88000 { - compatible = "qcom,ipq5018-mdio"; -- reg = <0x00088000 0x64>; -+ reg = <0x00088000 0x64>, -+ <0x019475c4 0x4>; - #address-cells = <1>; - #size-cells = <0>; - -@@ -195,6 +208,13 @@ - clock-names = "gcc_mdio_ahb_clk"; - - status = "disabled"; -+ -+ ge_phy: ethernet-phy@7 { -+ compatible = "ethernet-phy-id004d.d0c0"; -+ reg = <7>; -+ -+ resets = <&gcc GCC_GEPHY_MISC_ARES>; -+ }; - }; - - mdio1: mdio@90000 { -@@ -346,8 +366,8 @@ - <&pcie0_phy>, - <&pcie1_phy>, - <0>, -- <0>, -- <0>, -+ <&gephy_rx_clk>, -+ <&gephy_tx_clk>, - <0>, - <0>; - #clock-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.12/0060-v6.18-arm64-dts-qcom-ipq5018-Add-SPI-nand-support.patch b/target/linux/qualcommax/patches-6.12/0060-v6.18-arm64-dts-qcom-ipq5018-Add-SPI-nand-support.patch deleted file mode 100644 index f9b47cdbddd..00000000000 --- a/target/linux/qualcommax/patches-6.12/0060-v6.18-arm64-dts-qcom-ipq5018-Add-SPI-nand-support.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 8d2a8e8dc448f218b36b3b9f3790c9c0dfaa2b74 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Thu, 1 May 2025 13:20:52 +0400 -Subject: arm64: dts: qcom: ipq5018: Add SPI nand support - -Add QPIC SPI NAND support for IPQ5018 SoC. - -Signed-off-by: George Moussalem -Reviewed-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20250501-ipq5018-spi-qpic-snand-v1-2-31e01fbb606f@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 38 +++++++++++++++++++++++++++++++++++ - 1 file changed, 38 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -436,6 +436,44 @@ - status = "disabled"; - }; - -+ qpic_bam: dma-controller@7984000 { -+ compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0"; -+ reg = <0x07984000 0x1c000>; -+ -+ interrupts = ; -+ -+ clocks = <&gcc GCC_QPIC_AHB_CLK>; -+ clock-names = "bam_clk"; -+ -+ #dma-cells = <1>; -+ qcom,ee = <0>; -+ -+ status = "disabled"; -+ }; -+ -+ qpic_nand: spi@79b0000 { -+ compatible = "qcom,ipq5018-snand", "qcom,ipq9574-snand"; -+ reg = <0x079b0000 0x10000>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ clocks = <&gcc GCC_QPIC_CLK>, -+ <&gcc GCC_QPIC_AHB_CLK>, -+ <&gcc GCC_QPIC_IO_MACRO_CLK>; -+ clock-names = "core", -+ "aon", -+ "iom"; -+ -+ dmas = <&qpic_bam 0>, -+ <&qpic_bam 1>, -+ <&qpic_bam 2>; -+ dma-names = "tx", -+ "rx", -+ "cmd"; -+ -+ status = "disabled"; -+ }; -+ - usb: usb@8af8800 { - compatible = "qcom,ipq5018-dwc3", "qcom,dwc3"; - reg = <0x08af8800 0x400>; diff --git a/target/linux/qualcommax/patches-6.12/0061-v6.18-arm64-dts-qcom-ipq5018-add-PRNG-node.patch b/target/linux/qualcommax/patches-6.12/0061-v6.18-arm64-dts-qcom-ipq5018-add-PRNG-node.patch deleted file mode 100644 index bdb15ff9384..00000000000 --- a/target/linux/qualcommax/patches-6.12/0061-v6.18-arm64-dts-qcom-ipq5018-add-PRNG-node.patch +++ /dev/null @@ -1,34 +0,0 @@ -From bf258fdaa2bcbafa04bf1ad646e8ed42e13033cb Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 21 Jul 2025 10:30:46 +0400 -Subject: arm64: dts: qcom: ipq5018: add PRNG node - -PRNG inside of IPQ5018 is already supported, so let's add the node for it. - -Signed-off-by: George Moussalem -Reviewed-by: Dmitry Baryshkov -Link: https://lore.kernel.org/r/20250721-ipq5018-prng-v1-1-474310e0575d@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 8 ++++++++ - 1 file changed, 8 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -301,6 +301,14 @@ - }; - }; - -+ prng: rng@e3000 { -+ compatible = "qcom,prng-ee"; -+ reg = <0x000e3000 0x1000>; -+ clocks = <&gcc GCC_PRNG_AHB_CLK>; -+ clock-names = "core"; -+ status = "disabled"; -+ }; -+ - tsens: thermal-sensor@4a9000 { - compatible = "qcom,ipq5018-tsens", "qcom,tsens-v1"; - reg = <0x004a9000 0x1000>, diff --git a/target/linux/qualcommax/patches-6.12/0062-v6.18-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch b/target/linux/qualcommax/patches-6.12/0062-v6.18-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch deleted file mode 100644 index 808f8ea570f..00000000000 --- a/target/linux/qualcommax/patches-6.12/0062-v6.18-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch +++ /dev/null @@ -1,58 +0,0 @@ -From ed5789ba7c2ce548f01ebbdbd7488dd9cccc4513 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 21 Jul 2025 10:23:15 +0400 -Subject: arm64: dts: qcom: ipq5018: Add crypto nodes - -IPQ5018 uses Qualcomm QCE crypto engine v5.1 which is already supported. -So let's add the dts nodes for its DMA v1.7.4 and QCE itself. - -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Reviewed-by: Dmitry Baryshkov -Link: https://lore.kernel.org/r/20250721-ipq5018-crypto-v3-1-b9cd9b0ef147@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -348,6 +348,36 @@ - #thermal-sensor-cells = <1>; - }; - -+ cryptobam: dma-controller@704000 { -+ compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0"; -+ reg = <0x00704000 0x20000>; -+ interrupts = ; -+ -+ clocks = <&gcc GCC_CRYPTO_AHB_CLK>; -+ clock-names = "bam_clk"; -+ -+ #dma-cells = <1>; -+ qcom,ee = <1>; -+ qcom,controlled-remotely; -+ }; -+ -+ crypto: crypto@73a000 { -+ compatible = "qcom,crypto-v5.1"; -+ reg = <0x0073a000 0x6000>; -+ -+ clocks = <&gcc GCC_CRYPTO_AHB_CLK>, -+ <&gcc GCC_CRYPTO_AXI_CLK>, -+ <&gcc GCC_CRYPTO_CLK>; -+ clock-names = "iface", -+ "bus", -+ "core"; -+ -+ dmas = <&cryptobam 2>, -+ <&cryptobam 3>; -+ dma-names = "rx", -+ "tx"; -+ }; -+ - tlmm: pinctrl@1000000 { - compatible = "qcom,ipq5018-tlmm"; - reg = <0x01000000 0x300000>; diff --git a/target/linux/qualcommax/patches-6.12/0063-v6.14-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller.patch b/target/linux/qualcommax/patches-6.12/0063-v6.14-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller.patch deleted file mode 100644 index 01312eb5d11..00000000000 --- a/target/linux/qualcommax/patches-6.12/0063-v6.14-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller.patch +++ /dev/null @@ -1,132 +0,0 @@ -From c0f1cbf795095c21b92a46fa1dc47a7b787ce538 Mon Sep 17 00:00:00 2001 -From: Luo Jie -Date: Fri, 3 Jan 2025 15:31:34 +0800 -Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL clock controller for - IPQ SoC - -The CMN PLL controller provides clocks to networking hardware blocks -and to GCC on Qualcomm IPQ9574 SoC. It receives input clock from the -on-chip Wi-Fi, and produces output clocks at fixed rates. These output -rates are predetermined, and are unrelated to the input clock rate. -The primary purpose of CMN PLL is to supply clocks to the networking -hardware such as PPE (packet process engine), PCS and the externally -connected switch or PHY device. The CMN PLL block also outputs fixed -rate clocks to GCC, such as 24 MHZ as XO clock and 32 KHZ as sleep -clock supplied to GCC. - -Signed-off-by: Luo Jie -Reviewed-by: Krzysztof Kozlowski -Link: https://lore.kernel.org/r/20250103-qcom_ipq_cmnpll-v8-1-c89fb4d4849d@quicinc.com -Signed-off-by: Bjorn Andersson ---- - .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 77 +++++++++++++++++++ - include/dt-bindings/clock/qcom,ipq-cmn-pll.h | 22 ++++++ - 2 files changed, 99 insertions(+) - create mode 100644 Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml - create mode 100644 include/dt-bindings/clock/qcom,ipq-cmn-pll.h - ---- /dev/null -+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -@@ -0,0 +1,77 @@ -+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/clock/qcom,ipq9574-cmn-pll.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Qualcomm CMN PLL Clock Controller on IPQ SoC -+ -+maintainers: -+ - Bjorn Andersson -+ - Luo Jie -+ -+description: -+ The CMN (or common) PLL clock controller expects a reference -+ input clock. This reference clock is from the on-board Wi-Fi. -+ The CMN PLL supplies a number of fixed rate output clocks to -+ the devices providing networking functions and to GCC. These -+ networking hardware include PPE (packet process engine), PCS -+ and the externally connected switch or PHY devices. The CMN -+ PLL block also outputs fixed rate clocks to GCC. The PLL's -+ primary function is to enable fixed rate output clocks for -+ networking hardware functions used with the IPQ SoC. -+ -+properties: -+ compatible: -+ enum: -+ - qcom,ipq9574-cmn-pll -+ -+ reg: -+ maxItems: 1 -+ -+ clocks: -+ items: -+ - description: The reference clock. The supported clock rates include -+ 25000000, 31250000, 40000000, 48000000, 50000000 and 96000000 HZ. -+ - description: The AHB clock -+ - description: The SYS clock -+ description: -+ The reference clock is the source clock of CMN PLL, which is from the -+ Wi-Fi. The AHB and SYS clocks must be enabled to access CMN PLL -+ clock registers. -+ -+ clock-names: -+ items: -+ - const: ref -+ - const: ahb -+ - const: sys -+ -+ "#clock-cells": -+ const: 1 -+ -+required: -+ - compatible -+ - reg -+ - clocks -+ - clock-names -+ - "#clock-cells" -+ -+additionalProperties: false -+ -+examples: -+ - | -+ #include -+ #include -+ -+ cmn_pll: clock-controller@9b000 { -+ compatible = "qcom,ipq9574-cmn-pll"; -+ reg = <0x0009b000 0x800>; -+ clocks = <&cmn_pll_ref_clk>, -+ <&gcc GCC_CMN_12GPLL_AHB_CLK>, -+ <&gcc GCC_CMN_12GPLL_SYS_CLK>; -+ clock-names = "ref", "ahb", "sys"; -+ #clock-cells = <1>; -+ assigned-clocks = <&cmn_pll CMN_PLL_CLK>; -+ assigned-clock-rates-u64 = /bits/ 64 <12000000000>; -+ }; -+... ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq-cmn-pll.h -@@ -0,0 +1,22 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ9574. */ -+#define XO_24MHZ_CLK 1 -+#define SLEEP_32KHZ_CLK 2 -+#define PCS_31P25MHZ_CLK 3 -+#define NSS_1200MHZ_CLK 4 -+#define PPE_353MHZ_CLK 5 -+#define ETH0_50MHZ_CLK 6 -+#define ETH1_50MHZ_CLK 7 -+#define ETH2_50MHZ_CLK 8 -+#define ETH_25MHZ_CLK 9 -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0064-v6.14-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ-SoC.patch b/target/linux/qualcommax/patches-6.12/0064-v6.14-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ-SoC.patch deleted file mode 100644 index 350e8eeabbd..00000000000 --- a/target/linux/qualcommax/patches-6.12/0064-v6.14-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ-SoC.patch +++ /dev/null @@ -1,500 +0,0 @@ -From f81715a4c87c3b75ca2640bb61b6c66506061a64 Mon Sep 17 00:00:00 2001 -From: Luo Jie -Date: Fri, 3 Jan 2025 15:31:35 +0800 -Subject: [PATCH] clk: qcom: Add CMN PLL clock controller driver for IPQ SoC - -The CMN PLL clock controller supplies clocks to the hardware -blocks that together make up the Ethernet function on Qualcomm -IPQ SoCs and to GCC. The driver is initially supported for -IPQ9574 SoC. - -The CMN PLL clock controller expects a reference input clock -from the on-board Wi-Fi block acting as clock source. The input -reference clock needs to be configured to one of the supported -clock rates. - -The controller supplies a number of fixed-rate output clocks. -For the IPQ9574, there is one output clock of 353 MHZ to PPE -(Packet Process Engine) hardware block, three 50 MHZ output -clocks and an additional 25 MHZ output clock supplied to the -connected Ethernet devices. The PLL also supplies a 24 MHZ -clock as XO and a 32 KHZ sleep clock to GCC, and one 31.25 -MHZ clock to PCS. - -Signed-off-by: Luo Jie -Acked-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20250103-qcom_ipq_cmnpll-v8-2-c89fb4d4849d@quicinc.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/Kconfig | 9 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/ipq-cmn-pll.c | 435 +++++++++++++++++++++++++++++++++ - 3 files changed, 445 insertions(+) - create mode 100644 drivers/clk/qcom/ipq-cmn-pll.c - ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -190,6 +190,15 @@ config IPQ_APSS_6018 - Say Y if you want to support CPU frequency scaling on - ipq based devices. - -+config IPQ_CMN_PLL -+ tristate "IPQ CMN PLL Clock Controller" -+ help -+ Support for CMN PLL clock controller on IPQ platform. The -+ CMN PLL consumes the AHB/SYS clocks from GCC and supplies -+ the output clocks to the networking hardware and GCC blocks. -+ Say Y or M if you want to support CMN PLL clock on the IPQ -+ based devices. -+ - config IPQ_GCC_4019 - tristate "IPQ4019 Global Clock Controller" - help ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -29,6 +29,7 @@ obj-$(CONFIG_CLK_X1E80100_TCSRCC) += tcs - obj-$(CONFIG_CLK_QCM2290_GPUCC) += gpucc-qcm2290.o - obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o - obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o -+obj-$(CONFIG_IPQ_CMN_PLL) += ipq-cmn-pll.o - obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o - obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o - obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o ---- /dev/null -+++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -0,0 +1,435 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+/* -+ * CMN PLL block expects the reference clock from on-board Wi-Fi block, -+ * and supplies fixed rate clocks as output to the networking hardware -+ * blocks and to GCC. The networking related blocks include PPE (packet -+ * process engine), the externally connected PHY or switch devices, and -+ * the PCS. -+ * -+ * On the IPQ9574 SoC, there are three clocks with 50 MHZ and one clock -+ * with 25 MHZ which are output from the CMN PLL to Ethernet PHY (or switch), -+ * and one clock with 353 MHZ to PPE. The other fixed rate output clocks -+ * are supplied to GCC (24 MHZ as XO and 32 KHZ as sleep clock), and to PCS -+ * with 31.25 MHZ. -+ * -+ * +---------+ -+ * | GCC | -+ * +--+---+--+ -+ * AHB CLK| |SYS CLK -+ * V V -+ * +-------+---+------+ -+ * | +-------------> eth0-50mhz -+ * REF CLK | IPQ9574 | -+ * -------->+ +-------------> eth1-50mhz -+ * | CMN PLL block | -+ * | +-------------> eth2-50mhz -+ * | | -+ * +----+----+----+---+-------------> eth-25mhz -+ * | | | -+ * V V V -+ * GCC PCS NSS/PPE -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define CMN_PLL_REFCLK_SRC_SELECTION 0x28 -+#define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) -+ -+#define CMN_PLL_LOCKED 0x64 -+#define CMN_PLL_CLKS_LOCKED BIT(8) -+ -+#define CMN_PLL_POWER_ON_AND_RESET 0x780 -+#define CMN_ANA_EN_SW_RSTN BIT(6) -+ -+#define CMN_PLL_REFCLK_CONFIG 0x784 -+#define CMN_PLL_REFCLK_EXTERNAL BIT(9) -+#define CMN_PLL_REFCLK_DIV GENMASK(8, 4) -+#define CMN_PLL_REFCLK_INDEX GENMASK(3, 0) -+ -+#define CMN_PLL_CTRL 0x78c -+#define CMN_PLL_CTRL_LOCK_DETECT_EN BIT(15) -+ -+#define CMN_PLL_DIVIDER_CTRL 0x794 -+#define CMN_PLL_DIVIDER_CTRL_FACTOR GENMASK(9, 0) -+ -+/** -+ * struct cmn_pll_fixed_output_clk - CMN PLL output clocks information -+ * @id: Clock specifier to be supplied -+ * @name: Clock name to be registered -+ * @rate: Clock rate -+ */ -+struct cmn_pll_fixed_output_clk { -+ unsigned int id; -+ const char *name; -+ unsigned long rate; -+}; -+ -+/** -+ * struct clk_cmn_pll - CMN PLL hardware specific data -+ * @regmap: hardware regmap. -+ * @hw: handle between common and hardware-specific interfaces -+ */ -+struct clk_cmn_pll { -+ struct regmap *regmap; -+ struct clk_hw hw; -+}; -+ -+#define CLK_PLL_OUTPUT(_id, _name, _rate) { \ -+ .id = _id, \ -+ .name = _name, \ -+ .rate = _rate, \ -+} -+ -+#define to_clk_cmn_pll(_hw) container_of(_hw, struct clk_cmn_pll, hw) -+ -+static const struct regmap_config ipq_cmn_pll_regmap_config = { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = 0x7fc, -+ .fast_io = true, -+}; -+ -+static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = { -+ CLK_PLL_OUTPUT(XO_24MHZ_CLK, "xo-24mhz", 24000000UL), -+ CLK_PLL_OUTPUT(SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -+ CLK_PLL_OUTPUT(PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), -+ CLK_PLL_OUTPUT(NSS_1200MHZ_CLK, "nss-1200mhz", 1200000000UL), -+ CLK_PLL_OUTPUT(PPE_353MHZ_CLK, "ppe-353mhz", 353000000UL), -+ CLK_PLL_OUTPUT(ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), -+}; -+ -+/* -+ * CMN PLL has the single parent clock, which supports the several -+ * possible parent clock rates, each parent clock rate is reflected -+ * by the specific reference index value in the hardware. -+ */ -+static int ipq_cmn_pll_find_freq_index(unsigned long parent_rate) -+{ -+ int index = -EINVAL; -+ -+ switch (parent_rate) { -+ case 25000000: -+ index = 3; -+ break; -+ case 31250000: -+ index = 4; -+ break; -+ case 40000000: -+ index = 6; -+ break; -+ case 48000000: -+ case 96000000: -+ /* -+ * Parent clock rate 48 MHZ and 96 MHZ take the same value -+ * of reference clock index. 96 MHZ needs the source clock -+ * divider to be programmed as 2. -+ */ -+ index = 7; -+ break; -+ case 50000000: -+ index = 8; -+ break; -+ default: -+ break; -+ } -+ -+ return index; -+} -+ -+static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); -+ u32 val, factor; -+ -+ /* -+ * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted -+ * by HW according to the parent clock rate. -+ */ -+ regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); -+ factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); -+ -+ return parent_rate * 2 * factor; -+} -+ -+static int clk_cmn_pll_determine_rate(struct clk_hw *hw, -+ struct clk_rate_request *req) -+{ -+ int ret; -+ -+ /* Validate the rate of the single parent clock. */ -+ ret = ipq_cmn_pll_find_freq_index(req->best_parent_rate); -+ -+ return ret < 0 ? ret : 0; -+} -+ -+/* -+ * This function is used to initialize the CMN PLL to enable the fixed -+ * rate output clocks. It is expected to be configured once. -+ */ -+static int clk_cmn_pll_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); -+ int ret, index; -+ u32 val; -+ -+ /* -+ * Configure the reference input clock selection as per the given -+ * parent clock. The output clock rates are always of fixed value. -+ */ -+ index = ipq_cmn_pll_find_freq_index(parent_rate); -+ if (index < 0) -+ return index; -+ -+ ret = regmap_update_bits(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, -+ CMN_PLL_REFCLK_INDEX, -+ FIELD_PREP(CMN_PLL_REFCLK_INDEX, index)); -+ if (ret) -+ return ret; -+ -+ /* -+ * Update the source clock rate selection and source clock -+ * divider as 2 when the parent clock rate is 96 MHZ. -+ */ -+ if (parent_rate == 96000000) { -+ ret = regmap_update_bits(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, -+ CMN_PLL_REFCLK_DIV, -+ FIELD_PREP(CMN_PLL_REFCLK_DIV, 2)); -+ if (ret) -+ return ret; -+ -+ ret = regmap_update_bits(cmn_pll->regmap, CMN_PLL_REFCLK_SRC_SELECTION, -+ CMN_PLL_REFCLK_SRC_DIV, -+ FIELD_PREP(CMN_PLL_REFCLK_SRC_DIV, 0)); -+ if (ret) -+ return ret; -+ } -+ -+ /* Enable PLL locked detect. */ -+ ret = regmap_set_bits(cmn_pll->regmap, CMN_PLL_CTRL, -+ CMN_PLL_CTRL_LOCK_DETECT_EN); -+ if (ret) -+ return ret; -+ -+ /* -+ * Reset the CMN PLL block to ensure the updated configurations -+ * take effect. -+ */ -+ ret = regmap_clear_bits(cmn_pll->regmap, CMN_PLL_POWER_ON_AND_RESET, -+ CMN_ANA_EN_SW_RSTN); -+ if (ret) -+ return ret; -+ -+ usleep_range(1000, 1200); -+ ret = regmap_set_bits(cmn_pll->regmap, CMN_PLL_POWER_ON_AND_RESET, -+ CMN_ANA_EN_SW_RSTN); -+ if (ret) -+ return ret; -+ -+ /* Stability check of CMN PLL output clocks. */ -+ return regmap_read_poll_timeout(cmn_pll->regmap, CMN_PLL_LOCKED, val, -+ (val & CMN_PLL_CLKS_LOCKED), -+ 100, 100 * USEC_PER_MSEC); -+} -+ -+static const struct clk_ops clk_cmn_pll_ops = { -+ .recalc_rate = clk_cmn_pll_recalc_rate, -+ .determine_rate = clk_cmn_pll_determine_rate, -+ .set_rate = clk_cmn_pll_set_rate, -+}; -+ -+static struct clk_hw *ipq_cmn_pll_clk_hw_register(struct platform_device *pdev) -+{ -+ struct clk_parent_data pdata = { .index = 0 }; -+ struct device *dev = &pdev->dev; -+ struct clk_init_data init = {}; -+ struct clk_cmn_pll *cmn_pll; -+ struct regmap *regmap; -+ void __iomem *base; -+ int ret; -+ -+ base = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(base)) -+ return ERR_CAST(base); -+ -+ regmap = devm_regmap_init_mmio(dev, base, &ipq_cmn_pll_regmap_config); -+ if (IS_ERR(regmap)) -+ return ERR_CAST(regmap); -+ -+ cmn_pll = devm_kzalloc(dev, sizeof(*cmn_pll), GFP_KERNEL); -+ if (!cmn_pll) -+ return ERR_PTR(-ENOMEM); -+ -+ init.name = "cmn_pll"; -+ init.parent_data = &pdata; -+ init.num_parents = 1; -+ init.ops = &clk_cmn_pll_ops; -+ -+ cmn_pll->hw.init = &init; -+ cmn_pll->regmap = regmap; -+ -+ ret = devm_clk_hw_register(dev, &cmn_pll->hw); -+ if (ret) -+ return ERR_PTR(ret); -+ -+ return &cmn_pll->hw; -+} -+ -+static int ipq_cmn_pll_register_clks(struct platform_device *pdev) -+{ -+ const struct cmn_pll_fixed_output_clk *fixed_clk; -+ struct clk_hw_onecell_data *hw_data; -+ struct device *dev = &pdev->dev; -+ struct clk_hw *cmn_pll_hw; -+ unsigned int num_clks; -+ struct clk_hw *hw; -+ int ret, i; -+ -+ fixed_clk = ipq9574_output_clks; -+ num_clks = ARRAY_SIZE(ipq9574_output_clks); -+ -+ hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks + 1), -+ GFP_KERNEL); -+ if (!hw_data) -+ return -ENOMEM; -+ -+ /* -+ * Register the CMN PLL clock, which is the parent clock of -+ * the fixed rate output clocks. -+ */ -+ cmn_pll_hw = ipq_cmn_pll_clk_hw_register(pdev); -+ if (IS_ERR(cmn_pll_hw)) -+ return PTR_ERR(cmn_pll_hw); -+ -+ /* Register the fixed rate output clocks. */ -+ for (i = 0; i < num_clks; i++) { -+ hw = clk_hw_register_fixed_rate_parent_hw(dev, fixed_clk[i].name, -+ cmn_pll_hw, 0, -+ fixed_clk[i].rate); -+ if (IS_ERR(hw)) { -+ ret = PTR_ERR(hw); -+ goto unregister_fixed_clk; -+ } -+ -+ hw_data->hws[fixed_clk[i].id] = hw; -+ } -+ -+ /* -+ * Provide the CMN PLL clock. The clock rate of CMN PLL -+ * is configured to 12 GHZ by DT property assigned-clock-rates-u64. -+ */ -+ hw_data->hws[CMN_PLL_CLK] = cmn_pll_hw; -+ hw_data->num = num_clks + 1; -+ -+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data); -+ if (ret) -+ goto unregister_fixed_clk; -+ -+ platform_set_drvdata(pdev, hw_data); -+ -+ return 0; -+ -+unregister_fixed_clk: -+ while (i > 0) -+ clk_hw_unregister(hw_data->hws[fixed_clk[--i].id]); -+ -+ return ret; -+} -+ -+static int ipq_cmn_pll_clk_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ int ret; -+ -+ ret = devm_pm_runtime_enable(dev); -+ if (ret) -+ return ret; -+ -+ ret = devm_pm_clk_create(dev); -+ if (ret) -+ return ret; -+ -+ /* -+ * To access the CMN PLL registers, the GCC AHB & SYS clocks -+ * of CMN PLL block need to be enabled. -+ */ -+ ret = pm_clk_add(dev, "ahb"); -+ if (ret) -+ return dev_err_probe(dev, ret, "Fail to add AHB clock\n"); -+ -+ ret = pm_clk_add(dev, "sys"); -+ if (ret) -+ return dev_err_probe(dev, ret, "Fail to add SYS clock\n"); -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret) -+ return ret; -+ -+ /* Register CMN PLL clock and fixed rate output clocks. */ -+ ret = ipq_cmn_pll_register_clks(pdev); -+ pm_runtime_put(dev); -+ if (ret) -+ return dev_err_probe(dev, ret, -+ "Fail to register CMN PLL clocks\n"); -+ -+ return 0; -+} -+ -+static void ipq_cmn_pll_clk_remove(struct platform_device *pdev) -+{ -+ struct clk_hw_onecell_data *hw_data = platform_get_drvdata(pdev); -+ int i; -+ -+ /* -+ * The clock with index CMN_PLL_CLK is unregistered by -+ * device management. -+ */ -+ for (i = 0; i < hw_data->num; i++) { -+ if (i != CMN_PLL_CLK) -+ clk_hw_unregister(hw_data->hws[i]); -+ } -+} -+ -+static const struct dev_pm_ops ipq_cmn_pll_pm_ops = { -+ SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) -+}; -+ -+static const struct of_device_id ipq_cmn_pll_clk_ids[] = { -+ { .compatible = "qcom,ipq9574-cmn-pll", }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, ipq_cmn_pll_clk_ids); -+ -+static struct platform_driver ipq_cmn_pll_clk_driver = { -+ .probe = ipq_cmn_pll_clk_probe, -+ .remove = ipq_cmn_pll_clk_remove, -+ .driver = { -+ .name = "ipq_cmn_pll", -+ .of_match_table = ipq_cmn_pll_clk_ids, -+ .pm = &ipq_cmn_pll_pm_ops, -+ }, -+}; -+module_platform_driver(ipq_cmn_pll_clk_driver); -+ -+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ CMN PLL Driver"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/qualcommax/patches-6.12/0065-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5424-SoC.patch b/target/linux/qualcommax/patches-6.12/0065-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5424-SoC.patch deleted file mode 100644 index b3209630368..00000000000 --- a/target/linux/qualcommax/patches-6.12/0065-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5424-SoC.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0c25ae62f5dc6a438b563536b5fe7fb6da3612b8 Mon Sep 17 00:00:00 2001 -From: Luo Jie -Date: Tue, 10 Jun 2025 18:35:18 +0800 -Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5424 SoC - -The CMN PLL block in the IPQ5424 SoC takes 48 MHZ as the reference -input clock. The output clocks are the same as IPQ9574 SoC, except -for the clock rate of output clocks to PPE and NSS. - -Also, add the new header file to export the CMN PLL output clock -specifiers for IPQ5424 SoC. - -Acked-by: Rob Herring (Arm) -Signed-off-by: Luo Jie -Link: https://lore.kernel.org/r/20250610-qcom_ipq5424_cmnpll-v3-1-ceada8165645@quicinc.com -Signed-off-by: Bjorn Andersson ---- - .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + - .../dt-bindings/clock/qcom,ipq5424-cmn-pll.h | 22 +++++++++++++++++++ - 2 files changed, 23 insertions(+) - create mode 100644 include/dt-bindings/clock/qcom,ipq5424-cmn-pll.h - ---- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -@@ -24,6 +24,7 @@ description: - properties: - compatible: - enum: -+ - qcom,ipq5424-cmn-pll - - qcom,ipq9574-cmn-pll - - reg: ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq5424-cmn-pll.h -@@ -0,0 +1,22 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ5424_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ5424_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define IPQ5424_CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ5424. */ -+#define IPQ5424_XO_24MHZ_CLK 1 -+#define IPQ5424_SLEEP_32KHZ_CLK 2 -+#define IPQ5424_PCS_31P25MHZ_CLK 3 -+#define IPQ5424_NSS_300MHZ_CLK 4 -+#define IPQ5424_PPE_375MHZ_CLK 5 -+#define IPQ5424_ETH0_50MHZ_CLK 6 -+#define IPQ5424_ETH1_50MHZ_CLK 7 -+#define IPQ5424_ETH2_50MHZ_CLK 8 -+#define IPQ5424_ETH_25MHZ_CLK 9 -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0066-v6.17-clk-qcom-cmnpll-Add-IPQ5424-SoC-support.patch b/target/linux/qualcommax/patches-6.12/0066-v6.17-clk-qcom-cmnpll-Add-IPQ5424-SoC-support.patch deleted file mode 100644 index d13e0b1fd55..00000000000 --- a/target/linux/qualcommax/patches-6.12/0066-v6.17-clk-qcom-cmnpll-Add-IPQ5424-SoC-support.patch +++ /dev/null @@ -1,104 +0,0 @@ -From a2afa4c33f0a7f7f70d54a1bc5110e326753f982 Mon Sep 17 00:00:00 2001 -From: Luo Jie -Date: Tue, 10 Jun 2025 18:35:19 +0800 -Subject: clk: qcom: cmnpll: Add IPQ5424 SoC support - -The CMN PLL in IPQ5424 SoC supplies the fixed clock to NSS at 300 MHZ -and to PPE at 375 MHZ. Other output clocks from CMN PLL on this SoC, -and their rates are same as IPQ9574. - -Acked-by: Stephen Boyd -Signed-off-by: Luo Jie -Link: https://lore.kernel.org/r/20250610-qcom_ipq5424_cmnpll-v3-2-ceada8165645@quicinc.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/ipq-cmn-pll.c | 35 ++++++++++++++++++++++++++++++----- - 1 file changed, 30 insertions(+), 5 deletions(-) - -(limited to 'drivers/clk/qcom/ipq-cmn-pll.c') - ---- a/drivers/clk/qcom/ipq-cmn-pll.c -+++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0-only - /* -- * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. - */ - - /* -@@ -16,6 +16,10 @@ - * are supplied to GCC (24 MHZ as XO and 32 KHZ as sleep clock), and to PCS - * with 31.25 MHZ. - * -+ * On the IPQ5424 SoC, there is an output clock from CMN PLL to PPE at 375 MHZ, -+ * and an output clock to NSS (network subsystem) at 300 MHZ. The other output -+ * clocks from CMN PLL on IPQ5424 are the same as IPQ9574. -+ * - * +---------+ - * | GCC | - * +--+---+--+ -@@ -46,6 +50,7 @@ - #include - - #include -+#include - - #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 - #define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) -@@ -115,6 +120,20 @@ static const struct cmn_pll_fixed_output - CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), - CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), - CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), -+ { /* Sentinel */ } -+}; -+ -+static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { -+ CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), -+ CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -+ CLK_PLL_OUTPUT(IPQ5424_PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), -+ CLK_PLL_OUTPUT(IPQ5424_NSS_300MHZ_CLK, "nss-300mhz", 300000000UL), -+ CLK_PLL_OUTPUT(IPQ5424_PPE_375MHZ_CLK, "ppe-375mhz", 375000000UL), -+ CLK_PLL_OUTPUT(IPQ5424_ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(IPQ5424_ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(IPQ5424_ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(IPQ5424_ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), -+ { /* Sentinel */ } - }; - - /* -@@ -297,7 +316,7 @@ static struct clk_hw *ipq_cmn_pll_clk_hw - - static int ipq_cmn_pll_register_clks(struct platform_device *pdev) - { -- const struct cmn_pll_fixed_output_clk *fixed_clk; -+ const struct cmn_pll_fixed_output_clk *p, *fixed_clk; - struct clk_hw_onecell_data *hw_data; - struct device *dev = &pdev->dev; - struct clk_hw *cmn_pll_hw; -@@ -305,8 +324,13 @@ static int ipq_cmn_pll_register_clks(str - struct clk_hw *hw; - int ret, i; - -- fixed_clk = ipq9574_output_clks; -- num_clks = ARRAY_SIZE(ipq9574_output_clks); -+ fixed_clk = device_get_match_data(dev); -+ if (!fixed_clk) -+ return -EINVAL; -+ -+ num_clks = 0; -+ for (p = fixed_clk; p->name; p++) -+ num_clks++; - - hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, num_clks + 1), - GFP_KERNEL); -@@ -415,7 +439,8 @@ static const struct dev_pm_ops ipq_cmn_p - }; - - static const struct of_device_id ipq_cmn_pll_clk_ids[] = { -- { .compatible = "qcom,ipq9574-cmn-pll", }, -+ { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, -+ { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, - { } - }; - MODULE_DEVICE_TABLE(of, ipq_cmn_pll_clk_ids); diff --git a/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch b/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch deleted file mode 100644 index e2e3f74fd46..00000000000 --- a/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 314b903c30040632db7edd187cd33003b2aee512 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Fri, 16 May 2025 16:36:09 +0400 -Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5018 SoC - -The CMN PLL block in the IPQ5018 SoC takes 96 MHZ as the reference -input clock. Its output clocks are the XO (24Mhz), sleep (32Khz), and -ethernet (50Mhz) clocks. - -Reviewed-by: Rob Herring (Arm) -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250516-ipq5018-cmn-pll-v4-2-389a6b30e504@outlook.com -Signed-off-by: Bjorn Andersson ---- - .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + - include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h | 16 ++++++++++++++++ - 2 files changed, 17 insertions(+) - create mode 100644 include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h - ---- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -@@ -24,6 +24,7 @@ description: - properties: - compatible: - enum: -+ - qcom,ipq5018-cmn-pll - - qcom,ipq5424-cmn-pll - - qcom,ipq9574-cmn-pll - ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ5018_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ5018_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define IPQ5018_CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ5018. */ -+#define IPQ5018_XO_24MHZ_CLK 1 -+#define IPQ5018_SLEEP_32KHZ_CLK 2 -+#define IPQ5018_ETH_50MHZ_CLK 3 -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0068-v6.17-clk-qcom-ipq-cmn-pll-Add-IPQ5018-SoC-support.patch b/target/linux/qualcommax/patches-6.12/0068-v6.17-clk-qcom-ipq-cmn-pll-Add-IPQ5018-SoC-support.patch deleted file mode 100644 index d113c2d6c05..00000000000 --- a/target/linux/qualcommax/patches-6.12/0068-v6.17-clk-qcom-ipq-cmn-pll-Add-IPQ5018-SoC-support.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 25d12630561d8d0906f1f5eceb055da3af67c8c9 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Fri, 16 May 2025 16:36:10 +0400 -Subject: clk: qcom: ipq-cmn-pll: Add IPQ5018 SoC support - -The CMN PLL in IPQ5018 SoC supplies fixed clocks to XO, sleep, and the -ethernet block. - -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250516-ipq5018-cmn-pll-v4-3-389a6b30e504@outlook.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/ipq-cmn-pll.c | 37 +++++++++++++++++++++++-------------- - 1 file changed, 23 insertions(+), 14 deletions(-) - -(limited to 'drivers/clk/qcom/ipq-cmn-pll.c') - ---- a/drivers/clk/qcom/ipq-cmn-pll.c -+++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -50,6 +50,7 @@ - #include - - #include -+#include - #include - - #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 -@@ -110,16 +111,10 @@ static const struct regmap_config ipq_cm - .fast_io = true, - }; - --static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = { -- CLK_PLL_OUTPUT(XO_24MHZ_CLK, "xo-24mhz", 24000000UL), -- CLK_PLL_OUTPUT(SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -- CLK_PLL_OUTPUT(PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), -- CLK_PLL_OUTPUT(NSS_1200MHZ_CLK, "nss-1200mhz", 1200000000UL), -- CLK_PLL_OUTPUT(PPE_353MHZ_CLK, "ppe-353mhz", 353000000UL), -- CLK_PLL_OUTPUT(ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL), -- CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), -- CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), -- CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), -+static const struct cmn_pll_fixed_output_clk ipq5018_output_clks[] = { -+ CLK_PLL_OUTPUT(IPQ5018_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), -+ CLK_PLL_OUTPUT(IPQ5018_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -+ CLK_PLL_OUTPUT(IPQ5018_ETH_50MHZ_CLK, "eth-50mhz", 50000000UL), - { /* Sentinel */ } - }; - -@@ -136,6 +131,19 @@ static const struct cmn_pll_fixed_output - { /* Sentinel */ } - }; - -+static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = { -+ CLK_PLL_OUTPUT(XO_24MHZ_CLK, "xo-24mhz", 24000000UL), -+ CLK_PLL_OUTPUT(SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -+ CLK_PLL_OUTPUT(PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), -+ CLK_PLL_OUTPUT(NSS_1200MHZ_CLK, "nss-1200mhz", 1200000000UL), -+ CLK_PLL_OUTPUT(PPE_353MHZ_CLK, "ppe-353mhz", 353000000UL), -+ CLK_PLL_OUTPUT(ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), -+ CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), -+ { /* Sentinel */ } -+}; -+ - /* - * CMN PLL has the single parent clock, which supports the several - * possible parent clock rates, each parent clock rate is reflected -@@ -399,11 +407,11 @@ static int ipq_cmn_pll_clk_probe(struct - */ - ret = pm_clk_add(dev, "ahb"); - if (ret) -- return dev_err_probe(dev, ret, "Fail to add AHB clock\n"); -+ return dev_err_probe(dev, ret, "Failed to add AHB clock\n"); - - ret = pm_clk_add(dev, "sys"); - if (ret) -- return dev_err_probe(dev, ret, "Fail to add SYS clock\n"); -+ return dev_err_probe(dev, ret, "Failed to add SYS clock\n"); - - ret = pm_runtime_resume_and_get(dev); - if (ret) -@@ -414,7 +422,7 @@ static int ipq_cmn_pll_clk_probe(struct - pm_runtime_put(dev); - if (ret) - return dev_err_probe(dev, ret, -- "Fail to register CMN PLL clocks\n"); -+ "Failed to register CMN PLL clocks\n"); - - return 0; - } -@@ -439,8 +447,9 @@ static const struct dev_pm_ops ipq_cmn_p - }; - - static const struct of_device_id ipq_cmn_pll_clk_ids[] = { -- { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, -+ { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, - { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, -+ { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, - { } - }; - MODULE_DEVICE_TABLE(of, ipq_cmn_pll_clk_ids); diff --git a/target/linux/qualcommax/patches-6.12/0069-v6.18-arm64-dts-ipq5018-Add-CMN-PLL-node.patch b/target/linux/qualcommax/patches-6.12/0069-v6.18-arm64-dts-ipq5018-Add-CMN-PLL-node.patch deleted file mode 100644 index 83d6714153d..00000000000 --- a/target/linux/qualcommax/patches-6.12/0069-v6.18-arm64-dts-ipq5018-Add-CMN-PLL-node.patch +++ /dev/null @@ -1,89 +0,0 @@ -From c006b249c54441dd8a3a493c7c87158f441f8178 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 21 Jul 2025 10:04:35 +0400 -Subject: arm64: dts: ipq5018: Add CMN PLL node - -Add CMN PLL node for enabling output clocks to the networking -hardware blocks on IPQ5018 devices. - -The reference clock of CMN PLL is routed from XO to the CMN PLL -through the internal WiFi block. -.XO (48 MHZ) --> WiFi (multiplier/divider)--> 96 MHZ to CMN PLL. - -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250721-ipq5018-cmn-pll-v5-1-4cbf3479af65@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 33 +++++++++++++++++++++++++++++++-- - 1 file changed, 31 insertions(+), 2 deletions(-) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -2,12 +2,13 @@ - /* - * IPQ5018 SoC device tree source - * -- * Copyright (c) 2023 The Linux Foundation. All rights reserved. -+ * Copyright (c) 2023-2025 The Linux Foundation. All rights reserved. - */ - - #include --#include - #include -+#include -+#include - #include - #include - -@@ -29,6 +30,14 @@ - #clock-cells = <0>; - }; - -+ ref_96mhz_clk: ref-96mhz-clk { -+ compatible = "fixed-factor-clock"; -+ clocks = <&xo_clk>; -+ #clock-cells = <0>; -+ clock-div = <1>; -+ clock-mult = <2>; -+ }; -+ - sleep_clk: sleep-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; -@@ -38,6 +47,12 @@ - compatible = "fixed-clock"; - #clock-cells = <0>; - }; -+ -+ xo_clk: xo-clk { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <48000000>; -+ }; - }; - - cpus { -@@ -229,6 +244,20 @@ - status = "disabled"; - }; - -+ cmn_pll: clock-controller@9b000 { -+ compatible = "qcom,ipq5018-cmn-pll"; -+ reg = <0x0009b000 0x800>; -+ clocks = <&ref_96mhz_clk>, -+ <&gcc GCC_CMN_BLK_AHB_CLK>, -+ <&gcc GCC_CMN_BLK_SYS_CLK>; -+ clock-names = "ref", -+ "ahb", -+ "sys"; -+ #clock-cells = <1>; -+ assigned-clocks = <&cmn_pll IPQ5018_CMN_PLL_CLK>; -+ assigned-clock-rates-u64 = /bits/ 64 <9600000000>; -+ }; -+ - qfprom: qfprom@a0000 { - compatible = "qcom,ipq5018-qfprom", "qcom,qfprom"; - reg = <0x000a0000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.12/0070-v6.18-arm64-dts-qcom-Update-IPQ5018-xo_board_clk-to-use-fixed-factor-clock.patch b/target/linux/qualcommax/patches-6.12/0070-v6.18-arm64-dts-qcom-Update-IPQ5018-xo_board_clk-to-use-fixed-factor-clock.patch deleted file mode 100644 index 20c184d2626..00000000000 --- a/target/linux/qualcommax/patches-6.12/0070-v6.18-arm64-dts-qcom-Update-IPQ5018-xo_board_clk-to-use-fixed-factor-clock.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 5ca3d42384a66bcb66f91d75da16ec9e9f053aab Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 21 Jul 2025 10:04:36 +0400 -Subject: arm64: dts: qcom: Update IPQ5018 xo_board_clk to use fixed factor - clock - -The xo_board_clk is fixed to 24 MHZ, which is routed from WiFi output -clock 96 MHZ (also being the reference clock of CMN PLL) divided by 4 -to the analog block routing channel. Update the xo_board_clk nodes in -the board DTS files to use clock-div/clock-mult accordingly. - -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250721-ipq5018-cmn-pll-v5-2-4cbf3479af65@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -44,7 +44,8 @@ - }; - - xo_board_clk: xo-board-clk { -- compatible = "fixed-clock"; -+ compatible = "fixed-factor-clock"; -+ clocks = <&ref_96mhz_clk>; - #clock-cells = <0>; - }; - diff --git a/target/linux/qualcommax/patches-6.12/0071-v6.18-arm64-dts-qcom-ipq5018-add-QUP3-I2C-node.patch b/target/linux/qualcommax/patches-6.12/0071-v6.18-arm64-dts-qcom-ipq5018-add-QUP3-I2C-node.patch deleted file mode 100644 index abf0cab5b55..00000000000 --- a/target/linux/qualcommax/patches-6.12/0071-v6.18-arm64-dts-qcom-ipq5018-add-QUP3-I2C-node.patch +++ /dev/null @@ -1,120 +0,0 @@ -From patchwork Mon Sep 15 09:24:18 2025 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: George Moussalem -X-Patchwork-Id: 14231524 -Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org - [10.30.226.201]) - (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) - (No client certificate requested) - by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4305E2F49FD; - Mon, 15 Sep 2025 09:24:37 +0000 (UTC) -Authentication-Results: smtp.subspace.kernel.org; - arc=none smtp.client-ip=10.30.226.201 -ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; - t=1757928277; cv=none; - b=W4yjWHon01Sh2gBzP90ni3A/PWbNfjtSydk9zppsDHx/BPbANANjeMu4rhkRDwshIs4GuAEKBYZmGwGg0SnK/9N3ricPefdvF3PMI1VApzNpkksjRBlVeyccUbjx/+Y2/Oe/8cA1Oz4jQtocecxzE3CGh9+Kk9vqgpHa635Ky4M= -ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; - s=arc-20240116; t=1757928277; c=relaxed/simple; - bh=RgkgrwRypdT/518CgEj/V8LfEbAO8YRYx2xOm90hzPk=; - h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; - b=YM1Wxrnb67eDy0s8cezNXKJuM/Asp9L23eC7xAHqaosCV6mlayQFLEkibiqPvm1MRaNc/e2kOWU2I2cQMi88qUSuzlgK/veTaDHx5oePOCR/dZrkkC/bciWsT6CSiTbAbE9WOK2cuxGHzk2PQxCJaBBtFxN3Lg+Kar6d1TWv3Rw= -ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b=XbapCBXD; arc=none smtp.client-ip=10.30.226.201 -Authentication-Results: smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b="XbapCBXD" -Received: by smtp.kernel.org (Postfix) with ESMTPS id D1D1EC4CEF1; - Mon, 15 Sep 2025 09:24:36 +0000 (UTC) -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; - s=k20201202; t=1757928276; - bh=RgkgrwRypdT/518CgEj/V8LfEbAO8YRYx2xOm90hzPk=; - h=From:Date:Subject:To:Cc:Reply-To:From; - b=XbapCBXDF3N0JCGizjXEEnzD31MMXQC03i/+s2l2R0wZ8miOI4LMi5Gg4yH2GlKd+ - Q6p/IdLWuIk3O4m0FpZ5lI2pKF+5siZCyzECvT+Y9LHHb6fMbXp/FNJbrOak3mz8xy - PVdw03eFO4Wj5bF3mCqyGd6HKakkfAR6ZDU9B05omFHE5FzakQMTWdHqjgi51gFOm9 - EMzWFnvH1U1qqsNEicMESHpGGGhWu6VHZugCgkGnOZtnELneHxFgJLjJbYkjfZNBUZ - endu4HugvKFXwjcJFtv84yplTu3poxGqmKcC4DtURMjGwFa72IZgv6lJ1c/MXLajAx - 7/Y8KfWSDjnpQ== -Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org - (localhost.localdomain [127.0.0.1]) - by smtp.lore.kernel.org (Postfix) with ESMTP id C31A1CAC598; - Mon, 15 Sep 2025 09:24:36 +0000 (UTC) -Date: Mon, 15 Sep 2025 13:24:18 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: add QUP3 I2C node -Precedence: bulk -X-Mailing-List: linux-arm-msm@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -Message-Id: <20250915-ipq5018-i2c-v1-1-46bbf27396d6@outlook.com> -X-B4-Tracking: v=1; b=H4sIAEHbx2gC/x3MQQqAIBBA0avIrBNmBMu6SrSQGms2ZQoRiHdPW - r7F/wUyJ+EMkyqQ+JEs19lAnYL18OfOWrZmMGgsjkRa4m2RnBazavQYPA29ceSgFTFxkPe/zUu - tH1sLzpNdAAAA -X-Change-ID: 20250911-ipq5018-i2c-0a0fa1762818 -To: Bjorn Andersson , - Konrad Dybcio , Rob Herring , - Krzysztof Kozlowski , - Conor Dooley -Cc: linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, - linux-kernel@vger.kernel.org, - Vandhiadevan Karunamoorthy , - George Moussalem -X-Mailer: b4 0.14.2 -X-Developer-Signature: v=1; a=ed25519-sha256; t=1757928274; l=1420; - i=george.moussalem@outlook.com; s=20250321; h=from:subject:message-id; - bh=5AMg7/oGtHNBWL+11z9Q/HGGQ9F6ll9nuoBPaZhI9aM=; - b=YRzjCtxRPYCTIZbfjE7Ch/iQgVk2FPQ2D3Z9XheAftOihKy/1/tmjGVVJNjCCP6dXt51fm4rq - wtSUjXqnECrAN3FPi6I7N+IBTc6A+E6Q/wpYazdNwaMdyxGBj0tyK4f -X-Developer-Key: i=george.moussalem@outlook.com; a=ed25519; - pk=/PuRTSI9iYiHwcc6Nrde8qF4ZDhJBlUgpHdhsIjnqIk= -X-Endpoint-Received: by B4 Relay for george.moussalem@outlook.com/20250321 - with auth_id=364 -X-Original-From: George Moussalem -Reply-To: george.moussalem@outlook.com -From: George Moussalem - -From: Vandhiadevan Karunamoorthy - -Add node to support I2C bus inside of IPQ5018. - -Signed-off-by: Vandhiadevan Karunamoorthy -Signed-off-by: George Moussalem ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - - ---- -base-commit: b0971d2008c644b9064d968d440fb9f44606d90c -change-id: 20250911-ipq5018-i2c-0a0fa1762818 - -Best regards, - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -504,6 +504,21 @@ - status = "disabled"; - }; - -+ blsp1_i2c3: i2c@78b7000 { -+ compatible = "qcom,i2c-qup-v2.2.1"; -+ reg = <0x078b7000 0x600>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ interrupts = ; -+ clocks = <&gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>, -+ <&gcc GCC_BLSP1_AHB_CLK>; -+ clock-names = "core", "iface"; -+ clock-frequency = <400000>; -+ dmas = <&blsp_dma 9>, <&blsp_dma 8>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ - qpic_bam: dma-controller@7984000 { - compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0"; - reg = <0x07984000 0x1c000>; diff --git a/target/linux/qualcommax/patches-6.12/0072-v6.18-arm64-dts-qcom-ipq5018-add-QUP1-UART2-node.patch b/target/linux/qualcommax/patches-6.12/0072-v6.18-arm64-dts-qcom-ipq5018-add-QUP1-UART2-node.patch deleted file mode 100644 index e27807583e9..00000000000 --- a/target/linux/qualcommax/patches-6.12/0072-v6.18-arm64-dts-qcom-ipq5018-add-QUP1-UART2-node.patch +++ /dev/null @@ -1,36 +0,0 @@ -From b410d25fb349bc32132749bd2cb17aa17054287d Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -Date: Wed, 17 Sep 2025 15:49:00 +0400 -Subject: arm64: dts: qcom: ipq5018: add QUP1 UART2 node - -Add node to support the second UART node controller in IPQ5018. - -Signed-off-by: Manikanta Mylavarapu -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250917-ipq5018-uart2-v1-1-f8680bbf947f@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -490,6 +490,16 @@ - status = "disabled"; - }; - -+ blsp1_uart2: serial@78b0000 { -+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; -+ reg = <0x078b0000 0x200>; -+ interrupts = ; -+ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, -+ <&gcc GCC_BLSP1_AHB_CLK>; -+ clock-names = "core", "iface"; -+ status = "disabled"; -+ }; -+ - blsp1_spi1: spi@78b5000 { - compatible = "qcom,spi-qup-v2.2.1"; - #address-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.12/0073-v7.0-arm64-dts-qcom-ipq5018-Remove-tsens-v1-fallback-compatible.patch b/target/linux/qualcommax/patches-6.12/0073-v7.0-arm64-dts-qcom-ipq5018-Remove-tsens-v1-fallback-compatible.patch deleted file mode 100644 index 5b66e46efaa..00000000000 --- a/target/linux/qualcommax/patches-6.12/0073-v7.0-arm64-dts-qcom-ipq5018-Remove-tsens-v1-fallback-compatible.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 60fb18fbc234efb2d59472a889282dba2df3402a Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 18 Aug 2025 15:33:47 +0400 -Subject: arm64: dts: qcom: ipq5018: Remove tsens v1 fallback compatible - -Remove qcom,tsens-v1 as fallback compatible since this IP has no RPM -and, as such, must use its own init routine available in the driver. - -Reviewed-by: Dmitry Baryshkov -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20250818-ipq5018-tsens-fix-v1-2-0f08cf09182d@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -340,7 +340,7 @@ - }; - - tsens: thermal-sensor@4a9000 { -- compatible = "qcom,ipq5018-tsens", "qcom,tsens-v1"; -+ compatible = "qcom,ipq5018-tsens"; - reg = <0x004a9000 0x1000>, - <0x004a8000 0x1000>; - diff --git a/target/linux/qualcommax/patches-6.12/0075-v7.0-arm64-dts-qcom-ipq5018-Correct-USB-DWC3-wrapper-interrupts.patch b/target/linux/qualcommax/patches-6.12/0075-v7.0-arm64-dts-qcom-ipq5018-Correct-USB-DWC3-wrapper-interrupts.patch deleted file mode 100644 index ed5faef6509..00000000000 --- a/target/linux/qualcommax/patches-6.12/0075-v7.0-arm64-dts-qcom-ipq5018-Correct-USB-DWC3-wrapper-interrupts.patch +++ /dev/null @@ -1,48 +0,0 @@ -From d106f9681d1952b7c19726b0713cee6ae11211f5 Mon Sep 17 00:00:00 2001 -From: Krzysztof Kozlowski -Date: Tue, 6 Jan 2026 19:51:24 +0100 -Subject: arm64: dts: qcom: ipq5018: Correct USB DWC3 wrapper interrupts - -Interrupts for DWC3 node were completely mixed up - SPI interrupt 62 is -not listed in reference manual at all. It was also causing dtbs_check -warnings: - - ipq5018-rdp432-c2.dtb: usb@8af8800 (qcom,ipq5018-dwc3): interrupt-names:0: 'pwr_event' was expected - ipq5018-rdp432-c2.dtb: usb@8af8800 (qcom,ipq5018-dwc3): interrupt-names: ['hs_phy_irq'] is too short - -Warning itself was introduced by commit 53c6d854be4e ("dt-bindings: usb: -dwc3: Clean up hs_phy_irq in binding"), but this was trying to bring -sanity to the interrupts overall, although did a mistake for IPQ5018. -IPQ5018 does not have QUSB2 PHY and its interrupts should rather match -ones used in IPQ5332. - -Correct it by using interrupts matching the bindings and reference -manual. - -Signed-off-by: Krzysztof Kozlowski -Tested-by: George Moussalem -Link: https://lore.kernel.org/r/20260106185123.19929-3-krzysztof.kozlowski@oss.qualcomm.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -571,8 +571,12 @@ - compatible = "qcom,ipq5018-dwc3", "qcom,dwc3"; - reg = <0x08af8800 0x400>; - -- interrupts = ; -- interrupt-names = "hs_phy_irq"; -+ interrupts = , -+ , -+ ; -+ interrupt-names = "pwr_event", -+ "dp_hs_phy_irq", -+ "dm_hs_phy_irq"; - - clocks = <&gcc GCC_USB0_MASTER_CLK>, - <&gcc GCC_SYS_NOC_USB0_AXI_CLK>, diff --git a/target/linux/qualcommax/patches-6.12/0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch b/target/linux/qualcommax/patches-6.12/0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch deleted file mode 100644 index cdf97724107..00000000000 --- a/target/linux/qualcommax/patches-6.12/0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch +++ /dev/null @@ -1,74 +0,0 @@ -From a8cd1a1baadbfed43ac214f0bf3e26c90e3b521e Mon Sep 17 00:00:00 2001 -From: Devi Priya -Date: Mon, 6 Apr 2026 22:24:38 +0200 -Subject: [PATCH] dt-bindings: pwm: add IPQ6018 binding - -DT binding for the PWM block in Qualcomm IPQ6018 SoC. - -Reviewed-by: Bjorn Andersson -Reviewed-by: Krzysztof Kozlowski -Co-developed-by: Baruch Siach -Signed-off-by: Baruch Siach -Signed-off-by: Devi Priya -Signed-off-by: George Moussalem -Link: https://patch.msgid.link/20260406-ipq-pwm-v21-1-6ed1e868e4c2@outlook.com -Signed-off-by: Rob Herring (Arm) ---- - .../bindings/pwm/qcom,ipq6018-pwm.yaml | 51 +++++++++++++++++++ - 1 file changed, 51 insertions(+) - create mode 100644 Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml -@@ -0,0 +1,51 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/pwm/qcom,ipq6018-pwm.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Qualcomm IPQ6018 PWM controller -+ -+maintainers: -+ - George Moussalem -+ -+properties: -+ compatible: -+ oneOf: -+ - items: -+ - enum: -+ - qcom,ipq5018-pwm -+ - qcom,ipq5332-pwm -+ - qcom,ipq9574-pwm -+ - const: qcom,ipq6018-pwm -+ - const: qcom,ipq6018-pwm -+ -+ reg: -+ maxItems: 1 -+ -+ clocks: -+ maxItems: 1 -+ -+ "#pwm-cells": -+ const: 3 -+ -+required: -+ - compatible -+ - reg -+ - clocks -+ - "#pwm-cells" -+ -+additionalProperties: false -+ -+examples: -+ - | -+ #include -+ -+ pwm: pwm@1941010 { -+ compatible = "qcom,ipq6018-pwm"; -+ reg = <0x01941010 0x20>; -+ clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clock-rates = <100000000>; -+ #pwm-cells = <3>; -+ }; diff --git a/target/linux/qualcommax/patches-6.12/0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch b/target/linux/qualcommax/patches-6.12/0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch deleted file mode 100644 index ba0a65fcbe6..00000000000 --- a/target/linux/qualcommax/patches-6.12/0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch +++ /dev/null @@ -1,326 +0,0 @@ -From c436e3e9c265a1f012008ef5da6fd28863f8025c Mon Sep 17 00:00:00 2001 -From: Devi Priya -Date: Mon, 6 Apr 2026 22:24:39 +0200 -Subject: [PATCH] pwm: Driver for qualcomm ipq6018 pwm block -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Driver for the PWM block in Qualcomm IPQ6018 line of SoCs. Based on -driver from downstream Codeaurora kernel tree. Removed support for older -(V1) variants because I have no access to that hardware. - -Tested on IPQ5018 and IPQ6010 based hardware. - -Co-developed-by: Baruch Siach -Signed-off-by: Baruch Siach -Signed-off-by: Devi Priya -Reviewed-by: Bjorn Andersson -Signed-off-by: George Moussalem -Link: https://patch.msgid.link/20260406-ipq-pwm-v21-2-6ed1e868e4c2@outlook.com -[ukleinek: Fixed a few nitpicks as agreed on the mailing list] -Signed-off-by: Uwe Kleine-König ---- - drivers/pwm/Kconfig | 12 ++ - drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-ipq.c | 263 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 276 insertions(+) - create mode 100644 drivers/pwm/pwm-ipq.c - ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -319,6 +319,18 @@ config PWM_INTEL_LGM - To compile this driver as a module, choose M here: the module - will be called pwm-intel-lgm. - -+config PWM_IPQ -+ tristate "IPQ PWM support" -+ depends on ARCH_QCOM || COMPILE_TEST -+ depends on HAVE_CLK && HAS_IOMEM -+ help -+ Generic PWM framework driver for IPQ PWM block which supports -+ 4 pwm channels. Each of the these channels can be configured -+ independent of each other. -+ -+ To compile this driver as a module, choose M here: the module -+ will be called pwm-ipq. -+ - config PWM_IQS620A - tristate "Azoteq IQS620A PWM support" - depends on MFD_IQS62X || COMPILE_TEST ---- a/drivers/pwm/Makefile -+++ b/drivers/pwm/Makefile -@@ -27,6 +27,7 @@ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o - obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o - obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o - obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o -+obj-$(CONFIG_PWM_IPQ) += pwm-ipq.o - obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o - obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o - obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o ---- /dev/null -+++ b/drivers/pwm/pwm-ipq.c -@@ -0,0 +1,263 @@ -+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 -+/* -+ * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. -+ * -+ * Limitations: -+ * - The PWM controller has no publicly available datasheet. -+ * - Each of the four channels is programmed via two 32-bit registers -+ * (REG0 and REG1 at 8-byte stride). -+ * - Period and duty-cycle reconfiguration is fully atomic: new divider, -+ * pre-divider, and high-duration values are latched by setting the -+ * UPDATE bit (bit 30 in REG1). The hardware applies the new settings -+ * at the beginning of the next period without disabling the output, -+ * so the currently running period is always completed. -+ * - On disable (clearing the ENABLE bit 31 in REG1), the hardware -+ * finishes the current period before stopping the output. The pin -+ * is then driven to the inactive (low) level. -+ * - Upon disabling, the hardware resets the pre-divider (PRE_DIV) and divider -+ * fields (PWM_DIV) in REG0 and REG1 to 0x0000 and 0x0001 respectively. -+ * - Only normal polarity is supported. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* The frequency range supported is 1 Hz to 100 Mhz (clock rate) */ -+#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC) -+#define IPQ_PWM_MIN_PERIOD_NS 10 -+ -+/* -+ * Two 32-bit registers for each PWM: REG0, and REG1. -+ * Base offset for PWM #i is at 8 * #i. -+ */ -+#define IPQ_PWM_REG0 0 -+#define IPQ_PWM_REG0_PWM_DIV GENMASK(15, 0) -+#define IPQ_PWM_REG0_HI_DURATION GENMASK(31, 16) -+ -+#define IPQ_PWM_REG1 4 -+#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0) -+/* -+ * Enable bit is set to enable output toggling in pwm device. -+ * Update bit is set to trigger the change and is unset automatically -+ * to reflect the changed divider and high duration values in register. -+ */ -+#define IPQ_PWM_REG1_UPDATE BIT(30) -+#define IPQ_PWM_REG1_ENABLE BIT(31) -+ -+/* -+ * The max value specified for each field is based on the number of bits -+ * in the pwm control register for that field (16-bit) -+ */ -+#define IPQ_PWM_MAX_DIV FIELD_MAX(IPQ_PWM_REG0_PWM_DIV) -+ -+struct ipq_pwm_chip { -+ void __iomem *mem; -+ unsigned long clk_rate; -+}; -+ -+static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip) -+{ -+ return pwmchip_get_drvdata(chip); -+} -+ -+static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg) -+{ -+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); -+ unsigned int off = 8 * pwm->hwpwm + reg; -+ -+ return readl(ipq_chip->mem + off); -+} -+ -+static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg, -+ unsigned int val) -+{ -+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); -+ unsigned int off = 8 * pwm->hwpwm + reg; -+ -+ writel(val, ipq_chip->mem + off); -+} -+ -+static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, -+ const struct pwm_state *state) -+{ -+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); -+ unsigned int pre_div, pwm_div; -+ u64 period_ns, duty_ns; -+ unsigned long val = 0; -+ unsigned long hi_dur; -+ -+ if (!state->enabled) { -+ /* clear IPQ_PWM_REG1_ENABLE */ -+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE); -+ return 0; -+ } -+ -+ if (state->polarity != PWM_POLARITY_NORMAL) -+ return -EINVAL; -+ -+ /* -+ * Check the upper and lower bounds for the period as per -+ * hardware limits -+ */ -+ if (state->period < IPQ_PWM_MIN_PERIOD_NS) -+ return -ERANGE; -+ period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); -+ duty_ns = min(state->duty_cycle, period_ns); -+ -+ /* -+ * Pick the maximal value for PWM_DIV that still allows a -+ * 100% relative duty cycle. This allows a fine grained -+ * selection of duty cycles. -+ */ -+ pwm_div = IPQ_PWM_MAX_DIV - 1; -+ -+ /* -+ * although mul_u64_u64_div_u64 returns a u64, in practice it -+ * won't overflow due to above constraints. Take the max period -+ * of 10^9 (NSEC_PER_SEC) and the pwm_div + 1 (IPQ_PWM_MAX_DIV) -+ * 10^9 * 10^8 -+ * ------------- => which fits well into a 32-bit unsigned int. -+ * 10^9 * 65,535 -+ */ -+ pre_div = mul_u64_u64_div_u64(period_ns, ipq_chip->clk_rate, -+ (u64)NSEC_PER_SEC * (pwm_div + 1)); -+ -+ if (!pre_div) -+ return -ERANGE; -+ -+ pre_div -= 1; -+ -+ if (pre_div > IPQ_PWM_MAX_DIV) -+ pre_div = IPQ_PWM_MAX_DIV; -+ -+ /* pwm duty = HI_DUR * (PRE_DIV + 1) / clk_rate */ -+ hi_dur = mul_u64_u64_div_u64(duty_ns, ipq_chip->clk_rate, -+ (u64)NSEC_PER_SEC * (pre_div + 1)); -+ -+ val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) | -+ FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div); -+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val); -+ -+ val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div); -+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); -+ -+ /* PWM enable toggle needs a separate write to REG1 */ -+ val |= IPQ_PWM_REG1_UPDATE | IPQ_PWM_REG1_ENABLE; -+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); -+ -+ return 0; -+} -+ -+static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, -+ struct pwm_state *state) -+{ -+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); -+ unsigned int pre_div, pwm_div, hi_dur; -+ u64 effective_div, hi_div; -+ u32 reg0, reg1; -+ -+ reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1); -+ state->enabled = reg1 & IPQ_PWM_REG1_ENABLE; -+ -+ if (!state->enabled) -+ return 0; -+ -+ reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0); -+ -+ state->polarity = PWM_POLARITY_NORMAL; -+ -+ pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0); -+ hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0); -+ pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1); -+ -+ effective_div = (u64)(pwm_div + 1) * (pre_div + 1); -+ -+ /* -+ * effective_div <= 0x100000000, so the multiplication doesn't overflow. -+ */ -+ state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, -+ ipq_chip->clk_rate); -+ -+ hi_div = hi_dur * (pre_div + 1); -+ state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, -+ ipq_chip->clk_rate); -+ -+ /* -+ * ensure a valid config is passed back to PWM core in case duty_cycle -+ * is > period (>100%) -+ */ -+ state->duty_cycle = min(state->duty_cycle, state->period); -+ -+ return 0; -+} -+ -+static const struct pwm_ops ipq_pwm_ops = { -+ .apply = ipq_pwm_apply, -+ .get_state = ipq_pwm_get_state, -+}; -+ -+static int ipq_pwm_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct ipq_pwm_chip *pwm; -+ struct pwm_chip *chip; -+ struct clk *clk; -+ int ret; -+ -+ chip = devm_pwmchip_alloc(dev, 4, sizeof(*pwm)); -+ if (IS_ERR(chip)) -+ return PTR_ERR(chip); -+ pwm = ipq_pwm_from_chip(chip); -+ -+ pwm->mem = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(pwm->mem)) -+ return dev_err_probe(dev, PTR_ERR(pwm->mem), -+ "Failed to acquire resource\n"); -+ -+ clk = devm_clk_get_enabled(dev, NULL); -+ if (IS_ERR(clk)) -+ return dev_err_probe(dev, PTR_ERR(clk), -+ "Failed to get clock\n"); -+ -+ ret = devm_clk_rate_exclusive_get(dev, clk); -+ if (ret) -+ return dev_err_probe(dev, ret, "Failed to lock clock rate\n"); -+ -+ pwm->clk_rate = clk_get_rate(clk); -+ if (!pwm->clk_rate) -+ return dev_err_probe(dev, -EINVAL, "Failed due to clock rate being zero\n"); -+ -+ chip->ops = &ipq_pwm_ops; -+ -+ ret = devm_pwmchip_add(dev, chip); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); -+ -+ return 0; -+} -+ -+static const struct of_device_id pwm_ipq_dt_match[] = { -+ { .compatible = "qcom,ipq6018-pwm", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match); -+ -+static struct platform_driver ipq_pwm_driver = { -+ .driver = { -+ .name = "ipq-pwm", -+ .of_match_table = pwm_ipq_dt_match, -+ }, -+ .probe = ipq_pwm_probe, -+}; -+ -+module_platform_driver(ipq_pwm_driver); -+ -+MODULE_LICENSE("GPL"); diff --git a/target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch b/target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch deleted file mode 100644 index 4ee9086b890..00000000000 --- a/target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 6cc812b7435a64cd9d66af107c6dfefb2e77a7cb Mon Sep 17 00:00:00 2001 -From: Devi Priya -Date: Mon, 6 Apr 2026 22:24:40 +0200 -Subject: arm64: dts: qcom: ipq6018: add pwm node - -Describe the PWM block on IPQ6018. - -Although PWM is in the TCSR area, make pwm its own node as simple-mfd -has been removed from the bindings and as such hardware components -should have its own node. - -Reviewed-by: Krzysztof Kozlowski -Co-developed-by: Baruch Siach -Signed-off-by: Baruch Siach -Signed-off-by: Devi Priya -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20260406-ipq-pwm-v21-3-6ed1e868e4c2@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq6018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -416,6 +416,16 @@ - reg = <0x0 0x01937000 0x0 0x21000>; - }; - -+ pwm: pwm@1941010 { -+ compatible = "qcom,ipq6018-pwm"; -+ reg = <0x0 0x01941010 0x0 0x20>; -+ clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clock-rates = <100000000>; -+ #pwm-cells = <3>; -+ status = "disabled"; -+ }; -+ - usb2: usb@70f8800 { - compatible = "qcom,ipq6018-dwc3", "qcom,dwc3"; - reg = <0x0 0x070f8800 0x0 0x400>; diff --git a/target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch b/target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch deleted file mode 100644 index 4be6601939c..00000000000 --- a/target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 4b00afd17a28f44dc63a8602143f84e5f3fa592c Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 6 Apr 2026 22:24:41 +0200 -Subject: arm64: dts: qcom: ipq5018: add pwm node - -Describe the PWM block on IPQ5018. - -Although PWM is in the TCSR area, make pwm its own node as simple-mfd -has been removed from the bindings and as such hardware components -should have its own node. - -Reviewed-by: Dmitry Baryshkov -Reviewed-by: Konrad Dybcio -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20260406-ipq-pwm-v21-4-6ed1e868e4c2@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -453,6 +453,16 @@ - reg = <0x01937000 0x21000>; - }; - -+ pwm: pwm@1941010 { -+ compatible = "qcom,ipq5018-pwm", "qcom,ipq6018-pwm"; -+ reg = <0x01941010 0x20>; -+ clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clock-rates = <100000000>; -+ #pwm-cells = <3>; -+ status = "disabled"; -+ }; -+ - sdhc_1: mmc@7804000 { - compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; - reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch b/target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch deleted file mode 100644 index 0ab8bec21e2..00000000000 --- a/target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch +++ /dev/null @@ -1,52 +0,0 @@ -From a57666004f49fa5031d6bf388834213e6f961922 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Wed, 11 Mar 2026 19:39:38 +0100 -Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ6018 - -The CMN PLL block in the IPQ6018 SoC takes 48 MHz as the reference -input clock. Its output clocks are the bias_pll_cc_clk (300 MHz) and -bias_pll_nss_noc_clk (416.5 MHz) clocks used by the networking -subsystem. - -Add the related compatible for IPQ6018 to the ipq9574-cmn-pll -generic schema. - -Signed-off-by: John Crispin -Signed-off-by: Christian Marangi -Reviewed-by: Krzysztof Kozlowski -Link: https://lore.kernel.org/r/20260311183942.10134-2-ansuelsmth@gmail.com -Signed-off-by: Bjorn Andersson ---- - .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + - include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h | 15 +++++++++++++++ - 2 files changed, 16 insertions(+) - create mode 100644 include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h - ---- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -@@ -26,6 +26,7 @@ properties: - enum: - - qcom,ipq5018-cmn-pll - - qcom,ipq5424-cmn-pll -+ - qcom,ipq6018-cmn-pll - - qcom,ipq9574-cmn-pll - - reg: ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h -@@ -0,0 +1,15 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define IPQ6018_CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ6018. */ -+#define IPQ6018_BIAS_PLL_CC_CLK 1 -+#define IPQ6018_BIAS_PLL_NSS_NOC_CLK 2 -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch b/target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch deleted file mode 100644 index 8a104557b58..00000000000 --- a/target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 7156c65030006e6930dd99c5b8c5e84e69ca5f0b Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Wed, 11 Mar 2026 19:39:40 +0100 -Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ8074 - -The CMN PLL block in the IPQ8074 SoC takes 48 MHz as the reference -input clock. Its output clocks are the bias_pll_cc_clk (300 MHz) and -bias_pll_nss_noc_clk (416.5 MHz) clocks used by the networking -subsystem. - -Add the related compatible for IPQ8074 to the ipq9574-cmn-pll -generic schema. - -Signed-off-by: John Crispin -Signed-off-by: Christian Marangi -Reviewed-by: Krzysztof Kozlowski -Link: https://lore.kernel.org/r/20260311183942.10134-4-ansuelsmth@gmail.com -Signed-off-by: Bjorn Andersson ---- - .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + - include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h | 15 +++++++++++++++ - 2 files changed, 16 insertions(+) - create mode 100644 include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h - ---- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml -@@ -27,6 +27,7 @@ properties: - - qcom,ipq5018-cmn-pll - - qcom,ipq5424-cmn-pll - - qcom,ipq6018-cmn-pll -+ - qcom,ipq8074-cmn-pll - - qcom,ipq9574-cmn-pll - - reg: ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h -@@ -0,0 +1,15 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define IPQ8074_CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ8074. */ -+#define IPQ8074_BIAS_PLL_CC_CLK 1 -+#define IPQ8074_BIAS_PLL_NSS_NOC_CLK 2 -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch b/target/linux/qualcommax/patches-6.12/0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch deleted file mode 100644 index cfdeea5114e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 97eb2ac52726fbb702ced40d552a3f6f2683b664 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Wed, 11 Mar 2026 19:39:39 +0100 -Subject: [PATCH] clk: qcom: ipq-cmn-pll: Add IPQ6018 SoC support - -The CMN PLL in IPQ6018 SoC supplies fixed clocks to the networking -subsystem: bias_pll_cc_clk at 300 MHz and bias_pll_nss_noc_clk at -416.5 MHz. - -Signed-off-by: John Crispin -Signed-off-by: Christian Marangi -Link: https://lore.kernel.org/r/20260311183942.10134-3-ansuelsmth@gmail.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/ipq-cmn-pll.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/drivers/clk/qcom/ipq-cmn-pll.c -+++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -52,6 +52,7 @@ - #include - #include - #include -+#include - - #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 - #define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) -@@ -118,6 +119,12 @@ static const struct cmn_pll_fixed_output - { /* Sentinel */ } - }; - -+static const struct cmn_pll_fixed_output_clk ipq6018_output_clks[] = { -+ CLK_PLL_OUTPUT(IPQ6018_BIAS_PLL_CC_CLK, "bias_pll_cc_clk", 300000000UL), -+ CLK_PLL_OUTPUT(IPQ6018_BIAS_PLL_NSS_NOC_CLK, "bias_pll_nss_noc_clk", 416500000UL), -+ { /* Sentinel */ } -+}; -+ - static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { - CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), - CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -@@ -449,6 +456,7 @@ static const struct dev_pm_ops ipq_cmn_p - static const struct of_device_id ipq_cmn_pll_clk_ids[] = { - { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, - { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, -+ { .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks }, - { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, - { } - }; diff --git a/target/linux/qualcommax/patches-6.12/0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch b/target/linux/qualcommax/patches-6.12/0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch deleted file mode 100644 index e972b500b86..00000000000 --- a/target/linux/qualcommax/patches-6.12/0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 4e36f8ab45c406420f2c2ce6ee3988e0d13ba1c9 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Wed, 11 Mar 2026 19:39:41 +0100 -Subject: [PATCH] clk: qcom: ipq-cmn-pll: Add IPQ8074 SoC support - -The CMN PLL in IPQ8074 SoC supplies fixed clocks to the networking -subsystem: bias_pll_cc_clk at 300 MHz and bias_pll_nss_noc_clk at -416.5 MHz. - -Signed-off-by: John Crispin -Signed-off-by: Christian Marangi -Link: https://lore.kernel.org/r/20260311183942.10134-5-ansuelsmth@gmail.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/ipq-cmn-pll.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/drivers/clk/qcom/ipq-cmn-pll.c -+++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -53,6 +53,7 @@ - #include - #include - #include -+#include - - #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 - #define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) -@@ -125,6 +126,12 @@ static const struct cmn_pll_fixed_output - { /* Sentinel */ } - }; - -+static const struct cmn_pll_fixed_output_clk ipq8074_output_clks[] = { -+ CLK_PLL_OUTPUT(IPQ8074_BIAS_PLL_CC_CLK, "bias_pll_cc_clk", 300000000UL), -+ CLK_PLL_OUTPUT(IPQ8074_BIAS_PLL_NSS_NOC_CLK, "bias_pll_nss_noc_clk", 416500000UL), -+ { /* Sentinel */ } -+}; -+ - static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { - CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), - CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -@@ -457,6 +464,7 @@ static const struct of_device_id ipq_cmn - { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, - { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, - { .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks }, -+ { .compatible = "qcom,ipq8074-cmn-pll", .data = &ipq8074_output_clks }, - { .compatible = "qcom,ipq9574-cmn-pll", .data = &ipq9574_output_clks }, - { } - }; diff --git a/target/linux/qualcommax/patches-6.12/0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch b/target/linux/qualcommax/patches-6.12/0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch deleted file mode 100644 index 2c786752e50..00000000000 --- a/target/linux/qualcommax/patches-6.12/0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 88c543fff756450bcd04ec4560c4440be36c9e75 Mon Sep 17 00:00:00 2001 -From: Luo Jie -Date: Tue, 6 Jan 2026 21:35:10 -0800 -Subject: [PATCH] clk: qcom: cmnpll: Account for reference clock divider - -The clk_cmn_pll_recalc_rate() function must account for the reference clock -divider programmed in CMN_PLL_REFCLK_CONFIG. Without this fix, platforms -with a reference divider other than 1 calculate incorrect CMN PLL rates. -For example, on IPQ5332 where the reference divider is 2, the computed rate -becomes twice the actual output. - -Read CMN_PLL_REFCLK_DIV and divide the parent rate by this value before -applying the 2 * FACTOR scaling. This yields the correct rate calculation: -rate = (parent_rate / ref_div) * 2 * factor. - -Maintain backward compatibility with earlier platforms (e.g. IPQ9574, -IPQ5424, IPQ5018) that use ref_div = 1. - -Fixes: f81715a4c87c ("clk: qcom: Add CMN PLL clock controller driver for IPQ SoC") -Signed-off-by: Luo Jie -Reviewed-by: Konrad Dybcio -Tested-by: George Moussalem -Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-1-f9f7e4efbd79@oss.qualcomm.com -Signed-off-by: Bjorn Andersson ---- - drivers/clk/qcom/ipq-cmn-pll.c | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - ---- a/drivers/clk/qcom/ipq-cmn-pll.c -+++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -200,7 +200,7 @@ static unsigned long clk_cmn_pll_recalc_ - unsigned long parent_rate) - { - struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); -- u32 val, factor; -+ u32 val, factor, ref_div; - - /* - * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted -@@ -208,8 +208,15 @@ static unsigned long clk_cmn_pll_recalc_ - */ - regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); - factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); -+ if (WARN_ON(factor == 0)) -+ factor = 1; - -- return parent_rate * 2 * factor; -+ regmap_read(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, &val); -+ ref_div = FIELD_GET(CMN_PLL_REFCLK_DIV, val); -+ if (WARN_ON(ref_div == 0)) -+ ref_div = 1; -+ -+ return div_u64((u64)parent_rate * 2 * factor, ref_div); - } - - static int clk_cmn_pll_determine_rate(struct clk_hw *hw, diff --git a/target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch b/target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch deleted file mode 100644 index d3a2d8c086e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 02320d694fa1124b8b77fcb52191141004ef7fbb Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Thu, 21 May 2026 11:55:58 +0400 -Subject: arm64: dts: qcom: ipq5018: Correct CMN PLL reference clock rate - -The correct CMN PLL reference clock rate for IPQ5018 is 4.8 GHz. - -The CMN PLL driver did not account for the ref clock divider which is 2 -for IPQ5018. Therefore, the computed rate was twice the actual output. - -With the driver now accounting for the CMN PLL reference clock -divider (commit: 88c543fff756), set the correct reference clock rate. - -Fixes: c006b249c544 ("arm64: dts: ipq5018: Add CMN PLL node") -Signed-off-by: George Moussalem -Acked-by: Konrad Dybcio -Link: https://lore.kernel.org/r/20260521-ipq5018-cmn-pll-rate-fix-v2-1-04b28a92e0f2@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -256,7 +256,7 @@ - "sys"; - #clock-cells = <1>; - assigned-clocks = <&cmn_pll IPQ5018_CMN_PLL_CLK>; -- assigned-clock-rates-u64 = /bits/ 64 <9600000000>; -+ assigned-clock-rates-u64 = /bits/ 64 <4800000000>; - }; - - qfprom: qfprom@a0000 { diff --git a/target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch b/target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch deleted file mode 100644 index 52ff962cf4d..00000000000 --- a/target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 82eaf94d69fce20f8859a2b8dae8e7064d9343da Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Fri, 13 Jun 2025 05:55:07 +0400 -Subject: [PATCH] dt-bindings: net: qca,ar803x: Add IPQ5018 Internal GE PHY - support - -Document the IPQ5018 Internal Gigabit Ethernet PHY found in the IPQ5018 -SoC. Its output pins provide an MDI interface to either an external -switch in a PHY to PHY link scenario or is directly attached to an RJ45 -connector. - -The PHY supports 10/100/1000 mbps link modes, CDT, auto-negotiation and -802.3az EEE. - -For operation, the LDO controller found in the IPQ5018 SoC for which -there is provision in the mdio-4019 driver. - -Two common archictures across IPQ5018 boards are: -1. IPQ5018 PHY --> MDI --> RJ45 connector -2. IPQ5018 PHY --> MDI --> External PHY -In a phy to phy architecture, the DAC needs to be configured to -accommodate for the short cable length. As such, add an optional boolean -property so the driver sets preset DAC register values accordingly. - -Signed-off-by: George Moussalem -Reviewed-by: Krzysztof Kozlowski -Link: https://patch.msgid.link/20250613-ipq5018-ge-phy-v5-1-9af06e34ea6b@outlook.com -Signed-off-by: Jakub Kicinski ---- - .../devicetree/bindings/net/qca,ar803x.yaml | 43 +++++++++++++++++++ - 1 file changed, 43 insertions(+) - ---- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml -+++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml -@@ -16,8 +16,37 @@ description: | - - allOf: - - $ref: ethernet-phy.yaml# -+ - if: -+ properties: -+ compatible: -+ contains: -+ enum: -+ - ethernet-phy-id004d.d0c0 -+ -+ then: -+ properties: -+ reg: -+ const: 7 # This PHY is always at MDIO address 7 in the IPQ5018 SoC -+ -+ resets: -+ items: -+ - description: -+ GE PHY MISC reset which triggers a reset across MDC, DSP, RX, and TX lines. -+ -+ qcom,dac-preset-short-cable: -+ description: -+ Set if this phy is connected to another phy to adjust the values for -+ MDAC and EDAC to adjust amplitude, bias current settings, and error -+ detection and correction algorithm to accommodate for short cable length. -+ If not set, DAC values are not modified and it is assumed the MDI output pins -+ of this PHY are directly connected to an RJ45 connector. -+ type: boolean - - properties: -+ compatible: -+ enum: -+ - ethernet-phy-id004d.d0c0 -+ - qca,clk-out-frequency: - description: Clock output frequency in Hertz. - $ref: /schemas/types.yaml#/definitions/uint32 -@@ -132,3 +161,17 @@ examples: - }; - }; - }; -+ - | -+ #include -+ -+ mdio { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ge_phy: ethernet-phy@7 { -+ compatible = "ethernet-phy-id004d.d0c0"; -+ reg = <7>; -+ -+ resets = <&gcc GCC_GEPHY_MISC_ARES>; -+ }; -+ }; diff --git a/target/linux/qualcommax/patches-6.12/0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch b/target/linux/qualcommax/patches-6.12/0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch deleted file mode 100644 index 73d6975eca1..00000000000 --- a/target/linux/qualcommax/patches-6.12/0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 19e00bdfbb1b5f7a2d1f3c1c38b3da25e17ea486 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 8 Jun 2026 09:09:16 +0400 -Subject: [PATCH] dt-bindings: net: ethernet-phy: increase max clock count to - two - -The clocks property has a restriction to maximum one. -Yet, some PHYs may require more than 1 clock such as the IPQ5018 PHY -which requires two clocks for RX and TX. As such, increase maxItems to -two. - -Reviewed-by: Rob Herring (Arm) -Signed-off-by: George Moussalem -Link: https://patch.msgid.link/20260608-ipq5018-gephy-clocks-v4-1-fb2ccd56894b@outlook.com -Signed-off-by: Jakub Kicinski ---- - Documentation/devicetree/bindings/net/ethernet-phy.yaml | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - ---- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml -+++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml -@@ -102,10 +102,13 @@ properties: - by software. - - clocks: -- maxItems: 1 -+ minItems: 1 -+ maxItems: 2 - description: -- External clock connected to the PHY. If not specified it is assumed -- that the PHY uses a fixed crystal or an internal oscillator. -+ External clock connected to the PHY or RX and TX clocks that the PHY -+ requires to enable explicitly. If not specified it is assumed -+ that the PHY uses a fixed crystal or an internal oscillator or that the -+ RX/TX clocks are hardware enabled by default. - - enet-phy-lane-swap: - $ref: /schemas/types.yaml#/definitions/flag diff --git a/target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch b/target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch deleted file mode 100644 index d410a241871..00000000000 --- a/target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch +++ /dev/null @@ -1,66 +0,0 @@ -From fea4ae4b5b5059612d1a4f5acb88c27a5f7e60dc Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 8 Jun 2026 09:09:17 +0400 -Subject: [PATCH] dt-bindings: net: qca,ar803x: Add clocks for IPQ5018 PHY - -Further testing revealed that the RX and TX clocks of the IPQ5018 PHY -need to be explicitly enabled. As such, add the required clocks to the -schema. - -Acked-by: Conor Dooley -Signed-off-by: George Moussalem -Link: https://patch.msgid.link/20260608-ipq5018-gephy-clocks-v4-2-fb2ccd56894b@outlook.com -Signed-off-by: Jakub Kicinski ---- - .../devicetree/bindings/net/qca,ar803x.yaml | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - ---- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml -+++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml -@@ -28,6 +28,16 @@ allOf: - reg: - const: 7 # This PHY is always at MDIO address 7 in the IPQ5018 SoC - -+ clocks: -+ items: -+ - description: RX clock -+ - description: TX clock -+ -+ clock-names: -+ items: -+ - const: rx -+ - const: tx -+ - resets: - items: - - description: -@@ -42,6 +52,11 @@ allOf: - of this PHY are directly connected to an RJ45 connector. - type: boolean - -+ required: -+ - clocks -+ - clock-names -+ - resets -+ - properties: - compatible: - enum: -@@ -162,6 +177,7 @@ examples: - }; - }; - - | -+ #include - #include - - mdio { -@@ -172,6 +188,9 @@ examples: - compatible = "ethernet-phy-id004d.d0c0"; - reg = <7>; - -+ clocks = <&gcc GCC_GEPHY_RX_CLK>, -+ <&gcc GCC_GEPHY_TX_CLK>; -+ clock-names = "rx", "tx"; - resets = <&gcc GCC_GEPHY_MISC_ARES>; - }; - }; diff --git a/target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch b/target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch deleted file mode 100644 index 4a43b525494..00000000000 --- a/target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch +++ /dev/null @@ -1,51 +0,0 @@ -From cf6077e4903ffed2291f5f3cb9d61b29abe456c4 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 8 Jun 2026 09:09:19 +0400 -Subject: [PATCH] net: phy: at803x: add RX and TX clock management for IPQ5018 - PHY - -Acquire and enable the RX and TX clocks for the IPQ5018 PHY. -These clocks are required for the PHY's datapath to function correctly. - -Signed-off-by: George Moussalem -Link: https://patch.msgid.link/20260608-ipq5018-gephy-clocks-v4-4-fb2ccd56894b@outlook.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/phy/qcom/at803x.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - ---- a/drivers/net/phy/qcom/at803x.c -+++ b/drivers/net/phy/qcom/at803x.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1089,6 +1090,7 @@ static void ipq5018_link_change_notify(s - static int ipq5018_probe(struct phy_device *phydev) - { - struct device *dev = &phydev->mdio.dev; -+ struct clk *rx_clk, *tx_clk; - struct ipq5018_priv *priv; - int ret; - -@@ -1099,6 +1101,16 @@ static int ipq5018_probe(struct phy_devi - priv->set_short_cable_dac = of_property_read_bool(dev->of_node, - "qcom,dac-preset-short-cable"); - -+ rx_clk = devm_clk_get_enabled(dev, "rx"); -+ if (IS_ERR(rx_clk)) -+ return dev_err_probe(dev, PTR_ERR(rx_clk), -+ "failed to get and enable RX clock\n"); -+ -+ tx_clk = devm_clk_get_enabled(dev, "tx"); -+ if (IS_ERR(tx_clk)) -+ return dev_err_probe(dev, PTR_ERR(tx_clk), -+ "failed to get and enable TX clock\n"); -+ - priv->rst = devm_reset_control_array_get_exclusive(dev); - if (IS_ERR(priv->rst)) - return dev_err_probe(dev, PTR_ERR(priv->rst), diff --git a/target/linux/qualcommax/patches-6.12/0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch b/target/linux/qualcommax/patches-6.12/0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch deleted file mode 100644 index b77a83403cd..00000000000 --- a/target/linux/qualcommax/patches-6.12/0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 20959cefc27e7e2e506b4e789b728e5702c77f95 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Mon, 8 Jun 2026 09:09:18 +0400 -Subject: arm64: qcom: ipq5018: Add GEPHY RX and TX clocks - -Add RX and TX clocks for the IPQ5018 GEPHY to enable the datapath. - -Fixes: f5f2b835e316 ("arm64: dts: qcom: ipq5018: Add GE PHY to internal mdio bus") -Reviewed-by: Dmitry Baryshkov -Signed-off-by: George Moussalem -Link: https://lore.kernel.org/r/20260608-ipq5018-gephy-clocks-v4-3-fb2ccd56894b@outlook.com -Signed-off-by: Bjorn Andersson ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 3 +++ - 1 file changed, 3 insertions(+) - -(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -229,6 +229,9 @@ - compatible = "ethernet-phy-id004d.d0c0"; - reg = <7>; - -+ clocks = <&gcc GCC_GEPHY_RX_CLK>, -+ <&gcc GCC_GEPHY_TX_CLK>; -+ clock-names = "rx", "tx"; - resets = <&gcc GCC_GEPHY_MISC_ARES>; - }; - }; diff --git a/target/linux/qualcommax/patches-6.12/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch b/target/linux/qualcommax/patches-6.12/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch deleted file mode 100644 index 6d97641f658..00000000000 --- a/target/linux/qualcommax/patches-6.12/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch +++ /dev/null @@ -1,60 +0,0 @@ -From ad2d07f71739351eeea1d8a120c0918e2c4b265f Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 22 Dec 2021 12:23:34 +0100 -Subject: [PATCH] arm64: dts: ipq8074: add reserved memory nodes - -IPQ8074 has multiple reserved memory ranges, if they are not defined -then weird things tend to happen, board hangs and resets when PCI or -WLAN is used etc. - -So, to avoid all of that add the reserved memory nodes from the downstream -5.4 kernel from QCA. -This is their default layout meant for devices with 1GB of RAM, but -devices with lower ammounts can override the Q6 node. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 35 +++++++++++++++++++++++++++ - 1 file changed, 35 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -86,6 +86,16 @@ - #size-cells = <2>; - ranges; - -+ nss@40000000 { -+ no-map; -+ reg = <0x0 0x40000000 0x0 0x01000000>; -+ }; -+ -+ tzapp_region: tzapp@4a400000 { -+ no-map; -+ reg = <0x0 0x4a400000 0x0 0x00200000>; -+ }; -+ - bootloader@4a600000 { - reg = <0x0 0x4a600000 0x0 0x400000>; - no-map; -@@ -108,6 +118,21 @@ - reg = <0x0 0x4ac00000 0x0 0x400000>; - no-map; - }; -+ -+ q6_region: wcnss@4b000000 { -+ no-map; -+ reg = <0x0 0x4b000000 0x0 0x05f00000>; -+ }; -+ -+ q6_etr_region: q6_etr_dump@50f00000 { -+ no-map; -+ reg = <0x0 0x50f00000 0x0 0x00100000>; -+ }; -+ -+ m3_dump_region: m3_dump@51000000 { -+ no-map; -+ reg = <0x0 0x51000000 0x0 0x100000>; -+ }; - }; - - firmware { diff --git a/target/linux/qualcommax/patches-6.12/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch b/target/linux/qualcommax/patches-6.12/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch deleted file mode 100644 index a12dc442061..00000000000 --- a/target/linux/qualcommax/patches-6.12/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch +++ /dev/null @@ -1,43 +0,0 @@ -From fb1f6850be00d8dd8a54017be4c1336e224069ac Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 16 Nov 2022 22:26:25 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074: use msi-parent for PCIe - -Instead of hardcoding the IRQ, simply use msi-parent instead. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 8 +++----- - 1 file changed, 3 insertions(+), 5 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -759,7 +759,7 @@ - reg = <0x0b000000 0x1000>, <0x0b002000 0x1000>; - ranges = <0 0xb00a000 0xffd>; - -- v2m@0 { -+ gic_v2m0: v2m@0 { - compatible = "arm,gic-v2m-frame"; - msi-controller; - reg = <0x0 0xffd>; -@@ -872,8 +872,7 @@ - ranges = <0x81000000 0x0 0x00000000 0x10200000 0x0 0x10000>, /* I/O */ - <0x82000000 0x0 0x10220000 0x10220000 0x0 0xfde0000>; /* MEM */ - -- interrupts = ; -- interrupt-names = "msi"; -+ msi-parent = <&gic_v2m0>; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc 0 0 142 -@@ -944,8 +943,7 @@ - ranges = <0x81000000 0x0 0x00000000 0x20200000 0x0 0x10000>, /* I/O */ - <0x82000000 0x0 0x20220000 0x20220000 0x0 0xfde0000>; /* MEM */ - -- interrupts = ; -- interrupt-names = "msi"; -+ msi-parent = <&gic_v2m0>; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc 0 0 75 diff --git a/target/linux/qualcommax/patches-6.12/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch b/target/linux/qualcommax/patches-6.12/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch deleted file mode 100644 index ac46f9e79be..00000000000 --- a/target/linux/qualcommax/patches-6.12/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch +++ /dev/null @@ -1,155 +0,0 @@ -From 125681433c8e526356947acf572fe8ca8ad32291 Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:05 +0530 -Subject: [PATCH] remoteproc: qcom: Add PRNG proxy clock - -PRNG clock is needed by the secure PIL, support for the same -is added in subsequent patches. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 65 +++++++++++++++++++++-------- - 1 file changed, 47 insertions(+), 18 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -91,19 +91,6 @@ enum { - WCSS_QCS404, - }; - --struct wcss_data { -- const char *firmware_name; -- unsigned int crash_reason_smem; -- u32 version; -- bool aon_reset_required; -- bool wcss_q6_reset_required; -- const char *ssr_name; -- const char *sysmon_name; -- int ssctl_id; -- const struct rproc_ops *ops; -- bool requires_force_stop; --}; -- - struct q6v5_wcss { - struct device *dev; - -@@ -128,6 +115,7 @@ struct q6v5_wcss { - struct clk *qdsp6ss_xo_cbcr; - struct clk *qdsp6ss_core_gfmux; - struct clk *lcc_bcr_sleep; -+ struct clk *prng_clk; - struct regulator *cx_supply; - struct qcom_sysmon *sysmon; - -@@ -152,6 +140,21 @@ struct q6v5_wcss { - struct qcom_rproc_ssr ssr_subdev; - }; - -+struct wcss_data { -+ int (*init_clock)(struct q6v5_wcss *wcss); -+ int (*init_regulator)(struct q6v5_wcss *wcss); -+ const char *firmware_name; -+ unsigned int crash_reason_smem; -+ u32 version; -+ bool aon_reset_required; -+ bool wcss_q6_reset_required; -+ const char *ssr_name; -+ const char *sysmon_name; -+ int ssctl_id; -+ const struct rproc_ops *ops; -+ bool requires_force_stop; -+}; -+ - static int q6v5_wcss_reset(struct q6v5_wcss *wcss) - { - int ret; -@@ -241,6 +244,12 @@ static int q6v5_wcss_start(struct rproc - struct q6v5_wcss *wcss = rproc->priv; - int ret; - -+ ret = clk_prepare_enable(wcss->prng_clk); -+ if (ret) { -+ dev_err(wcss->dev, "prng clock enable failed\n"); -+ return ret; -+ } -+ - qcom_q6v5_prepare(&wcss->q6v5); - - /* Release Q6 and WCSS reset */ -@@ -734,6 +743,7 @@ static int q6v5_wcss_stop(struct rproc * - return ret; - } - -+ clk_disable_unprepare(wcss->prng_clk); - qcom_q6v5_unprepare(&wcss->q6v5); - - return 0; -@@ -900,7 +910,21 @@ static int q6v5_alloc_memory_region(stru - return 0; - } - --static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss) -+static int ipq8074_init_clock(struct q6v5_wcss *wcss) -+{ -+ int ret; -+ -+ wcss->prng_clk = devm_clk_get(wcss->dev, "prng"); -+ if (IS_ERR(wcss->prng_clk)) { -+ ret = PTR_ERR(wcss->prng_clk); -+ if (ret != -EPROBE_DEFER) -+ dev_err(wcss->dev, "Failed to get prng clock\n"); -+ return ret; -+ } -+ return 0; -+} -+ -+static int qcs404_init_clock(struct q6v5_wcss *wcss) - { - int ret; - -@@ -990,7 +1014,7 @@ static int q6v5_wcss_init_clock(struct q - return 0; - } - --static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss) -+static int qcs404_init_regulator(struct q6v5_wcss *wcss) - { - wcss->cx_supply = devm_regulator_get(wcss->dev, "cx"); - if (IS_ERR(wcss->cx_supply)) -@@ -1034,12 +1058,14 @@ static int q6v5_wcss_probe(struct platfo - if (ret) - return ret; - -- if (wcss->version == WCSS_QCS404) { -- ret = q6v5_wcss_init_clock(wcss); -+ if (desc->init_clock) { -+ ret = desc->init_clock(wcss); - if (ret) - return ret; -+ } - -- ret = q6v5_wcss_init_regulator(wcss); -+ if (desc->init_regulator) { -+ ret = desc->init_regulator(wcss); - if (ret) - return ret; - } -@@ -1081,6 +1107,7 @@ static void q6v5_wcss_remove(struct plat - } - - static const struct wcss_data wcss_ipq8074_res_init = { -+ .init_clock = ipq8074_init_clock, - .firmware_name = "IPQ8074/q6_fw.mdt", - .crash_reason_smem = WCSS_CRASH_REASON, - .aon_reset_required = true, -@@ -1090,6 +1117,8 @@ static const struct wcss_data wcss_ipq80 - }; - - static const struct wcss_data wcss_qcs404_res_init = { -+ .init_clock = qcs404_init_clock, -+ .init_regulator = qcs404_init_regulator, - .crash_reason_smem = WCSS_CRASH_REASON, - .firmware_name = "wcnss.mdt", - .version = WCSS_QCS404, diff --git a/target/linux/qualcommax/patches-6.12/0113-remoteproc-qcom-Add-secure-PIL-support.patch b/target/linux/qualcommax/patches-6.12/0113-remoteproc-qcom-Add-secure-PIL-support.patch deleted file mode 100644 index 6b11f048087..00000000000 --- a/target/linux/qualcommax/patches-6.12/0113-remoteproc-qcom-Add-secure-PIL-support.patch +++ /dev/null @@ -1,143 +0,0 @@ -From 7358d42dfbdfdb5d4f1d0d4c2e5c2bb4143a29b0 Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:06 +0530 -Subject: [PATCH] remoteproc: qcom: Add secure PIL support - -IPQ8074 uses secure PIL. Hence, adding the support for the same. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 43 +++++++++++++++++++++++++++-- - 1 file changed, 40 insertions(+), 3 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -18,6 +18,7 @@ - #include - #include - #include -+#include - #include "qcom_common.h" - #include "qcom_pil_info.h" - #include "qcom_q6v5.h" -@@ -86,6 +87,9 @@ - #define TCSR_WCSS_CLK_ENABLE 0x14 - - #define MAX_HALT_REG 4 -+ -+#define WCNSS_PAS_ID 6 -+ - enum { - WCSS_IPQ8074, - WCSS_QCS404, -@@ -134,6 +138,7 @@ struct q6v5_wcss { - unsigned int crash_reason_smem; - u32 version; - bool requires_force_stop; -+ bool need_mem_protection; - - struct qcom_rproc_glink glink_subdev; - struct qcom_rproc_pdm pdm_subdev; -@@ -153,6 +158,7 @@ struct wcss_data { - int ssctl_id; - const struct rproc_ops *ops; - bool requires_force_stop; -+ bool need_mem_protection; - }; - - static int q6v5_wcss_reset(struct q6v5_wcss *wcss) -@@ -252,6 +258,15 @@ static int q6v5_wcss_start(struct rproc - - qcom_q6v5_prepare(&wcss->q6v5); - -+ if (wcss->need_mem_protection) { -+ ret = qcom_scm_pas_auth_and_reset(WCNSS_PAS_ID); -+ if (ret) { -+ dev_err(wcss->dev, "wcss_reset failed\n"); -+ return ret; -+ } -+ goto wait_for_reset; -+ } -+ - /* Release Q6 and WCSS reset */ - ret = reset_control_deassert(wcss->wcss_reset); - if (ret) { -@@ -286,6 +301,7 @@ static int q6v5_wcss_start(struct rproc - if (ret) - goto wcss_q6_reset; - -+wait_for_reset: - ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ); - if (ret == -ETIMEDOUT) - dev_err(wcss->dev, "start timed out\n"); -@@ -719,6 +735,15 @@ static int q6v5_wcss_stop(struct rproc * - struct q6v5_wcss *wcss = rproc->priv; - int ret; - -+ if (wcss->need_mem_protection) { -+ ret = qcom_scm_pas_shutdown(WCNSS_PAS_ID); -+ if (ret) { -+ dev_err(wcss->dev, "not able to shutdown\n"); -+ return ret; -+ } -+ goto pas_done; -+ } -+ - /* WCSS powerdown */ - if (wcss->requires_force_stop) { - ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL); -@@ -743,6 +768,7 @@ static int q6v5_wcss_stop(struct rproc * - return ret; - } - -+pas_done: - clk_disable_unprepare(wcss->prng_clk); - qcom_q6v5_unprepare(&wcss->q6v5); - -@@ -766,9 +792,15 @@ static int q6v5_wcss_load(struct rproc * - struct q6v5_wcss *wcss = rproc->priv; - int ret; - -- ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, -- 0, wcss->mem_region, wcss->mem_phys, -- wcss->mem_size, &wcss->mem_reloc); -+ if (wcss->need_mem_protection) -+ ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, -+ WCNSS_PAS_ID, wcss->mem_region, -+ wcss->mem_phys, wcss->mem_size, -+ &wcss->mem_reloc); -+ else -+ ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, -+ 0, wcss->mem_region, wcss->mem_phys, -+ wcss->mem_size, &wcss->mem_reloc); - if (ret) - return ret; - -@@ -1036,6 +1068,9 @@ static int q6v5_wcss_probe(struct platfo - if (!desc) - return -EINVAL; - -+ if (desc->need_mem_protection && !qcom_scm_is_available()) -+ return -EPROBE_DEFER; -+ - rproc = devm_rproc_alloc(&pdev->dev, pdev->name, desc->ops, - desc->firmware_name, sizeof(*wcss)); - if (!rproc) { -@@ -1049,6 +1084,7 @@ static int q6v5_wcss_probe(struct platfo - - wcss->version = desc->version; - wcss->requires_force_stop = desc->requires_force_stop; -+ wcss->need_mem_protection = desc->need_mem_protection; - - ret = q6v5_wcss_init_mmio(wcss, pdev); - if (ret) -@@ -1114,6 +1150,7 @@ static const struct wcss_data wcss_ipq80 - .wcss_q6_reset_required = true, - .ops = &q6v5_wcss_ipq8074_ops, - .requires_force_stop = true, -+ .need_mem_protection = true, - }; - - static const struct wcss_data wcss_qcs404_res_init = { diff --git a/target/linux/qualcommax/patches-6.12/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch b/target/linux/qualcommax/patches-6.12/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch deleted file mode 100644 index be793d65865..00000000000 --- a/target/linux/qualcommax/patches-6.12/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch +++ /dev/null @@ -1,103 +0,0 @@ -From b422c9d4f048b086ce83f44a7cfcddcce162897f Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:07 +0530 -Subject: [PATCH] remoteproc: qcom: Add support for split q6 + m3 wlan firmware - -IPQ8074 supports split firmware for q6 and m3 as well. -So add support for loading the m3 firmware before q6. -Now the drivers works fine for both split and unified -firmwares. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 33 +++++++++++++++++++++++++---- - 1 file changed, 29 insertions(+), 4 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -139,6 +139,7 @@ struct q6v5_wcss { - u32 version; - bool requires_force_stop; - bool need_mem_protection; -+ const char *m3_firmware_name; - - struct qcom_rproc_glink glink_subdev; - struct qcom_rproc_pdm pdm_subdev; -@@ -148,7 +149,8 @@ struct q6v5_wcss { - struct wcss_data { - int (*init_clock)(struct q6v5_wcss *wcss); - int (*init_regulator)(struct q6v5_wcss *wcss); -- const char *firmware_name; -+ const char *q6_firmware_name; -+ const char *m3_firmware_name; - unsigned int crash_reason_smem; - u32 version; - bool aon_reset_required; -@@ -790,8 +792,29 @@ static void *q6v5_wcss_da_to_va(struct r - static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) - { - struct q6v5_wcss *wcss = rproc->priv; -+ const struct firmware *m3_fw; - int ret; - -+ if (wcss->m3_firmware_name) { -+ ret = request_firmware(&m3_fw, wcss->m3_firmware_name, -+ wcss->dev); -+ if (ret) -+ goto skip_m3; -+ -+ ret = qcom_mdt_load_no_init(wcss->dev, m3_fw, -+ wcss->m3_firmware_name, 0, -+ wcss->mem_region, wcss->mem_phys, -+ wcss->mem_size, &wcss->mem_reloc); -+ -+ release_firmware(m3_fw); -+ -+ if (ret) { -+ dev_err(wcss->dev, "can't load m3_fw.bXX\n"); -+ return ret; -+ } -+ } -+ -+skip_m3: - if (wcss->need_mem_protection) - ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, - WCNSS_PAS_ID, wcss->mem_region, -@@ -1072,7 +1095,7 @@ static int q6v5_wcss_probe(struct platfo - return -EPROBE_DEFER; - - rproc = devm_rproc_alloc(&pdev->dev, pdev->name, desc->ops, -- desc->firmware_name, sizeof(*wcss)); -+ desc->q6_firmware_name, sizeof(*wcss)); - if (!rproc) { - dev_err(&pdev->dev, "failed to allocate rproc\n"); - return -ENOMEM; -@@ -1085,6 +1108,7 @@ static int q6v5_wcss_probe(struct platfo - wcss->version = desc->version; - wcss->requires_force_stop = desc->requires_force_stop; - wcss->need_mem_protection = desc->need_mem_protection; -+ wcss->m3_firmware_name = desc->m3_firmware_name; - - ret = q6v5_wcss_init_mmio(wcss, pdev); - if (ret) -@@ -1144,7 +1168,8 @@ static void q6v5_wcss_remove(struct plat - - static const struct wcss_data wcss_ipq8074_res_init = { - .init_clock = ipq8074_init_clock, -- .firmware_name = "IPQ8074/q6_fw.mdt", -+ .q6_firmware_name = "IPQ8074/q6_fw.mdt", -+ .m3_firmware_name = "IPQ8074/m3_fw.mdt", - .crash_reason_smem = WCSS_CRASH_REASON, - .aon_reset_required = true, - .wcss_q6_reset_required = true, -@@ -1157,7 +1182,7 @@ static const struct wcss_data wcss_qcs40 - .init_clock = qcs404_init_clock, - .init_regulator = qcs404_init_regulator, - .crash_reason_smem = WCSS_CRASH_REASON, -- .firmware_name = "wcnss.mdt", -+ .q6_firmware_name = "wcnss.mdt", - .version = WCSS_QCS404, - .aon_reset_required = false, - .wcss_q6_reset_required = false, diff --git a/target/linux/qualcommax/patches-6.12/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch b/target/linux/qualcommax/patches-6.12/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch deleted file mode 100644 index 5d6a573c911..00000000000 --- a/target/linux/qualcommax/patches-6.12/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 3a8f67b4770c817b04794c9a02e3f88f85d86280 Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:08 +0530 -Subject: [PATCH] remoteproc: qcom: Add ssr subdevice identifier - -Add name for ssr subdevice on IPQ8074 SoC. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -1173,6 +1173,7 @@ static const struct wcss_data wcss_ipq80 - .crash_reason_smem = WCSS_CRASH_REASON, - .aon_reset_required = true, - .wcss_q6_reset_required = true, -+ .ssr_name = "q6wcss", - .ops = &q6v5_wcss_ipq8074_ops, - .requires_force_stop = true, - .need_mem_protection = true, diff --git a/target/linux/qualcommax/patches-6.12/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch b/target/linux/qualcommax/patches-6.12/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch deleted file mode 100644 index c9795b80282..00000000000 --- a/target/linux/qualcommax/patches-6.12/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 8c73af6e8d78c66cfef0f551b00d375ec0b67ff3 Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:09 +0530 -Subject: [PATCH] remoteproc: qcom: Update regmap offsets for halt register - -Fixed issue in reading halt-regs parameter from device-tree. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 22 ++++++++++++++-------- - 1 file changed, 14 insertions(+), 8 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -155,6 +155,7 @@ struct wcss_data { - u32 version; - bool aon_reset_required; - bool wcss_q6_reset_required; -+ bool bcr_reset_required; - const char *ssr_name; - const char *sysmon_name; - int ssctl_id; -@@ -876,10 +877,13 @@ static int q6v5_wcss_init_reset(struct q - } - } - -- wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset"); -- if (IS_ERR(wcss->wcss_q6_bcr_reset)) { -- dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); -- return PTR_ERR(wcss->wcss_q6_bcr_reset); -+ if (desc->bcr_reset_required) { -+ wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, -+ "wcss_q6_bcr_reset"); -+ if (IS_ERR(wcss->wcss_q6_bcr_reset)) { -+ dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n"); -+ return PTR_ERR(wcss->wcss_q6_bcr_reset); -+ } - } - - return 0; -@@ -1173,6 +1177,7 @@ static const struct wcss_data wcss_ipq80 - .crash_reason_smem = WCSS_CRASH_REASON, - .aon_reset_required = true, - .wcss_q6_reset_required = true, -+ .bcr_reset_required = false, - .ssr_name = "q6wcss", - .ops = &q6v5_wcss_ipq8074_ops, - .requires_force_stop = true, -@@ -1187,6 +1192,7 @@ static const struct wcss_data wcss_qcs40 - .version = WCSS_QCS404, - .aon_reset_required = false, - .wcss_q6_reset_required = false, -+ .bcr_reset_required = true, - .ssr_name = "mpss", - .sysmon_name = "wcnss", - .ssctl_id = 0x12, diff --git a/target/linux/qualcommax/patches-6.12/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch b/target/linux/qualcommax/patches-6.12/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch deleted file mode 100644 index fe0e0f9e0bf..00000000000 --- a/target/linux/qualcommax/patches-6.12/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch +++ /dev/null @@ -1,26 +0,0 @@ -From ff7c6533ed8c4de58ed6c8aab03ea59c03eb4f31 Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:10 +0530 -Subject: [PATCH] dt-bindings: clock: qcom: Add reset for WCSSAON - -Add binding for WCSSAON reset required for Q6v5 reset on IPQ8074 SoC. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V -Acked-by: Rob Herring -Acked-by: Stephen Boyd ---- - include/dt-bindings/clock/qcom,gcc-ipq8074.h | 1 + - 1 file changed, 1 insertion(+) - ---- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h -+++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h -@@ -381,6 +381,7 @@ - #define GCC_NSSPORT4_RESET 143 - #define GCC_NSSPORT5_RESET 144 - #define GCC_NSSPORT6_RESET 145 -+#define GCC_WCSSAON_RESET 146 - - #define USB0_GDSC 0 - #define USB1_GDSC 1 diff --git a/target/linux/qualcommax/patches-6.12/0118-clk-qcom-Add-WCSSAON-reset.patch b/target/linux/qualcommax/patches-6.12/0118-clk-qcom-Add-WCSSAON-reset.patch deleted file mode 100644 index 7938b1823f3..00000000000 --- a/target/linux/qualcommax/patches-6.12/0118-clk-qcom-Add-WCSSAON-reset.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 43d9788f546d24df22d8ba3fcc2497d7ccc198f3 Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:11 +0530 -Subject: [PATCH] clk: qcom: Add WCSSAON reset - -Add WCSSAON reset required for Q6v5 on IPQ8074 SoC. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V -Acked-by: Stephen Boyd ---- - drivers/clk/qcom/gcc-ipq8074.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/clk/qcom/gcc-ipq8074.c -+++ b/drivers/clk/qcom/gcc-ipq8074.c -@@ -4712,6 +4712,7 @@ static const struct qcom_reset_map gcc_i - [GCC_NSSPORT4_RESET] = { .reg = 0x68014, .bitmask = BIT(27) | GENMASK(9, 8) }, - [GCC_NSSPORT5_RESET] = { .reg = 0x68014, .bitmask = BIT(28) | GENMASK(11, 10) }, - [GCC_NSSPORT6_RESET] = { .reg = 0x68014, .bitmask = BIT(29) | GENMASK(13, 12) }, -+ [GCC_WCSSAON_RESET] = { 0x59010, 0 }, - }; - - static struct gdsc *gcc_ipq8074_gdscs[] = { diff --git a/target/linux/qualcommax/patches-6.12/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch b/target/linux/qualcommax/patches-6.12/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch deleted file mode 100644 index ccb78dec035..00000000000 --- a/target/linux/qualcommax/patches-6.12/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 406a332fd1bcc4e18d73cce390f56272fe9111d7 Mon Sep 17 00:00:00 2001 -From: Sivaprakash Murugesan -Date: Fri, 17 Apr 2020 16:37:10 +0530 -Subject: [PATCH] remoteproc: wcss: disable auto boot for IPQ8074 - -There is no need for remoteproc to boot automatically, ath11k will trigger -booting when its probing. - -Signed-off-by: Sivaprakash Murugesan -Signed-off-by: Robert Marko ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -162,6 +162,7 @@ struct wcss_data { - const struct rproc_ops *ops; - bool requires_force_stop; - bool need_mem_protection; -+ bool need_auto_boot; - }; - - static int q6v5_wcss_reset(struct q6v5_wcss *wcss) -@@ -1151,6 +1152,7 @@ static int q6v5_wcss_probe(struct platfo - desc->sysmon_name, - desc->ssctl_id); - -+ rproc->auto_boot = desc->need_auto_boot; - ret = rproc_add(rproc); - if (ret) - return ret; -@@ -1182,6 +1184,7 @@ static const struct wcss_data wcss_ipq80 - .ops = &q6v5_wcss_ipq8074_ops, - .requires_force_stop = true, - .need_mem_protection = true, -+ .need_auto_boot = false, - }; - - static const struct wcss_data wcss_qcs404_res_init = { -@@ -1198,6 +1201,7 @@ static const struct wcss_data wcss_qcs40 - .ssctl_id = 0x12, - .ops = &q6v5_wcss_qcs404_ops, - .requires_force_stop = false, -+ .need_auto_boot = true, - }; - - static const struct of_device_id q6v5_wcss_of_match[] = { diff --git a/target/linux/qualcommax/patches-6.12/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch b/target/linux/qualcommax/patches-6.12/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch deleted file mode 100644 index ea723f5912a..00000000000 --- a/target/linux/qualcommax/patches-6.12/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch +++ /dev/null @@ -1,120 +0,0 @@ -From 7388400b8bd42f71d040dbf2fdbdcb834fcc0ede Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Sat, 30 Jan 2021 10:50:13 +0530 -Subject: [PATCH] arm64: dts: qcom: Enable Q6v5 WCSS for ipq8074 SoC - -Enable remoteproc WCSS PIL driver with glink and ssr subdevices. -Also enables smp2p and mailboxes required for IPC. - -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: Sricharan R -Signed-off-by: Nikhil Prakash V -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 81 +++++++++++++++++++++++++++ - 1 file changed, 81 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -142,6 +142,32 @@ - }; - }; - -+ wcss: smp2p-wcss { -+ compatible = "qcom,smp2p"; -+ qcom,smem = <435>, <428>; -+ -+ interrupt-parent = <&intc>; -+ interrupts = <0 322 1>; -+ -+ mboxes = <&apcs_glb 9>; -+ -+ qcom,local-pid = <0>; -+ qcom,remote-pid = <1>; -+ -+ wcss_smp2p_out: master-kernel { -+ qcom,entry-name = "master-kernel"; -+ qcom,smp2p-feature-ssr-ack; -+ #qcom,smem-state-cells = <1>; -+ }; -+ -+ wcss_smp2p_in: slave-kernel { -+ qcom,entry-name = "slave-kernel"; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ }; -+ - soc: soc@0 { - #address-cells = <1>; - #size-cells = <1>; -@@ -415,6 +441,11 @@ - reg = <0x01937000 0x21000>; - }; - -+ tcsr_q6: syscon@1945000 { -+ compatible = "syscon"; -+ reg = <0x01945000 0xe000>; -+ }; -+ - spmi_bus: spmi@200f000 { - compatible = "qcom,spmi-pmic-arb"; - reg = <0x0200f000 0x001000>, -@@ -994,6 +1025,56 @@ - ranges; - }; - }; -+ -+ q6v5_wcss: q6v5_wcss@cd00000 { -+ compatible = "qcom,ipq8074-wcss-pil"; -+ reg = <0x0cd00000 0x4040>, -+ <0x004ab000 0x20>; -+ reg-names = "qdsp6", -+ "rmb"; -+ qca,auto-restart; -+ qca,extended-intc; -+ interrupts-extended = <&intc 0 325 1>, -+ <&wcss_smp2p_in 0 0>, -+ <&wcss_smp2p_in 1 0>, -+ <&wcss_smp2p_in 2 0>, -+ <&wcss_smp2p_in 3 0>; -+ interrupt-names = "wdog", -+ "fatal", -+ "ready", -+ "handover", -+ "stop-ack"; -+ -+ resets = <&gcc GCC_WCSSAON_RESET>, -+ <&gcc GCC_WCSS_BCR>, -+ <&gcc GCC_WCSS_Q6_BCR>; -+ -+ reset-names = "wcss_aon_reset", -+ "wcss_reset", -+ "wcss_q6_reset"; -+ -+ clocks = <&gcc GCC_PRNG_AHB_CLK>; -+ clock-names = "prng"; -+ -+ qcom,halt-regs = <&tcsr_q6 0xa000 0xd000 0x0>; -+ -+ qcom,smem-states = <&wcss_smp2p_out 0>, -+ <&wcss_smp2p_out 1>; -+ qcom,smem-state-names = "shutdown", -+ "stop"; -+ -+ memory-region = <&q6_region>; -+ -+ glink-edge { -+ interrupts = ; -+ qcom,remote-pid = <1>; -+ mboxes = <&apcs_glb 8>; -+ -+ rpm_requests { -+ qcom,glink-channels = "IPCRTR"; -+ }; -+ }; -+ }; - }; - - timer { diff --git a/target/linux/qualcommax/patches-6.12/0121-arm64-dts-ipq8074-Add-WLAN-node.patch b/target/linux/qualcommax/patches-6.12/0121-arm64-dts-ipq8074-Add-WLAN-node.patch deleted file mode 100644 index dcab3294c7f..00000000000 --- a/target/linux/qualcommax/patches-6.12/0121-arm64-dts-ipq8074-Add-WLAN-node.patch +++ /dev/null @@ -1,135 +0,0 @@ -From a67d1901741c162645eda0dbdc3a2c0c2aff5cf4 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Tue, 21 Dec 2021 14:49:36 +0100 -Subject: [PATCH] arm64: dts: ipq8074: Add WLAN node - -IPQ8074 has a AHB based Q6v5 802.11ax radios that are supported -by the ath11k. - -Add the required DT node to enable the built-in radios. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 111 ++++++++++++++++++++++++++ - 1 file changed, 111 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -1075,6 +1075,117 @@ - }; - }; - }; -+ -+ wifi: wifi@c0000000 { -+ compatible = "qcom,ipq8074-wifi"; -+ reg = <0xc000000 0x2000000>; -+ -+ interrupts = , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ ; -+ -+ interrupt-names = "misc-pulse1", -+ "misc-latch", -+ "sw-exception", -+ "ce0", -+ "ce1", -+ "ce2", -+ "ce3", -+ "ce4", -+ "ce5", -+ "ce6", -+ "ce7", -+ "ce8", -+ "ce9", -+ "ce10", -+ "ce11", -+ "host2wbm-desc-feed", -+ "host2reo-re-injection", -+ "host2reo-command", -+ "host2rxdma-monitor-ring3", -+ "host2rxdma-monitor-ring2", -+ "host2rxdma-monitor-ring1", -+ "reo2ost-exception", -+ "wbm2host-rx-release", -+ "reo2host-status", -+ "reo2host-destination-ring4", -+ "reo2host-destination-ring3", -+ "reo2host-destination-ring2", -+ "reo2host-destination-ring1", -+ "rxdma2host-monitor-destination-mac3", -+ "rxdma2host-monitor-destination-mac2", -+ "rxdma2host-monitor-destination-mac1", -+ "ppdu-end-interrupts-mac3", -+ "ppdu-end-interrupts-mac2", -+ "ppdu-end-interrupts-mac1", -+ "rxdma2host-monitor-status-ring-mac3", -+ "rxdma2host-monitor-status-ring-mac2", -+ "rxdma2host-monitor-status-ring-mac1", -+ "host2rxdma-host-buf-ring-mac3", -+ "host2rxdma-host-buf-ring-mac2", -+ "host2rxdma-host-buf-ring-mac1", -+ "rxdma2host-destination-ring-mac3", -+ "rxdma2host-destination-ring-mac2", -+ "rxdma2host-destination-ring-mac1", -+ "host2tcl-input-ring4", -+ "host2tcl-input-ring3", -+ "host2tcl-input-ring2", -+ "host2tcl-input-ring1", -+ "wbm2host-tx-completions-ring3", -+ "wbm2host-tx-completions-ring2", -+ "wbm2host-tx-completions-ring1", -+ "tcl2host-status-ring"; -+ qcom,rproc = <&q6v5_wcss>; -+ status = "disabled"; -+ }; - }; - - timer { diff --git a/target/linux/qualcommax/patches-6.12/0122-arm64-dts-ipq8074-add-CPU-clock.patch b/target/linux/qualcommax/patches-6.12/0122-arm64-dts-ipq8074-add-CPU-clock.patch deleted file mode 100644 index a6265ff0068..00000000000 --- a/target/linux/qualcommax/patches-6.12/0122-arm64-dts-ipq8074-add-CPU-clock.patch +++ /dev/null @@ -1,59 +0,0 @@ -From cb3ef99c1553565e1dc0301ccd5c1c0fa2d15c15 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 31 Dec 2021 17:56:14 +0100 -Subject: [PATCH] arm64: dts: ipq8074: add CPU clock - -Now that CPU clock is exposed and can be controlled, add the necessary -properties to the CPU nodes. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 9 +++++++++ - 1 file changed, 9 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -5,6 +5,7 @@ - - #include - #include -+#include - - / { - #address-cells = <2>; -@@ -38,6 +39,8 @@ - reg = <0x0>; - next-level-cache = <&l2_0>; - enable-method = "psci"; -+ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; -+ clock-names = "cpu"; - }; - - cpu1: cpu@1 { -@@ -46,6 +49,8 @@ - enable-method = "psci"; - reg = <0x1>; - next-level-cache = <&l2_0>; -+ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; -+ clock-names = "cpu"; - }; - - cpu2: cpu@2 { -@@ -54,6 +59,8 @@ - enable-method = "psci"; - reg = <0x2>; - next-level-cache = <&l2_0>; -+ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; -+ clock-names = "cpu"; - }; - - cpu3: cpu@3 { -@@ -62,6 +69,8 @@ - enable-method = "psci"; - reg = <0x3>; - next-level-cache = <&l2_0>; -+ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; -+ clock-names = "cpu"; - }; - - l2_0: l2-cache { diff --git a/target/linux/qualcommax/patches-6.12/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch b/target/linux/qualcommax/patches-6.12/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch deleted file mode 100644 index bea358045fa..00000000000 --- a/target/linux/qualcommax/patches-6.12/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 347ca56e86c99021fad059b9a8ef101245b8507e Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 31 Dec 2021 20:38:06 +0100 -Subject: [PATCH] arm64: dts: ipq8074: add cooling cells to CPU nodes - -Since there is CPU Freq support as well as thermal sensor support -now for the IPQ8074, add cooling cells to CPU nodes so that they can -be used as cooling devices using CPU Freq. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -41,6 +41,7 @@ - enable-method = "psci"; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; -+ #cooling-cells = <2>; - }; - - cpu1: cpu@1 { -@@ -51,6 +52,7 @@ - next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; -+ #cooling-cells = <2>; - }; - - cpu2: cpu@2 { -@@ -61,6 +63,7 @@ - next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; -+ #cooling-cells = <2>; - }; - - cpu3: cpu@3 { -@@ -71,6 +74,7 @@ - next-level-cache = <&l2_0>; - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; -+ #cooling-cells = <2>; - }; - - l2_0: l2-cache { diff --git a/target/linux/qualcommax/patches-6.12/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch b/target/linux/qualcommax/patches-6.12/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch deleted file mode 100644 index a9ac0293b19..00000000000 --- a/target/linux/qualcommax/patches-6.12/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch +++ /dev/null @@ -1,121 +0,0 @@ -From 04d2fc6a551bbd972a6428059b45ce79cb9de9d7 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 6 May 2022 22:38:24 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq8074: add QFPROM fuses - -Add the QFPROM node and CPR fuses. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 107 ++++++++++++++++++++++++++ - 1 file changed, 107 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -326,6 +326,106 @@ - reg = <0x000a4000 0x2000>; - #address-cells = <1>; - #size-cells = <1>; -+ -+ cpr_efuse_speedbin: speedbin@125 { -+ reg = <0x125 0x1>; -+ bits = <0 3>; -+ }; -+ -+ cpr_efuse_boost_cfg: boost_cfg@125 { -+ reg = <0x125 0x1>; -+ bits = <3 3>; -+ }; -+ -+ cpr_efuse_misc_volt_adj: misc_volt_adj@125 { -+ reg = <0x125 0x1>; -+ bits = <3 3>; -+ }; -+ -+ cpr_efuse_boost_volt: boost_volt@126 { -+ reg = <0x126 0x1>; -+ bits = <6 1>; -+ }; -+ -+ cpr_efuse_revision: revision@23e { -+ reg = <0x23e 0x1>; -+ bits = <5 3>; -+ }; -+ -+ cpr_efuse_ro_sel0: rosel0@249 { -+ reg = <0x249 0x1>; -+ bits = <0 4>; -+ }; -+ -+ cpr_efuse_ro_sel1: rosel1@248 { -+ reg = <0x248 0x1>; -+ bits = <4 4>; -+ }; -+ -+ cpr_efuse_ro_sel2: rosel2@248 { -+ reg = <0x248 0x2>; -+ bits = <0 4>; -+ }; -+ -+ cpr_efuse_ro_sel3: rosel3@249 { -+ reg = <0x249 0x1>; -+ bits = <4 4>; -+ }; -+ -+ cpr_efuse_init_voltage0: ivoltage0@23a { -+ reg = <0x23a 0x1>; -+ bits = <2 6>; -+ }; -+ -+ cpr_efuse_init_voltage1: ivoltage1@239 { -+ reg = <0x239 0x2>; -+ bits = <4 6>; -+ }; -+ -+ cpr_efuse_init_voltage2: ivoltage2@238 { -+ reg = <0x238 0x2>; -+ bits = <6 6>; -+ }; -+ -+ cpr_efuse_init_voltage3: ivoltage3@238 { -+ reg = <0x238 0x1>; -+ bits = <0 6>; -+ }; -+ -+ cpr_efuse_quot0: quot0@244 { -+ reg = <0x244 0x2>; -+ bits = <0 12>; -+ }; -+ -+ cpr_efuse_quot1: quot1@242 { -+ reg = <0x242 0x2>; -+ bits = <4 12>; -+ }; -+ -+ cpr_efuse_quot2: quot2@241 { -+ reg = <0x241 0x2>; -+ bits = <0 12>; -+ }; -+ -+ cpr_efuse_quot3: quot3@245 { -+ reg = <0x245 0x2>; -+ bits = <4 12>; -+ }; -+ -+ cpr_efuse_quot0_offset: quot0_offset@23d { -+ reg = <0x23d 0x2>; -+ bits = <6 7>; -+ }; -+ -+ cpr_efuse_quot1_offset: quot1_offset@23c { -+ reg = <0x23c 0x2>; -+ bits = <7 7>; -+ }; -+ -+ cpr_efuse_quot2_offset: quot2_offset@23c { -+ reg = <0x23c 0x1>; -+ bits = <0 7>; -+ }; - }; - - prng: rng@e3000 { diff --git a/target/linux/qualcommax/patches-6.12/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch b/target/linux/qualcommax/patches-6.12/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch deleted file mode 100644 index 9de502c4c36..00000000000 --- a/target/linux/qualcommax/patches-6.12/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch +++ /dev/null @@ -1,102 +0,0 @@ -From a20c4e8738a00087aa5d53fe5148ed484e23d229 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sat, 31 Dec 2022 13:56:26 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074: add CPU OPP table - -Now that there is NVMEM CPUFreq support for IPQ8074, we can add the OPP -table for SoC. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 52 +++++++++++++++++++++++++++ - 1 file changed, 52 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -42,6 +42,7 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - #cooling-cells = <2>; -+ operating-points-v2 = <&cpu_opp_table>; - }; - - cpu1: cpu@1 { -@@ -53,6 +54,7 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - #cooling-cells = <2>; -+ operating-points-v2 = <&cpu_opp_table>; - }; - - cpu2: cpu@2 { -@@ -64,6 +66,7 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - #cooling-cells = <2>; -+ operating-points-v2 = <&cpu_opp_table>; - }; - - cpu3: cpu@3 { -@@ -75,6 +78,7 @@ - clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; - clock-names = "cpu"; - #cooling-cells = <2>; -+ operating-points-v2 = <&cpu_opp_table>; - }; - - l2_0: l2-cache { -@@ -84,6 +88,54 @@ - }; - }; - -+ cpu_opp_table: opp-table { -+ compatible = "operating-points-v2-kryo-cpu"; -+ nvmem-cells = <&cpr_efuse_speedbin>; -+ opp-shared; -+ -+ opp-1017600000 { -+ opp-hz = /bits/ 64 <1017600000>; -+ opp-microvolt = <1>; -+ opp-supported-hw = <0xf>; -+ clock-latency-ns = <200000>; -+ }; -+ -+ opp-1382400000 { -+ opp-hz = /bits/ 64 <1382400000>; -+ opp-microvolt = <2>; -+ opp-supported-hw = <0xf>; -+ clock-latency-ns = <200000>; -+ }; -+ -+ opp-1651200000 { -+ opp-hz = /bits/ 64 <1651200000>; -+ opp-microvolt = <3>; -+ opp-supported-hw = <0x1>; -+ clock-latency-ns = <200000>; -+ }; -+ -+ opp-1843200000 { -+ opp-hz = /bits/ 64 <1843200000>; -+ opp-microvolt = <4>; -+ opp-supported-hw = <0x1>; -+ clock-latency-ns = <200000>; -+ }; -+ -+ opp-1920000000 { -+ opp-hz = /bits/ 64 <1920000000>; -+ opp-microvolt = <5>; -+ opp-supported-hw = <0x1>; -+ clock-latency-ns = <200000>; -+ }; -+ -+ opp-2208000000 { -+ opp-hz = /bits/ 64 <2208000000>; -+ opp-microvolt = <6>; -+ opp-supported-hw = <0x1>; -+ clock-latency-ns = <200000>; -+ }; -+ }; -+ - pmu { - compatible = "arm,cortex-a53-pmu"; - interrupts = ; diff --git a/target/linux/qualcommax/patches-6.12/0135-arm64-dts-qcom-ipq6018-add-NSS-reserved-memory.patch b/target/linux/qualcommax/patches-6.12/0135-arm64-dts-qcom-ipq6018-add-NSS-reserved-memory.patch deleted file mode 100644 index 0d53531bc91..00000000000 --- a/target/linux/qualcommax/patches-6.12/0135-arm64-dts-qcom-ipq6018-add-NSS-reserved-memory.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 7e102b1eb2ca3eff7a6f33ebeab17825e6f70956 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 4 Nov 2024 22:01:24 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq6018: add NSS reserved memory - -It seems that despite NSS not being supported in OpenWrt the memory it -usually uses needs to be reserved anyway for stability reasons. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -194,6 +194,11 @@ - no-map; - }; - -+ nss_region: memory@40000000 { -+ reg = <0x0 0x40000000 0x0 0x01000000>; -+ no-map; -+ }; -+ - bootloader@4a100000 { - reg = <0x0 0x4a100000 0x0 0x400000>; - no-map; diff --git a/target/linux/qualcommax/patches-6.12/0136-remoteproc-qcom-wcss-populate-driver-data-for-IPQ601.patch b/target/linux/qualcommax/patches-6.12/0136-remoteproc-qcom-wcss-populate-driver-data-for-IPQ601.patch deleted file mode 100644 index b1175d48e10..00000000000 --- a/target/linux/qualcommax/patches-6.12/0136-remoteproc-qcom-wcss-populate-driver-data-for-IPQ601.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 9dd19a9ae36bc60d58287d0c52e53024d484e64d Mon Sep 17 00:00:00 2001 -From: Gokul Sriram Palanisamy -Date: Fri, 29 Jan 2021 22:41:59 +0530 -Subject: [PATCH 2/3] remoteproc: qcom: wcss: populate driver data for IPQ6018 - -Populate hardcoded param using driver data for IPQ6018 SoCs. - -Signed-off-by: Gokul Sriram Palanisamy ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 19 +++++++++++++++++-- - 1 file changed, 17 insertions(+), 2 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -970,7 +970,7 @@ static int q6v5_alloc_memory_region(stru - return 0; - } - --static int ipq8074_init_clock(struct q6v5_wcss *wcss) -+static int ipq_init_clock(struct q6v5_wcss *wcss) - { - int ret; - -@@ -1173,7 +1173,7 @@ static void q6v5_wcss_remove(struct plat - } - - static const struct wcss_data wcss_ipq8074_res_init = { -- .init_clock = ipq8074_init_clock, -+ .init_clock = ipq_init_clock, - .q6_firmware_name = "IPQ8074/q6_fw.mdt", - .m3_firmware_name = "IPQ8074/m3_fw.mdt", - .crash_reason_smem = WCSS_CRASH_REASON, -@@ -1187,6 +1187,20 @@ static const struct wcss_data wcss_ipq80 - .need_auto_boot = false, - }; - -+static const struct wcss_data wcss_ipq6018_res_init = { -+ .init_clock = ipq_init_clock, -+ .q6_firmware_name = "IPQ6018/q6_fw.mdt", -+ .m3_firmware_name = "IPQ6018/m3_fw.mdt", -+ .crash_reason_smem = WCSS_CRASH_REASON, -+ .aon_reset_required = true, -+ .wcss_q6_reset_required = true, -+ .bcr_reset_required = false, -+ .ssr_name = "q6wcss", -+ .ops = &q6v5_wcss_ipq8074_ops, -+ .requires_force_stop = true, -+ .need_mem_protection = true, -+}; -+ - static const struct wcss_data wcss_qcs404_res_init = { - .init_clock = qcs404_init_clock, - .init_regulator = qcs404_init_regulator, -@@ -1206,6 +1220,7 @@ static const struct wcss_data wcss_qcs40 - - static const struct of_device_id q6v5_wcss_of_match[] = { - { .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init }, -+ { .compatible = "qcom,ipq6018-wcss-pil", .data = &wcss_ipq6018_res_init }, - { .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init }, - { }, - }; diff --git a/target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch b/target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch deleted file mode 100644 index 22016b474cc..00000000000 --- a/target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch +++ /dev/null @@ -1,1245 +0,0 @@ -From patchwork Thu Mar 27 18:17:50 2025 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: Sricharan Ramabadhran -X-Patchwork-Id: 14031335 -Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com - [205.220.180.131]) - (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) - (No client certificate requested) - by smtp.subspace.kernel.org (Postfix) with ESMTPS id B6FC4218EB3; - Thu, 27 Mar 2025 18:18:22 +0000 (UTC) -Authentication-Results: smtp.subspace.kernel.org; - arc=none smtp.client-ip=205.220.180.131 -ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; - t=1743099505; cv=none; - b=EIGqvUFVdX2yAV1X5BSy4Vd6avjUSpDkQyVdP3vFK5RB8T9Eoxto8HY1cWSbjWATbbmUQdxpIqDsTwpW93ZxX2u+xR66TEfrq53Mz7WXezAMNj+c7sxKOvD1LuN/SwZlpLeymS3aYCTXQF9tlaX4Kchd/XG9PHCNNlj4J/HMLEU= -ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; - s=arc-20240116; t=1743099505; c=relaxed/simple; - bh=1bC94iLOVn4uSZE28DCVE/Z1lxJpVgGYizWuGyj1fB4=; - h=From:To:Subject:Date:Message-ID:In-Reply-To:References: - MIME-Version:Content-Type; - b=Vd0k6dRvTlGuhPjBJy4f26hetu50FFAxHBHHvpt01PASSbUWn5QdLpfIBDf4hhOejN+OP/t5Y4zRcQ8ls9btkI0XMU7nqZw/h8kFFPlXO+VWPWCIVQo6DeM4rgWKPIskkdmi3YCmekJQ8mOvrnj0Z1B7840MW6GkQowDA1ijXyQ= -ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; - dmarc=pass (p=none dis=none) header.from=quicinc.com; - spf=pass smtp.mailfrom=quicinc.com; - dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com - header.b=nWsnWiiP; arc=none smtp.client-ip=205.220.180.131 -Authentication-Results: smtp.subspace.kernel.org; - dmarc=pass (p=none dis=none) header.from=quicinc.com -Authentication-Results: smtp.subspace.kernel.org; - spf=pass smtp.mailfrom=quicinc.com -Authentication-Results: smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com - header.b="nWsnWiiP" -Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) - by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id - 52RElAqC023840; - Thu, 27 Mar 2025 18:18:16 GMT -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= - content-transfer-encoding:content-type:date:from:in-reply-to - :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= - QkU6cfXx/Dr1uYdS2bfC4yKOb27hRI4xX4Mjt2GlxX0=; b=nWsnWiiPd2BhTQo2 - 8bvwRq7Eyr3yMh8LCCDfuk/SXkQbpNukzvr4A4R66oU3PmGnp5Y+Yeo0w09T3vue - 4JYS0hV8suACJbj1eqqWEu+sLl1Uns0BXlWaWmOCnRVIwh/bpA3XIfCi9Fy+rANI - aXzrjtC81dBCEuqIj+0J97gY2Tt/kGays9pGkqXp64pNSHDoVQ7ijZgQlosIAg4b - qnNh7nFqwkbdlodmMjTuAQk0RBEAf1C5Wbyu2SP+jmUdu1Ign7AZHlK8OQtHCeR5 - W1EPvlsTzXeaqCfYpgYZWI/kcO/WfBgjLFlrn4EG660ta9NShqlsNyHbuG0R5u8P - GiugpQ== -Received: from nasanppmta03.qualcomm.com (i-global254.qualcomm.com - [199.106.103.254]) - by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 45kyr9qahq-1 - (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); - Thu, 27 Mar 2025 18:18:16 +0000 (GMT) -Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com - [10.46.141.250]) - by NASANPPMTA03.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id - 52RIIFs2028891 - (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); - Thu, 27 Mar 2025 18:18:15 GMT -Received: from hu-srichara-blr.qualcomm.com (10.80.80.8) by - nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server - (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id - 15.2.1544.9; Thu, 27 Mar 2025 11:18:11 -0700 -From: Sricharan R -To: , , , - , , - , , - , , - , , - -Subject: [PATCH V4 2/2] mailbox: tmelite-qmp: Introduce TMEL QMP mailbox - driver -Date: Thu, 27 Mar 2025 23:47:50 +0530 -Message-ID: <20250327181750.3733881-3-quic_srichara@quicinc.com> -X-Mailer: git-send-email 2.34.1 -In-Reply-To: <20250327181750.3733881-1-quic_srichara@quicinc.com> -References: <20250327181750.3733881-1-quic_srichara@quicinc.com> -Precedence: bulk -X-Mailing-List: linux-arm-msm@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To - nasanex01b.na.qualcomm.com (10.46.141.250) -X-QCInternal: smtphost -X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 - signatures=585085 -X-Proofpoint-GUID: 0rO4P6gnHKFRp6byxTJdsYHwAuRh45Fp -X-Authority-Analysis: v=2.4 cv=UblRSLSN c=1 sm=1 tr=0 ts=67e59668 cx=c_pps - a=JYp8KDb2vCoCEuGobkYCKw==:117 a=JYp8KDb2vCoCEuGobkYCKw==:17 - a=GEpy-HfZoHoA:10 a=Vs1iUdzkB0EA:10 a=COk6AnOGAAAA:8 a=hOkhvlhF9aMxycy05ToA:9 - a=RVmHIydaz68A:10 - a=TjNXssC_j7lpFel5tvFf:22 -X-Proofpoint-ORIG-GUID: 0rO4P6gnHKFRp6byxTJdsYHwAuRh45Fp -X-Proofpoint-Virus-Version: vendor=baseguard - engine=ICAP:2.0.293,Aquarius:18.0.1095,Hydra:6.0.680,FMLib:17.12.68.34 - definitions=2025-03-27_03,2025-03-26_02,2024-11-22_01 -X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 - lowpriorityscore=0 - suspectscore=0 priorityscore=1501 phishscore=0 mlxscore=0 impostorscore=0 - adultscore=0 clxscore=1015 mlxlogscore=999 bulkscore=0 malwarescore=0 - spamscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound - adjust=0 reason=mlx scancount=1 engine=8.19.0-2502280000 - definitions=main-2503270125 - -From: Sricharan Ramabadhran - -This mailbox facilitates the communication between the TMEL server -subsystem (Trust Management Engine Lite) and the TMEL client -(APPSS/BTSS/AUDIOSS), used for secure services like secure image -authentication, enable/disable efuses, crypto services etc. Each client in -the SoC has its own block of message RAM and IRQ for communication with the -TMEL SS. The protocol used to communicate in the message RAM is known as -Qualcomm Messaging Protocol (QMP). - -Remote proc driver subscribes to this mailbox and uses the -mbox_send_message to use TMEL to securely authenticate/teardown the images. - -Signed-off-by: Sricharan Ramabadhran ---- -[V4] - Fixed TME-L naming in all places and expanded it. - Folded tmel_work in tmel. - Added more kernel doc as relevant. - Removed __packed in all places, as not required. - Renamed all functions to have tmel_ prefixes. - Used readl/writel in all places. - Added Inline for all required functions. - Removed redundant type conversions. - Removed redundant 'goto's - Added __free macro - Fixed Linux std errno in tmel_sec_boot_auth/teardown - Added spinlock in qmp_startup - Used of_mbox_index_xlate and dropped the tmel_qmp_mbox_xlate - Updated header file to have only mbox consumer required and moved rest to .c file - Fixed the TMEL_MSG macros to use standard GENMASK - Moved the irq registration to end of probe - - Following tests were done and no issues. - - *) Checkpatch - *) Codespell - *) Sparse - *) kerneldoc check - *) Kernel lock debugging - *) dt_binding_check and dtbs_check - -[V3] - - Fixed wrong code/comments wrappings. - Fixed Kconfig and Makefile entries to right place. - Removed unused headers inclusion. - Fixed locking, removed the mutexes and having only tx spinlock. - Removed the use of global ptr for tmel, made it as device specific. - Replaced pr_err/pr_debug with dev_err/dev_dbg in all places. - Fixed usage of dev_err_probe. - Fixed xlate callback as per comments. - Used devm equivalents and kcalloc version as per comments. - Removed all un-nessecary wrapper macros for register access, inlined it - as per comments. - Re-organised the function layout as per comments and make it more readable. - Removed the pictures in headers files as per comments. - Used Field_prep/get as per comments. - Fixed Kernel test reported issues. - Fixed all other comments as well. - - Following tests were done and no issues. - - *) Checkpatch - *) Codespell - *) Sparse - *) kerneldoc check - *) Kernel lock debugging - *) dt_binding_check and dtbs_check - -[v2] - Added HW description in the bindings patch. - Fixed review comments for bindings from Krzysztof and Dmitry - Changed patch#2 driver to add work for mailbox tx processing - Cleaned up patch#2 for some checkpatch warnings. - There are some checkpatch [CHECK] like below, which looks like false positive. - - CHECK: Macro argument 'm' may be better as '(m)' to avoid precedence issues - #1072: FILE: include/linux/mailbox/tmelcom-qmp.h:40: - +#define TMEL_MSG_UID_CREATE(m, a) ((u32)(((m & 0xff) << 8) | (a & 0xff))) - -[v1] - RFC Post - - drivers/mailbox/Kconfig | 10 + - drivers/mailbox/Makefile | 2 + - drivers/mailbox/qcom-tmel-qmp.c | 947 ++++++++++++++++++++++++++++ - include/linux/mailbox/tmelcom-qmp.h | 65 ++ - 4 files changed, 1024 insertions(+) - create mode 100644 drivers/mailbox/qcom-tmel-qmp.c - create mode 100644 include/linux/mailbox/tmelcom-qmp.h - ---- a/drivers/mailbox/Kconfig -+++ b/drivers/mailbox/Kconfig -@@ -295,4 +295,14 @@ config QCOM_IPCC - acts as an interrupt controller for receiving interrupts from clients. - Say Y here if you want to build this driver. - -+config QCOM_TMEL_QMP_MAILBOX -+ tristate "QCOM Mailbox Protocol(QMP) for TMEL SS" -+ depends on ARCH_QCOM || COMPILE_TEST -+ help -+ Say yes to add support for the QMP Mailbox Protocol driver for -+ Trust Management Engine Lite Sub System (TMEL SS). -+ QMP is a lightweight communication protocol for sending messages to -+ TMEL. This protocol fits into the Generic Mailbox Framework. -+ QMP uses a mailbox registers. -+ - endif ---- a/drivers/mailbox/Makefile -+++ b/drivers/mailbox/Makefile -@@ -47,6 +47,8 @@ obj-$(CONFIG_POLARFIRE_SOC_MAILBOX) += m - - obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o - -+obj-$(CONFIG_QCOM_TMEL_QMP_MAILBOX) += qcom-tmel-qmp.o -+ - obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o - - obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o ---- /dev/null -+++ b/drivers/mailbox/qcom-tmel-qmp.c -@@ -0,0 +1,947 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define QMP_NUM_CHANS 0x1 -+#define QMP_TOUT_MS 1000 -+#define QMP_CTRL_DATA_SIZE 4 -+#define QMP_MAX_PKT_SIZE 0x18 -+#define QMP_UCORE_DESC_OFFSET 0x1000 -+#define QMP_SEND_TIMEOUT 30000 -+ -+#define QMP_HW_MBOX_SIZE 32 -+#define QMP_MBOX_RSV_SIZE 4 -+#define QMP_MBOX_IPC_PACKET_SIZE (QMP_HW_MBOX_SIZE - QMP_CTRL_DATA_SIZE - QMP_MBOX_RSV_SIZE) -+#define QMP_MBOX_IPC_MAX_PARAMS 5 -+ -+#define QMP_MAX_PARAM_IN_PARAM_ID 14 -+#define QMP_PARAM_CNT_FOR_OUTBUF 3 -+#define QMP_SRAM_IPC_MAX_PARAMS (QMP_MAX_PARAM_IN_PARAM_ID * QMP_PARAM_CNT_FOR_OUTBUF) -+#define QMP_SRAM_IPC_MAX_BUF_SIZE (QMP_SRAM_IPC_MAX_PARAMS * sizeof(u32)) -+ -+#define TMEL_ERROR_GENERIC (0x1u) -+#define TMEL_ERROR_NOT_SUPPORTED (0x2u) -+#define TMEL_ERROR_BAD_PARAMETER (0x3u) -+#define TMEL_ERROR_BAD_MESSAGE (0x4u) -+#define TMEL_ERROR_BAD_ADDRESS (0x5u) -+#define TMEL_ERROR_TMELCOM_FAILURE (0x6u) -+#define TMEL_ERROR_TMEL_BUSY (0x7u) -+ -+/* -+ * mbox data can be shared over mem or sram -+ */ -+enum ipc_type { -+ IPC_MBOX_MEM, -+ IPC_MBOX_SRAM, -+}; -+ -+/* -+ * mbox header indicates the type of payload and action required. -+ */ -+struct ipc_header { -+ u8 ipc_type:1; -+ u8 msg_len:7; -+ u8 msg_type; -+ u8 action_id; -+ s8 response; -+}; -+ -+struct mbox_payload { -+ u32 param[QMP_MBOX_IPC_MAX_PARAMS]; -+}; -+ -+struct sram_payload { -+ u32 payload_ptr; -+ u32 payload_len; -+}; -+ -+union ipc_payload { -+ struct mbox_payload mbox_payload; -+ struct sram_payload sram_payload; -+}; -+ -+struct tmel_ipc_pkt { -+ struct ipc_header msg_hdr; -+ union ipc_payload payload; -+}; -+ -+/** -+ * enum qmp_local_state - definition of the local state machine -+ * @LINK_DISCONNECTED: Init state, waiting for ucore to start -+ * @LINK_NEGOTIATION: Set local link state to up, wait for ucore ack -+ * @LINK_CONNECTED: Link state up, channel not connected -+ * @LOCAL_CONNECTING: Channel opening locally, wait for ucore ack -+ * @CHANNEL_CONNECTED: Channel fully opened -+ * @LOCAL_DISCONNECTING: Channel disconnected locally, wait for ucore ack -+ */ -+enum qmp_local_state { -+ LINK_DISCONNECTED, -+ LINK_NEGOTIATION, -+ LINK_CONNECTED, -+ LOCAL_CONNECTING, -+ CHANNEL_CONNECTED, -+ LOCAL_DISCONNECTING, -+}; -+ -+/** -+ * struct qmp_channel_desc - IPC bits -+ * @bits: Var to access each member -+ * @val: u32 representation of above -+ */ -+union qmp_channel_desc { -+ struct { -+ u32 link_state:1; -+ u32 link_state_ack:1; -+ u32 ch_state:1; -+ u32 ch_state_ack:1; -+ u32 tx:1; -+ u32 tx_ack:1; -+ u32 rx_done:1; -+ u32 rx_done_ack:1; -+ u32 reserved:8; -+ u32 frag_size:8; -+ u32 rem_frag_count:8; -+ } bits; -+ unsigned int val; -+}; -+ -+/** -+ * struct qmp_device - local information for managing a single mailbox -+ * @dev: The device that corresponds to this mailbox -+ * @mcore_desc: Local core (APSS) mailbox descriptor -+ * @ucore_desc: Remote core (TME-L) mailbox descriptor -+ * @mcore: Local core (APSS) channel descriptor -+ * @ucore: Remote core (TME-L) channel descriptor -+ * @rx_pkt: Buffer to pass to client, holds received data from mailbox -+ * @mbox_client: Mailbox client for the IPC interrupt -+ * @mbox_chan: Mailbox client chan for the IPC interrupt -+ * @local_state: Current state of mailbox protocol -+ * @tx_lock: Serialize access for writes to mailbox -+ * @link_complete: Use to block until link negotiation with remote proc -+ * @ch_complete: Use to block until the channel is fully opened -+ * @tx_sent: True if tx is sent and remote proc has not sent ack -+ */ -+struct qmp_device { -+ struct device *dev; -+ -+ void __iomem *mcore_desc; -+ void __iomem *ucore_desc; -+ union qmp_channel_desc mcore; -+ union qmp_channel_desc ucore; -+ -+ struct kvec rx_pkt; -+ -+ struct mbox_client mbox_client; -+ struct mbox_chan *mbox_chan; -+ -+ enum qmp_local_state local_state; -+ -+ /* -+ * Serialize access to mcore IPC descriptors. -+ * mcore refers to the IPC request descriptors sent to TMEL, -+ * protecting it from various SM transitions using this. -+ */ -+ spinlock_t tx_lock; -+ -+ struct completion link_complete; -+ struct completion ch_complete; -+ -+ atomic_t tx_sent; -+}; -+ -+/** -+ * struct tmel - tmel controller instance -+ * @dev: The device that corresponds to this mailbox -+ * @ctrl: Mailbox controller for use by tmel clients -+ * @mdev: qmp_device associated with this tmel instance -+ * @pkt: Buffer from client, to be sent over mailbox -+ * @ipc_pkt: wrapper used for prepare/un_prepare -+ * @sram_dma_addr: mailbox sram address to copy the data -+ * @waitq: Use to wait for posted messages completion -+ * @rx_done: Use to indicate receive completion from remote -+ * @twork: worker for posting the client req to tmel ctrl -+ * @data: client data to be sent for the current request -+ */ -+struct tmel { -+ struct device *dev; -+ struct mbox_controller ctrl; -+ struct qmp_device *mdev; -+ struct kvec pkt; -+ struct tmel_ipc_pkt *ipc_pkt; -+ dma_addr_t sram_dma_addr; -+ wait_queue_head_t waitq; -+ bool rx_done; -+ struct work_struct twork; -+ void *data; -+}; -+ -+struct tmel_msg_param_type_buf_in { -+ u32 buf; -+ u32 buf_len; -+}; -+ -+struct tmel_secboot_sec_auth_req { -+ u32 sw_id; -+ struct tmel_msg_param_type_buf_in elf_buf; -+ struct tmel_msg_param_type_buf_in region_list; -+ u32 relocate; -+}; -+ -+struct tmel_secboot_sec_auth_resp { -+ u32 first_seg_addr; -+ u32 first_seg_len; -+ u32 entry_addr; -+ u32 extended_error; -+ u32 status; -+}; -+ -+struct tmel_secboot_sec_auth { -+ struct tmel_secboot_sec_auth_req req; -+ struct tmel_secboot_sec_auth_resp resp; -+}; -+ -+struct tmel_secboot_sec { -+ struct device *dev; -+ void *elf_buf; -+ struct tmel_secboot_sec_auth msg; -+}; -+ -+struct tmel_secboot_teardown_req { -+ u32 sw_id; -+ u32 secondary_sw_id; -+}; -+ -+struct tmel_secboot_teardown_resp { -+ u32 status; -+}; -+ -+struct tmel_secboot_teardown { -+ struct tmel_secboot_teardown_req req; -+ struct tmel_secboot_teardown_resp resp; -+}; -+ -+/** -+ * tmel_qmp_send_irq() - send an irq to a remote entity as an event signal. -+ * @mdev: Which remote entity that should receive the irq. -+ */ -+static inline void tmel_qmp_send_irq(struct qmp_device *mdev) -+{ -+ writel(mdev->mcore.val, mdev->mcore_desc); -+ /* Ensure desc update is visible before IPC */ -+ wmb(); -+ -+ dev_dbg(mdev->dev, "%s: mcore 0x%x ucore 0x%x", __func__, -+ mdev->mcore.val, mdev->ucore.val); -+ -+ mbox_send_message(mdev->mbox_chan, NULL); -+ mbox_client_txdone(mdev->mbox_chan, 0); -+} -+ -+/** -+ * tmel_qmp_send_data() - Send the data to remote and notify. -+ * @mdev: qmp_device to send the data to. -+ * @data: Data to be sent to remote processor, should be in the format of -+ * a kvec. -+ * -+ * Copy the data to the channel's mailbox and notify remote subsystem of new -+ * data. This function will return an error if the previous message sent has -+ * not been read. -+ */ -+static inline int tmel_qmp_send_data(struct qmp_device *mdev, void *data) -+{ -+ struct kvec *pkt = (struct kvec *)data; -+ void __iomem *addr; -+ unsigned long flags; -+ -+ if (pkt->iov_len > QMP_MAX_PKT_SIZE) { -+ dev_err(mdev->dev, "Unsupported packet size"); -+ return -EINVAL; -+ } -+ -+ if (atomic_read(&mdev->tx_sent)) -+ return -EAGAIN; -+ -+ dev_dbg(mdev->dev, "%s: mcore 0x%x ucore 0x%x", __func__, -+ mdev->mcore.val, mdev->ucore.val); -+ -+ addr = mdev->mcore_desc + QMP_CTRL_DATA_SIZE; -+ memcpy_toio(addr, pkt->iov_base, pkt->iov_len); -+ -+ mdev->mcore.bits.frag_size = pkt->iov_len; -+ mdev->mcore.bits.rem_frag_count = 0; -+ -+ dev_dbg(mdev->dev, "Copied buffer to mbox, sz: %d", -+ mdev->mcore.bits.frag_size); -+ -+ atomic_set(&mdev->tx_sent, 1); -+ -+ spin_lock_irqsave(&mdev->tx_lock, flags); -+ mdev->mcore.bits.tx = !(mdev->mcore.bits.tx); -+ tmel_qmp_send_irq(mdev); -+ spin_unlock_irqrestore(&mdev->tx_lock, flags); -+ -+ return 0; -+} -+ -+/** -+ * tmel_qmp_notify_client() - Notify the tmel client about remote data. -+ * @tdev: tmel device to notify. -+ * @message: response pkt from remote processor, should be in format of kvec. -+ * -+ * Wakeup the clients after receiving data from the remote. -+ */ -+static inline void tmel_qmp_notify_client(struct tmel *tdev, void *message) -+{ -+ struct kvec *pkt = NULL; -+ -+ if (!message) { -+ dev_err(tdev->dev, "spurious message received\n"); -+ goto notify_fail; -+ } -+ -+ if (tdev->rx_done) { -+ dev_err(tdev->dev, "tmel response pending\n"); -+ goto notify_fail; -+ } -+ -+ pkt = (struct kvec *)message; -+ tdev->pkt.iov_len = pkt->iov_len; -+ tdev->pkt.iov_base = pkt->iov_base; -+ tdev->rx_done = true; -+ -+notify_fail: -+ wake_up_interruptible(&tdev->waitq); -+} -+ -+/** -+ * tmel_qmp_recv_data() - Receive data and send ack. -+ * @tdev: tmel device that received the notification. -+ * @mbox_of: offset of mailbox after QMP Control data. -+ * -+ * Copies data from mailbox and passes to the client upon receiving data -+ * available notification. Also acknowledges the read completion. -+ */ -+static inline void tmel_qmp_recv_data(struct tmel *tdev, u32 mbox_of) -+{ -+ struct qmp_device *mdev = tdev->mdev; -+ void __iomem *addr; -+ struct kvec *pkt; -+ -+ addr = mdev->ucore_desc + mbox_of; -+ pkt = &mdev->rx_pkt; -+ pkt->iov_len = mdev->ucore.bits.frag_size; -+ -+ memcpy_fromio(pkt->iov_base, addr, pkt->iov_len); -+ mdev->mcore.bits.tx_ack = mdev->ucore.bits.tx; -+ dev_dbg(mdev->dev, "%s: Send RX data to TMEL Client", __func__); -+ tmel_qmp_notify_client(tdev, pkt); -+ -+ mdev->mcore.bits.rx_done = !(mdev->mcore.bits.rx_done); -+ tmel_qmp_send_irq(mdev); -+} -+ -+/** -+ * tmel_qmp_clr_mcore_ch_state() - Clear the mcore state of a mailbox. -+ * @mdev: mailbox device to be initialized. -+ */ -+static inline void tmel_qmp_clr_mcore_ch_state(struct qmp_device *mdev) -+{ -+ /* Clear all fields except link_state */ -+ mdev->mcore.bits.ch_state = 0; -+ mdev->mcore.bits.ch_state_ack = 0; -+ mdev->mcore.bits.tx = 0; -+ mdev->mcore.bits.tx_ack = 0; -+ mdev->mcore.bits.rx_done = 0; -+ mdev->mcore.bits.rx_done_ack = 0; -+ mdev->mcore.bits.frag_size = 0; -+ mdev->mcore.bits.rem_frag_count = 0; -+} -+ -+/** -+ * tmel_qmp_rx() - Handle incoming messages from remote processor. -+ * @tdev: tmel device to send the event to. -+ */ -+static inline void tmel_qmp_rx(struct tmel *tdev) -+{ -+ struct qmp_device *mdev = tdev->mdev; -+ unsigned long flags; -+ -+ /* read remote_desc from mailbox register */ -+ mdev->ucore.val = readl(mdev->ucore_desc); -+ -+ dev_dbg(mdev->dev, "%s: mcore 0x%x ucore 0x%x", __func__, -+ mdev->mcore.val, mdev->ucore.val); -+ -+ spin_lock_irqsave(&mdev->tx_lock, flags); -+ -+ /* Check if remote link down */ -+ if (mdev->local_state >= LINK_CONNECTED && -+ !(mdev->ucore.bits.link_state)) { -+ mdev->local_state = LINK_NEGOTIATION; -+ mdev->mcore.bits.link_state_ack = mdev->ucore.bits.link_state; -+ tmel_qmp_send_irq(mdev); -+ spin_unlock_irqrestore(&mdev->tx_lock, flags); -+ return; -+ } -+ -+ switch (mdev->local_state) { -+ case LINK_NEGOTIATION: -+ if (!(mdev->mcore.bits.link_state) || -+ !(mdev->ucore.bits.link_state)) { -+ dev_err(mdev->dev, "rx irq:link down state\n"); -+ break; -+ } -+ tmel_qmp_clr_mcore_ch_state(mdev); -+ mdev->mcore.bits.link_state_ack = mdev->ucore.bits.link_state; -+ mdev->local_state = LINK_CONNECTED; -+ complete_all(&mdev->link_complete); -+ dev_dbg(mdev->dev, "Set to link connected"); -+ break; -+ case LINK_CONNECTED: -+ /* No need to handle until local opens */ -+ break; -+ case LOCAL_CONNECTING: -+ /* Ack to remote ch_state change */ -+ mdev->mcore.bits.ch_state_ack = mdev->ucore.bits.ch_state; -+ mdev->local_state = CHANNEL_CONNECTED; -+ complete_all(&mdev->ch_complete); -+ dev_dbg(mdev->dev, "Set to channel connected"); -+ tmel_qmp_send_irq(mdev); -+ break; -+ case CHANNEL_CONNECTED: -+ /* Check for remote channel down */ -+ if (!(mdev->ucore.bits.ch_state)) { -+ mdev->local_state = LOCAL_CONNECTING; -+ mdev->mcore.bits.ch_state_ack = mdev->ucore.bits.ch_state; -+ dev_dbg(mdev->dev, "Remote Disconnect"); -+ tmel_qmp_send_irq(mdev); -+ } -+ -+ /* Check TX done */ -+ if (atomic_read(&mdev->tx_sent) && -+ mdev->ucore.bits.rx_done != mdev->mcore.bits.rx_done_ack) { -+ /* Ack to remote */ -+ mdev->mcore.bits.rx_done_ack = mdev->ucore.bits.rx_done; -+ atomic_set(&mdev->tx_sent, 0); -+ dev_dbg(mdev->dev, "TX flag cleared"); -+ } -+ -+ /* Check if remote is Transmitting */ -+ if (!(mdev->ucore.bits.tx != mdev->mcore.bits.tx_ack)) -+ break; -+ if (mdev->ucore.bits.frag_size == 0 || -+ mdev->ucore.bits.frag_size > QMP_MAX_PKT_SIZE) { -+ dev_err(mdev->dev, "Rx frag size error %d\n", -+ mdev->ucore.bits.frag_size); -+ break; -+ } -+ tmel_qmp_recv_data(tdev, QMP_CTRL_DATA_SIZE); -+ break; -+ case LOCAL_DISCONNECTING: -+ if (!(mdev->mcore.bits.ch_state)) { -+ tmel_qmp_clr_mcore_ch_state(mdev); -+ mdev->local_state = LINK_CONNECTED; -+ dev_dbg(mdev->dev, "Channel closed"); -+ reinit_completion(&mdev->ch_complete); -+ } -+ -+ break; -+ default: -+ dev_err(mdev->dev, "Local Channel State corrupted\n"); -+ } -+ spin_unlock_irqrestore(&mdev->tx_lock, flags); -+} -+ -+/** -+ * tmel_qmp_irq_handler() - Handle incoming messages from remote processor. -+ * @irq: ipc interrupt from remote -+ * @priv: ptr to the corresponding tmel device. -+ */ -+static inline irqreturn_t tmel_qmp_irq_handler(int irq, void *priv) -+{ -+ struct tmel *tdev = (struct tmel *)priv; -+ -+ tmel_qmp_rx(tdev); -+ -+ return IRQ_HANDLED; -+} -+ -+/** -+ * tmel_prepare_msg() - copies the payload to the mbox destination -+ * @tdev: the tmel device -+ * @msg_uid: msg_type/action_id combo -+ * @msg_buf: payload to be sent -+ * @msg_size: size of the payload -+ */ -+static inline int tmel_prepare_msg(struct tmel *tdev, u32 msg_uid, -+ void *msg_buf, size_t msg_size) -+{ -+ struct tmel_ipc_pkt *ipc_pkt = tdev->ipc_pkt; -+ struct ipc_header *msg_hdr = &ipc_pkt->msg_hdr; -+ struct mbox_payload *mbox_payload = &ipc_pkt->payload.mbox_payload; -+ struct sram_payload *sram_payload = &ipc_pkt->payload.sram_payload; -+ int ret; -+ -+ memset(ipc_pkt, 0, sizeof(struct tmel_ipc_pkt)); -+ -+ msg_hdr->msg_type = TMEL_MSG_UID_MSG_TYPE(msg_uid); -+ msg_hdr->action_id = TMEL_MSG_UID_ACTION_ID(msg_uid); -+ -+ dev_dbg(tdev->dev, "uid: %d, msg_size: %zu msg_type:%d, action_id:%d\n", -+ msg_uid, msg_size, msg_hdr->msg_type, msg_hdr->action_id); -+ -+ if (sizeof(struct ipc_header) + msg_size <= QMP_MBOX_IPC_PACKET_SIZE) { -+ /* Mbox only */ -+ msg_hdr->ipc_type = IPC_MBOX_MEM; -+ msg_hdr->msg_len = msg_size; -+ memcpy((void *)mbox_payload, msg_buf, msg_size); -+ } else if (msg_size <= QMP_SRAM_IPC_MAX_BUF_SIZE) { -+ /* SRAM */ -+ msg_hdr->ipc_type = IPC_MBOX_SRAM; -+ msg_hdr->msg_len = 8; -+ -+ tdev->sram_dma_addr = dma_map_single(tdev->dev, msg_buf, -+ msg_size, -+ DMA_BIDIRECTIONAL); -+ ret = dma_mapping_error(tdev->dev, tdev->sram_dma_addr); -+ if (ret) { -+ dev_err(tdev->dev, "SRAM DMA mapping error: %d\n", ret); -+ return ret; -+ } -+ -+ sram_payload->payload_ptr = tdev->sram_dma_addr; -+ sram_payload->payload_len = msg_size; -+ } else { -+ dev_err(tdev->dev, "Invalid payload length: %zu\n", msg_size); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * tmel_unprepare_message() - Get the response data back for client -+ * @tdev: the tmel device -+ * @msg_buf: payload to be sent -+ * @msg_size: size of the payload -+ */ -+static inline void tmel_unprepare_message(struct tmel *tdev, void *msg_buf, size_t msg_size) -+{ -+ struct tmel_ipc_pkt *ipc_pkt = (struct tmel_ipc_pkt *)tdev->pkt.iov_base; -+ struct mbox_payload *mbox_payload = &ipc_pkt->payload.mbox_payload; -+ -+ if (ipc_pkt->msg_hdr.ipc_type == IPC_MBOX_MEM) { -+ memcpy(msg_buf, mbox_payload, msg_size); -+ } else if (ipc_pkt->msg_hdr.ipc_type == IPC_MBOX_SRAM) { -+ dma_unmap_single(tdev->dev, tdev->sram_dma_addr, msg_size, DMA_BIDIRECTIONAL); -+ tdev->sram_dma_addr = 0; -+ } -+} -+ -+static inline bool tmel_rx_done(struct tmel *tdev) -+{ -+ return tdev->rx_done; -+} -+ -+/** -+ * tmel_process_request() - process client msg and wait for response -+ * @tdev: the tmel device -+ * @msg_uid: msg_type/action_id combo -+ * @msg_buf: payload to be sent -+ * @msg_size: size of the payload -+ */ -+static inline int tmel_process_request(struct tmel *tdev, u32 msg_uid, -+ void *msg_buf, size_t msg_size) -+{ -+ struct qmp_device *mdev = tdev->mdev; -+ struct tmel_ipc_pkt *resp_ipc_pkt; -+ struct mbox_chan *chan; -+ unsigned long jiffies; -+ long time_left = 0; -+ int ret = 0; -+ -+ chan = &tdev->ctrl.chans[0]; -+ -+ if (!msg_buf || !msg_size) { -+ dev_err(tdev->dev, "Invalid msg_buf or msg_size\n"); -+ return -EINVAL; -+ } -+ -+ tdev->rx_done = false; -+ -+ ret = tmel_prepare_msg(tdev, msg_uid, msg_buf, msg_size); -+ if (ret) -+ return ret; -+ -+ tdev->pkt.iov_len = sizeof(struct tmel_ipc_pkt); -+ tdev->pkt.iov_base = (void *)tdev->ipc_pkt; -+ -+ tmel_qmp_send_data(mdev, &tdev->pkt); -+ jiffies = msecs_to_jiffies(QMP_SEND_TIMEOUT); -+ -+ time_left = wait_event_interruptible_timeout(tdev->waitq, -+ tmel_rx_done(tdev), -+ jiffies); -+ -+ if (!time_left) { -+ dev_err(tdev->dev, "Request timed out\n"); -+ atomic_set(&mdev->tx_sent, 0); -+ mbox_chan_txdone(chan, ret); -+ return -ETIMEDOUT; -+ } -+ -+ if (tdev->pkt.iov_len != sizeof(struct tmel_ipc_pkt)) -+ return -EPROTO; -+ -+ resp_ipc_pkt = (struct tmel_ipc_pkt *)tdev->pkt.iov_base; -+ tmel_unprepare_message(tdev, msg_buf, msg_size); -+ tdev->rx_done = false; -+ -+ return resp_ipc_pkt->msg_hdr.response; -+} -+ -+/** -+ * tmel_secboot_sec_free() - Release the dma alloc and kmalloc'ed memory -+ * @ptr: Address of the tmel_secboot_sec wrapper for dma and kmalloc region. -+ */ -+void tmel_secboot_sec_free(void *ptr) -+{ -+ struct tmel_secboot_sec *smsg = ptr; -+ void *elf_buf = smsg->elf_buf; -+ dma_addr_t elf_buf_phys; -+ u32 size; -+ -+ elf_buf_phys = smsg->msg.req.elf_buf.buf; -+ size = smsg->msg.req.elf_buf.buf_len; -+ dma_free_coherent(smsg->dev, size, elf_buf, elf_buf_phys); -+ kfree(smsg); -+} -+ -+/** -+ * tmel_secboot_sec_auth() - authenticate the remote subsys image -+ * @tdev: the tmel device -+ * @sw_id: pas_id of the remote -+ * @metadata: payload to be sent -+ * @size: size of the payload -+ */ -+static inline int tmel_secboot_sec_auth(struct tmel *tdev, u32 sw_id, -+ void *metadata, size_t size) -+{ -+ struct tmel_secboot_sec *smsg __free(tmel_secboot_sec_f) = NULL; -+ struct device *dev = tdev->dev; -+ dma_addr_t elf_buf_phys; -+ void *elf_buf; -+ int ret; -+ -+ if (!dev || !metadata) -+ return -EINVAL; -+ -+ smsg = kzalloc(sizeof(*smsg), GFP_KERNEL); -+ -+ elf_buf = dma_alloc_coherent(dev, size, &elf_buf_phys, GFP_KERNEL); -+ if (!elf_buf) -+ return -ENOMEM; -+ -+ memcpy(elf_buf, metadata, size); -+ -+ smsg->dev = dev; -+ smsg->elf_buf = elf_buf; -+ -+ smsg->msg.req.sw_id = sw_id; -+ smsg->msg.req.elf_buf.buf = (u32)elf_buf_phys; -+ smsg->msg.req.elf_buf.buf_len = (u32)size; -+ -+ ret = tmel_process_request(tdev, TMEL_MSG_UID_SECBOOT_SEC_AUTH, -+ &smsg->msg, -+ sizeof(struct tmel_secboot_sec_auth)); -+ if (ret) { -+ dev_err(dev, "Failed to send IPC: %d\n", ret); -+ } else if (smsg->msg.resp.status) { -+ dev_err(dev, "Failed with status: %d", smsg->msg.resp.status); -+ ret = smsg->msg.resp.status ? -EINVAL : 0; -+ } else if (smsg->msg.resp.extended_error) { -+ dev_err(dev, "Failed with error: %d", smsg->msg.resp.extended_error); -+ ret = smsg->msg.resp.extended_error ? -EINVAL : 0; -+ } -+ -+ return ret; -+} -+ -+/** -+ * tmel_secboot_teardown() - teardown the remote subsys -+ * @tdev: tmel device -+ * @sw_id: pas_id of the remote -+ * @secondary_sw_id: extra argument for the pas_id -+ */ -+static inline int tmel_secboot_teardown(struct tmel *tdev, u32 sw_id, -+ u32 secondary_sw_id) -+{ -+ struct tmel_secboot_teardown msg = {0}; -+ struct device *dev = tdev->dev; -+ int ret; -+ -+ if (!dev) -+ return -EINVAL; -+ -+ msg.req.sw_id = sw_id; -+ msg.req.secondary_sw_id = secondary_sw_id; -+ msg.resp.status = TMEL_ERROR_GENERIC; -+ -+ ret = tmel_process_request(tdev, TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN, -+ &msg, sizeof(msg)); -+ if (ret) { -+ dev_err(dev, "Failed to send IPC: %d\n", ret); -+ } else if (msg.resp.status) { -+ dev_err(dev, "Failed with status: %d\n", msg.resp.status); -+ ret = msg.resp.status ? -EINVAL : 0; -+ } -+ -+ return ret; -+} -+ -+static inline void tmel_qmp_send_work(struct work_struct *work) -+{ -+ struct tmel *tdev = container_of(work, struct tmel, twork); -+ struct tmel_qmp_msg *tmsg = tdev->data; -+ struct tmel_sec_auth *smsg = tmsg->msg; -+ struct mbox_chan *chan; -+ int ret; -+ -+ chan = &tdev->ctrl.chans[0]; -+ -+ switch (tmsg->msg_id) { -+ case TMEL_MSG_UID_SECBOOT_SEC_AUTH: -+ ret = tmel_secboot_sec_auth(tdev, smsg->pas_id, smsg->data, smsg->size); -+ break; -+ case TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN: -+ ret = tmel_secboot_teardown(tdev, smsg->pas_id, 0); -+ break; -+ } -+ -+ mbox_chan_txdone(chan, ret); -+} -+ -+/** -+ * tmel_qmp_startup() - Start qmp mailbox channel for communication. -+ * @chan: mailbox channel that is being opened. -+ * Waits for remote subsystem to open channel if link is not -+ * initiated or until timeout. -+ */ -+static inline int tmel_qmp_startup(struct mbox_chan *chan) -+{ -+ struct tmel *tdev = chan->con_priv; -+ struct qmp_device *mdev = tdev->mdev; -+ unsigned long flags; -+ int ret; -+ -+ /* -+ * Kick start the SM from the negotiation phase -+ * Rest of the link changes would follow when remote responds. -+ */ -+ spin_lock_irqsave(&mdev->tx_lock, flags); -+ mdev->mcore.bits.link_state = 1; -+ mdev->local_state = LINK_NEGOTIATION; -+ spin_unlock_irqrestore(&mdev->tx_lock, flags); -+ -+ mdev->rx_pkt.iov_base = devm_kcalloc(mdev->dev, 1, QMP_MAX_PKT_SIZE, -+ GFP_KERNEL); -+ if (!mdev->rx_pkt.iov_base) -+ return -ENOMEM; -+ -+ tmel_qmp_send_irq(mdev); -+ -+ ret = wait_for_completion_timeout(&mdev->link_complete, -+ msecs_to_jiffies(QMP_TOUT_MS)); -+ if (!ret) -+ return -EAGAIN; -+ -+ spin_lock_irqsave(&mdev->tx_lock, flags); -+ if (mdev->local_state == LINK_CONNECTED) { -+ mdev->mcore.bits.ch_state = 1; -+ mdev->local_state = LOCAL_CONNECTING; -+ dev_dbg(mdev->dev, "link complete, local connecting"); -+ tmel_qmp_send_irq(mdev); -+ } -+ spin_unlock_irqrestore(&mdev->tx_lock, flags); -+ -+ ret = wait_for_completion_timeout(&mdev->ch_complete, -+ msecs_to_jiffies(QMP_TOUT_MS)); -+ if (!ret) -+ return -ETIMEDOUT; -+ -+ return 0; -+} -+ -+/** -+ * tmel_qmp_shutdown() - Shutdown this mailbox channel. -+ * @chan: mailbox channel to be shutdown. -+ * Disconnect this mailbox channel so the client does not receive anymore -+ * data and can reliquish control of the channel. -+ */ -+static inline void tmel_qmp_shutdown(struct mbox_chan *chan) -+{ -+ struct qmp_device *mdev = chan->con_priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&mdev->tx_lock, flags); -+ if (mdev->local_state != LINK_DISCONNECTED) { -+ mdev->local_state = LOCAL_DISCONNECTING; -+ mdev->mcore.bits.ch_state = 0; -+ tmel_qmp_send_irq(mdev); -+ } -+ spin_unlock_irqrestore(&mdev->tx_lock, flags); -+} -+ -+static inline int tmel_qmp_send(struct mbox_chan *chan, void *data) -+{ -+ struct tmel *tdev = chan->con_priv; -+ -+ tdev->data = data; -+ queue_work(system_wq, &tdev->twork); -+ -+ return 0; -+} -+ -+static struct mbox_chan_ops tmel_qmp_ops = { -+ .startup = tmel_qmp_startup, -+ .shutdown = tmel_qmp_shutdown, -+ .send_data = tmel_qmp_send, -+}; -+ -+static inline struct tmel *tmel_init(struct platform_device *pdev) -+{ -+ struct tmel *tdev; -+ struct mbox_chan *chans; -+ -+ tdev = devm_kcalloc(&pdev->dev, 1, sizeof(*tdev), GFP_KERNEL); -+ if (!tdev) -+ return ERR_PTR(-ENOMEM); -+ -+ tdev->ipc_pkt = devm_kcalloc(&pdev->dev, 1, sizeof(struct tmel_ipc_pkt), GFP_KERNEL); -+ if (!tdev->ipc_pkt) -+ return ERR_PTR(-ENOMEM); -+ -+ init_waitqueue_head(&tdev->waitq); -+ -+ tdev->rx_done = false; -+ tdev->dev = &pdev->dev; -+ platform_set_drvdata(pdev, tdev); -+ -+ chans = devm_kcalloc(&pdev->dev, QMP_NUM_CHANS, sizeof(*chans), GFP_KERNEL); -+ if (!chans) -+ return ERR_PTR(-ENOMEM); -+ -+ tdev->ctrl.chans = chans; -+ INIT_WORK(&tdev->twork, tmel_qmp_send_work); -+ -+ return tdev; -+} -+ -+static inline struct qmp_device *qmp_init(struct platform_device *pdev) -+{ -+ struct qmp_device *mdev; -+ -+ mdev = devm_kcalloc(&pdev->dev, 1, sizeof(*mdev), GFP_KERNEL); -+ if (!mdev) -+ return ERR_PTR(-ENOMEM); -+ -+ mdev->dev = &pdev->dev; -+ mdev->mcore_desc = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(mdev->mcore_desc)) -+ return ERR_PTR(-EIO); -+ -+ mdev->ucore_desc = mdev->mcore_desc + QMP_UCORE_DESC_OFFSET; -+ -+ spin_lock_init(&mdev->tx_lock); -+ mdev->local_state = LINK_DISCONNECTED; -+ init_completion(&mdev->link_complete); -+ init_completion(&mdev->ch_complete); -+ -+ return mdev; -+} -+ -+static inline int qmp_mbox_client_init(struct qmp_device *mdev) -+{ -+ int ret = 0; -+ -+ mdev->mbox_client.dev = mdev->dev; -+ mdev->mbox_client.knows_txdone = false; -+ mdev->mbox_chan = mbox_request_channel(&mdev->mbox_client, 0); -+ if (IS_ERR(mdev->mbox_chan)) -+ ret = PTR_ERR(mdev->mbox_chan); -+ -+ return ret; -+} -+ -+static inline int tmel_mbox_ctrl_init(struct tmel *tdev) -+{ -+ tdev->ctrl.dev = tdev->dev; -+ tdev->ctrl.ops = &tmel_qmp_ops; -+ tdev->ctrl.chans[0].con_priv = tdev; -+ tdev->ctrl.num_chans = QMP_NUM_CHANS; -+ tdev->ctrl.txdone_irq = true; -+ -+ return devm_mbox_controller_register(tdev->dev, &tdev->ctrl); -+} -+ -+static inline int tmel_qmp_probe(struct platform_device *pdev) -+{ -+ struct device_node *node = pdev->dev.of_node; -+ struct qmp_device *mdev; -+ struct tmel *tdev; -+ int ret = 0; -+ -+ tdev = tmel_init(pdev); -+ if (IS_ERR(tdev)) -+ return dev_err_probe(tdev->dev, ret, "tmel device init failed\n"); -+ -+ mdev = qmp_init(pdev); -+ if (IS_ERR(mdev)) -+ return dev_err_probe(tdev->dev, ret, "qmp device init failed\n"); -+ -+ tdev->mdev = mdev; -+ -+ ret = qmp_mbox_client_init(mdev); -+ if (ret) -+ return dev_err_probe(mdev->dev, ret, "IPC chan missing, client init failed"); -+ -+ ret = tmel_mbox_ctrl_init(tdev); -+ if (ret) -+ return dev_err_probe(tdev->dev, ret, "failed to register mbox controller"); -+ -+ ret = platform_get_irq(pdev, 0); -+ ret = devm_request_threaded_irq(tdev->dev, ret, NULL, tmel_qmp_irq_handler, -+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, -+ node->name, (void *)tdev); -+ if (ret < 0) -+ return dev_err_probe(tdev->dev, ret, "request threaded irq failed\n"); -+ -+ return ret; -+} -+ -+static const struct of_device_id tmel_qmp_dt_match[] = { -+ { .compatible = "qcom,ipq5424-tmel" }, -+ {}, -+}; -+ -+static struct platform_driver tmel_qmp_driver = { -+ .driver = { -+ .name = "tmel_qmp_mbox", -+ .of_match_table = tmel_qmp_dt_match, -+ }, -+ .probe = tmel_qmp_probe, -+}; -+module_platform_driver(tmel_qmp_driver); -+ -+MODULE_DESCRIPTION("QCOM TMEL QMP driver"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/include/linux/mailbox/tmelcom-qmp.h -@@ -0,0 +1,65 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+#ifndef _TMELCOM_H_ -+#define _TMELCOM_H_ -+ -+#include -+ -+/* -+ * Macro used to define unique TMEL Message Identifier based on -+ * message type and action identifier. -+ */ -+#define MSGTYPE_MASK GENMASK(15, 8) -+#define ACTIONID_MASK GENMASK(7, 0) -+ -+#define TMEL_MSG_UID_CREATE(msg_type, action_id) \ -+ (FIELD_PREP_CONST(MSGTYPE_MASK, msg_type) | \ -+ FIELD_PREP_CONST(ACTIONID_MASK, action_id)) -+ -+/* -+ * Helper macro to extract the messageType from TMEL_MSG_UID -+ */ -+#define TMEL_MSG_UID_MSG_TYPE(v) FIELD_GET(MSGTYPE_MASK, v) -+ -+/* -+ * Helper macro to extract the actionID from TMEL_MSG_UID -+ */ -+#define TMEL_MSG_UID_ACTION_ID(v) FIELD_GET(ACTIONID_MASK, v) -+ -+/* -+ * All definitions of supported messageTypes. -+ */ -+#define TMEL_MSG_SECBOOT 0x00 -+ -+/* -+ * Action IDs for TMEL_MSG_SECBOOT -+ */ -+#define TMEL_ACTION_SECBOOT_SEC_AUTH 0x04 -+#define TMEL_ACTION_SECBOOT_SS_TEAR_DOWN 0x0a -+ -+/* -+ * UIDs for TMEL_MSG_SECBOOT -+ */ -+#define TMEL_MSG_UID_SECBOOT_SEC_AUTH TMEL_MSG_UID_CREATE(TMEL_MSG_SECBOOT,\ -+ TMEL_ACTION_SECBOOT_SEC_AUTH) -+ -+#define TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN TMEL_MSG_UID_CREATE(TMEL_MSG_SECBOOT,\ -+ TMEL_ACTION_SECBOOT_SS_TEAR_DOWN) -+ -+struct tmel_qmp_msg { -+ void *msg; -+ u32 msg_id; -+}; -+ -+struct tmel_sec_auth { -+ void *data; -+ u32 size; -+ u32 pas_id; -+}; -+ -+void tmel_secboot_sec_free(void *ptr); -+ -+DEFINE_FREE(tmel_secboot_sec_f, void *, if (_T) tmel_secboot_sec_free(_T)) -+#endif /* _TMELCOM_H_ */ diff --git a/target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch b/target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch deleted file mode 100644 index 103c4434d70..00000000000 --- a/target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 9d67219c941458fd26eb474d25f9a6b2525876ca Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -Date: Tue, 21 Oct 2025 15:38:28 +0400 -Subject: [PATCH v6 1/8] firmware: qcom_scm: ipq5332: add support to pass metadata size -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20251021-ipq5018-wifi-v6-1-c55c547df6fc@outlook.com> - -IPQ5332 security software running under trustzone requires metadata size. -With new command support added in TrustZone that includes a size parameter, -pass metadata size as well. - -Reviewed-by: Konrad Dybcio -Signed-off-by: Manikanta Mylavarapu -Signed-off-by: Gokul Sriram Palanisamy ---- - drivers/firmware/qcom/qcom_scm.c | 17 +++++++++++++---- - drivers/firmware/qcom/qcom_scm.h | 1 + - 2 files changed, 14 insertions(+), 4 deletions(-) - ---- a/drivers/firmware/qcom/qcom_scm.c -+++ b/drivers/firmware/qcom/qcom_scm.c -@@ -583,9 +583,6 @@ int qcom_scm_pas_init_image(u32 peripher - int ret; - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_PIL, -- .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, -- .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), -- .args[0] = peripheral, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; -@@ -617,7 +614,19 @@ int qcom_scm_pas_init_image(u32 peripher - if (ret) - goto disable_clk; - -- desc.args[1] = mdata_phys; -+ if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, -+ QCOM_SCM_PIL_PAS_INIT_IMAGE_V2)) { -+ desc.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE_V2; -+ desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL); -+ desc.args[0] = peripheral; -+ desc.args[1] = mdata_phys; -+ desc.args[2] = size; -+ } else { -+ desc.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE; -+ desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); -+ desc.args[0] = peripheral; -+ desc.args[1] = mdata_phys; -+ } - - ret = qcom_scm_call(__scm->dev, &desc, &res); - qcom_scm_bw_disable(); ---- a/drivers/firmware/qcom/qcom_scm.h -+++ b/drivers/firmware/qcom/qcom_scm.h -@@ -101,6 +101,7 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm - #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 - #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 - #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a -+#define QCOM_SCM_PIL_PAS_INIT_IMAGE_V2 0x1a - - #define QCOM_SCM_SVC_IO 0x05 - #define QCOM_SCM_IO_READ 0x01 diff --git a/target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch b/target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch deleted file mode 100644 index ff74f44bdd6..00000000000 --- a/target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch +++ /dev/null @@ -1,157 +0,0 @@ -From db128ef0282b8cc41816caf39a03fa17d10fa865 Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -Date: Tue, 21 Oct 2025 15:38:29 +0400 -Subject: [PATCH v6 2/8] dt-bindings: remoteproc: qcom: document hexagon based WCSS secure PIL -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20251021-ipq5018-wifi-v6-2-c55c547df6fc@outlook.com> - -Add new binding document for hexagon based WCSS secure PIL remoteproc. -IPQ5018, IPQ5332, IPQ5424 and IPQ9574 follow secure PIL remoteproc. - -Reviewed-by: Krzysztof Kozlowski -Signed-off-by: Manikanta Mylavarapu -Signed-off-by: Gokul Sriram Palanisamy ---- - .../bindings/remoteproc/qcom,wcss-sec-pil.yaml | 133 +++++++++++++++++++++ - 1 file changed, 133 insertions(+) - ---- /dev/null -+++ b/Documentation/devicetree/bindings/remoteproc/qcom,wcss-sec-pil.yaml -@@ -0,0 +1,135 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/remoteproc/qcom,wcss-sec-pil.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Qualcomm WCSS Secure Peripheral Image Loader -+ -+maintainers: -+ - Manikanta Mylavarapu -+ -+description: -+ Wireless Connectivity Subsystem (WCSS) Secure Peripheral Image Loader loads -+ firmware and power up QDSP6 remoteproc on the Qualcomm IPQ series SoC. -+ -+properties: -+ compatible: -+ enum: -+ - qcom,ipq5018-wcss-sec-pil -+ - qcom,ipq5332-wcss-sec-pil -+ - qcom,ipq5424-wcss-sec-pil -+ - qcom,ipq9574-wcss-sec-pil -+ -+ reg: -+ maxItems: 1 -+ -+ firmware-name: -+ maxItems: 1 -+ description: Firmware name for the Hexagon core -+ -+ interrupts: -+ items: -+ - description: Watchdog interrupt -+ - description: Fatal interrupt -+ - description: Ready interrupt -+ - description: Handover interrupt -+ - description: Stop acknowledge interrupt -+ -+ interrupt-names: -+ items: -+ - const: wdog -+ - const: fatal -+ - const: ready -+ - const: handover -+ - const: stop-ack -+ -+ clocks: -+ items: -+ - description: sleep clock -+ - description: AHB interconnect between system NOC and WCSS block -+ -+ clock-names: -+ items: -+ - const: sleep -+ - const: interconnect -+ -+ mboxes: -+ maxItems: 1 -+ items: -+ - description: A phandle to the TMECom mailbox device node -+ -+ qcom,smem-states: -+ $ref: /schemas/types.yaml#/definitions/phandle-array -+ description: States used by the AP to signal the remote processor -+ items: -+ - description: Stop Q6 -+ - description: Shutdown Q6 -+ -+ qcom,smem-state-names: -+ description: -+ Names of the states used by the AP to signal the remote processor -+ items: -+ - const: stop -+ - const: shutdown -+ -+ memory-region: -+ items: -+ - description: Q6 reserved region -+ -+ glink-edge: -+ $ref: /schemas/remoteproc/qcom,glink-edge.yaml# -+ description: -+ Qualcomm G-Link subnode which represents communication edge, channels -+ and devices related to the Modem. -+ unevaluatedProperties: false -+ -+required: -+ - compatible -+ - reg -+ - firmware-name -+ - interrupts -+ - interrupt-names -+ - qcom,smem-states -+ - qcom,smem-state-names -+ - memory-region -+ -+additionalProperties: false -+ -+examples: -+ - | -+ #include -+ #include -+ remoteproc@d100000 { -+ compatible = "qcom,ipq5424-wcss-sec-pil"; -+ reg = <0x0d100000 0x4040>; -+ firmware-name = "ath12k/IPQ5424/hw1.0/q6_fw0.mbn"; -+ interrupts-extended = <&intc GIC_SPI 508 IRQ_TYPE_EDGE_RISING>, -+ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>; -+ interrupt-names = "wdog", -+ "fatal", -+ "ready", -+ "handover", -+ "stop-ack"; -+ -+ clocks = <&gcc GCC_IM_SLEEP_CLK>; -+ clock-names = "sleep"; -+ -+ mboxes = <&tmel_qmp 0>; -+ qcom,smem-states = <&wcss_smp2p_out 1>, -+ <&wcss_smp2p_out 0>; -+ qcom,smem-state-names = "stop", -+ "shutdown"; -+ -+ memory-region = <&q6_region>; -+ -+ glink-edge { -+ interrupts = ; -+ label = "rtr"; -+ qcom,remote-pid = <1>; -+ mboxes = <&apcs_glb 8>; -+ }; -+ }; diff --git a/target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch b/target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch deleted file mode 100644 index d999e5563d1..00000000000 --- a/target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch +++ /dev/null @@ -1,500 +0,0 @@ -From 47d46157200bb5f67e540a87c67aab9cde62f8a4 Mon Sep 17 00:00:00 2001 -From: Vignesh Viswanathan -Date: Tue, 21 Oct 2025 15:38:30 +0400 -Subject: [PATCH v6 3/8] remoteproc: qcom: add hexagon based WCSS secure PIL driver -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20251021-ipq5018-wifi-v6-3-c55c547df6fc@outlook.com> - -Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx -SoCs support secure Peripheral Image Loading (PIL). - -Secure PIL image is signed firmware image which only trusted software such -as TrustZone (TZ) can authenticate and load. Linux kernel will send a -Peripheral Authentication Service (PAS) request to TZ to authenticate and -load the PIL images. This change also introduces secure firmware -authentication using Trusted Management Engine-Lite (TME-L) which is -supported on IPQ5424 SoC. This driver uses mailbox based PAS request to -TME-L for image authentication if supported, else it will fallback to use -SCM call based PAS request to TZ. - -In order to avoid overloading the existing WCSS driver or PAS driver, we -came up with this new PAS based IPQ WCSS driver. - -Signed-off-by: Vignesh Viswanathan -Signed-off-by: Manikanta Mylavarapu -Signed-off-by: Gokul Sriram Palanisamy -Signed-off-by: George Moussalem ---- - drivers/remoteproc/Kconfig | 19 ++ - drivers/remoteproc/Makefile | 1 + - drivers/remoteproc/qcom_q6v5_wcss_sec.c | 390 ++++++++++++++++++++++++++++++++ - include/linux/remoteproc.h | 2 + - 4 files changed, 412 insertions(+) - ---- a/drivers/remoteproc/Kconfig -+++ b/drivers/remoteproc/Kconfig -@@ -255,6 +255,25 @@ config QCOM_Q6V5_WCSS - Hexagon V5 based WCSS remote processors on e.g. IPQ8074. This is - a non-TrustZone wireless subsystem. - -+config QCOM_Q6V5_WCSS_SEC -+ tristate "Qualcomm Hexagon based WCSS Secure Peripheral Image Loader" -+ depends on OF && ARCH_QCOM -+ depends on QCOM_SMEM -+ depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n -+ depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n -+ select QCOM_MDT_LOADER -+ select QCOM_PIL_INFO -+ select QCOM_Q6V5_COMMON -+ select QCOM_RPROC_COMMON -+ select QCOM_SCM -+ help -+ Say y here to support the Qualcomm Secure Peripheral Image Loader -+ for the Hexagon based remote processors on e.g. IPQ5332. -+ -+ This is TrustZone wireless subsystem. The firmware is -+ verified and booted with the help of the Peripheral Authentication -+ System (PAS) in TrustZone. -+ - config QCOM_SYSMON - tristate "Qualcomm sysmon driver" - depends on RPMSG ---- a/drivers/remoteproc/Makefile -+++ b/drivers/remoteproc/Makefile -@@ -28,6 +28,7 @@ obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6 - obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o - obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o - obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o -+obj-$(CONFIG_QCOM_Q6V5_WCSS_SEC) += qcom_q6v5_wcss_sec.o - obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o - obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o - qcom_wcnss_pil-y += qcom_wcnss.o ---- /dev/null -+++ b/drivers/remoteproc/qcom_q6v5_wcss_sec.c -@@ -0,0 +1,406 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2016-2018 Linaro Ltd. -+ * Copyright (C) 2014 Sony Mobile Communications AB -+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. -+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qcom_common.h" -+#include "qcom_q6v5.h" -+#include "qcom_pil_info.h" -+ -+#define WCSS_CRASH_REASON 421 -+ -+#define WCSS_PAS_ID 0x6 -+#define MPD_WCSS_PAS_ID 0xd -+ -+#define Q6_WAIT_TIMEOUT (5 * HZ) -+ -+struct wcss_sec { -+ struct device *dev; -+ struct qcom_rproc_glink glink_subdev; -+ struct qcom_rproc_ssr ssr_subdev; -+ struct qcom_q6v5 q6; -+ phys_addr_t mem_phys; -+ phys_addr_t mem_reloc; -+ void *mem_region; -+ size_t mem_size; -+ const struct wcss_data *desc; -+ -+ struct mbox_client mbox_client; -+ struct mbox_chan *mbox_chan; -+ void *metadata; -+ size_t metadata_len; -+}; -+ -+struct wcss_data { -+ u32 pasid; -+ const char *ss_name; -+ bool auto_boot; -+ bool use_tmelcom; -+}; -+ -+static int wcss_sec_start(struct rproc *rproc) -+{ -+ struct wcss_sec *wcss = rproc->priv; -+ struct device *dev = wcss->dev; -+ int ret; -+ -+ ret = qcom_q6v5_prepare(&wcss->q6); -+ if (ret) -+ return ret; -+ -+ if (wcss->desc->use_tmelcom) { -+ struct tmel_sec_auth tsa; -+ struct tmel_qmp_msg tqm; -+ -+ tsa.data = wcss->metadata; -+ tsa.size = wcss->metadata_len; -+ tsa.pas_id = wcss->desc->pasid; -+ tqm.msg = &tsa; -+ tqm.msg_id = TMEL_MSG_UID_SECBOOT_SEC_AUTH; -+ -+ ret = mbox_send_message(wcss->mbox_chan, (void *)&tqm); -+ if (ret < 0) { -+ dev_err(dev, "Failed to send message via mailbox\n"); -+ goto unprepare; -+ } -+ } else { -+ ret = qcom_scm_pas_auth_and_reset(wcss->desc->pasid); -+ if (ret) { -+ dev_err(dev, "wcss_reset failed\n"); -+ goto unprepare; -+ } -+ } -+ -+ ret = qcom_q6v5_wait_for_start(&wcss->q6, Q6_WAIT_TIMEOUT); -+ if (ret == -ETIMEDOUT) -+ dev_err(dev, "start timed out\n"); -+ -+unprepare: -+ qcom_q6v5_unprepare(&wcss->q6); -+ -+ return ret; -+} -+ -+static int wcss_sec_stop(struct rproc *rproc) -+{ -+ struct wcss_sec *wcss = rproc->priv; -+ struct device *dev = wcss->dev; -+ int ret; -+ -+ if (wcss->desc->use_tmelcom) { -+ struct tmel_sec_auth tsa = {0}; -+ struct tmel_qmp_msg tqm; -+ -+ tsa.pas_id = wcss->desc->pasid; -+ tqm.msg = &tsa; -+ tqm.msg_id = TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN; -+ -+ mbox_send_message(wcss->mbox_chan, (void *)&tqm); -+ } else { -+ ret = qcom_scm_pas_shutdown(wcss->desc->pasid); -+ if (ret) { -+ dev_err(dev, "not able to shutdown\n"); -+ return ret; -+ } -+ } -+ -+ qcom_q6v5_unprepare(&wcss->q6); -+ -+ return 0; -+} -+ -+static void *wcss_sec_da_to_va(struct rproc *rproc, u64 da, size_t len, -+ bool *is_iomem) -+{ -+ struct wcss_sec *wcss = rproc->priv; -+ int offset; -+ -+ offset = da - wcss->mem_reloc; -+ if (offset < 0 || offset + len > wcss->mem_size) -+ return NULL; -+ -+ return wcss->mem_region + offset; -+} -+ -+static int wcss_sec_load(struct rproc *rproc, const struct firmware *fw) -+{ -+ struct wcss_sec *wcss = rproc->priv; -+ struct device *dev = wcss->dev; -+ int ret; -+ -+ if (wcss->desc->use_tmelcom) { -+ wcss->metadata = qcom_mdt_read_metadata(fw, &wcss->metadata_len, -+ rproc->firmware, wcss->dev); -+ if (IS_ERR(wcss->metadata)) { -+ ret = PTR_ERR(wcss->metadata); -+ dev_err(wcss->dev, "error %d reading firmware %s metadata\n", -+ ret, rproc->firmware); -+ return ret; -+ } -+ -+ ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, wcss->desc->pasid, -+ wcss->mem_region, wcss->mem_phys, wcss->mem_size, -+ &wcss->mem_reloc); -+ if (ret) { -+ kfree(wcss->metadata); -+ return ret; -+ } -+ } else { -+ ret = qcom_mdt_load(dev, fw, rproc->firmware, wcss->desc->pasid, wcss->mem_region, -+ wcss->mem_phys, wcss->mem_size, &wcss->mem_reloc); -+ if (ret) -+ return ret; -+ } -+ -+ qcom_pil_info_store("wcss", wcss->mem_phys, wcss->mem_size); -+ -+ return 0; -+} -+ -+static unsigned long wcss_sec_panic(struct rproc *rproc) -+{ -+ struct wcss_sec *wcss = rproc->priv; -+ -+ return qcom_q6v5_panic(&wcss->q6); -+} -+ -+static void wcss_sec_copy_segment(struct rproc *rproc, -+ struct rproc_dump_segment *segment, -+ void *dest, size_t offset, size_t size) -+{ -+ struct wcss_sec *wcss = rproc->priv; -+ struct device *dev = wcss->dev; -+ -+ if (!segment->io_ptr) -+ segment->io_ptr = ioremap_wc(segment->da, segment->size); -+ -+ if (!segment->io_ptr) { -+ dev_err(dev, "Failed to ioremap segment %pad size 0x%zx\n", -+ &segment->da, segment->size); -+ return; -+ } -+ -+ if (offset + size < segment->size) { -+ memcpy(dest, segment->io_ptr + offset, size); -+ } else { -+ iounmap(segment->io_ptr); -+ segment->io_ptr = NULL; -+ } -+} -+ -+static int wcss_sec_dump_segments(struct rproc *rproc, -+ const struct firmware *fw) -+{ -+ struct device *dev = rproc->dev.parent; -+ struct reserved_mem *rmem = NULL; -+ struct device_node *node; -+ int num_segs, index; -+ int ret; -+ -+ /* -+ * Parse through additional reserved memory regions for the rproc -+ * and add them to the coredump segments -+ */ -+ num_segs = of_count_phandle_with_args(dev->of_node, -+ "memory-region", NULL); -+ for (index = 0; index < num_segs; index++) { -+ node = of_parse_phandle(dev->of_node, -+ "memory-region", index); -+ if (!node) -+ return -EINVAL; -+ -+ rmem = of_reserved_mem_lookup(node); -+ of_node_put(node); -+ if (!rmem) { -+ dev_err(dev, "unable to acquire memory-region index %d num_segs %d\n", -+ index, num_segs); -+ return -EINVAL; -+ } -+ -+ dev_dbg(dev, "Adding segment 0x%pa size 0x%pa", -+ &rmem->base, &rmem->size); -+ ret = rproc_coredump_add_custom_segment(rproc, -+ rmem->base, -+ rmem->size, -+ wcss_sec_copy_segment, -+ NULL); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static const struct rproc_ops wcss_sec_ops = { -+ .start = wcss_sec_start, -+ .stop = wcss_sec_stop, -+ .da_to_va = wcss_sec_da_to_va, -+ .load = wcss_sec_load, -+ .get_boot_addr = rproc_elf_get_boot_addr, -+ .panic = wcss_sec_panic, -+ .parse_fw = wcss_sec_dump_segments, -+}; -+ -+static int wcss_sec_alloc_memory_region(struct wcss_sec *wcss) -+{ -+ struct reserved_mem *rmem = NULL; -+ struct device_node *node; -+ struct device *dev = wcss->dev; -+ -+ node = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (!node) { -+ dev_err(dev, "can't find phandle memory-region\n"); -+ return -EINVAL; -+ } -+ -+ rmem = of_reserved_mem_lookup(node); -+ of_node_put(node); -+ -+ if (!rmem) { -+ dev_err(dev, "unable to acquire memory-region\n"); -+ return -EINVAL; -+ } -+ -+ wcss->mem_phys = rmem->base; -+ wcss->mem_reloc = rmem->base; -+ wcss->mem_size = rmem->size; -+ wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size); -+ if (!wcss->mem_region) { -+ dev_err(dev, "unable to map memory region: %pa+%pa\n", -+ &rmem->base, &rmem->size); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static int wcss_sec_probe(struct platform_device *pdev) -+{ -+ struct rproc *rproc; -+ struct wcss_sec *wcss; -+ struct clk *sleep_clk; -+ struct clk *int_clk; -+ const char *fw_name = NULL; -+ const struct wcss_data *desc = of_device_get_match_data(&pdev->dev); -+ int ret; -+ -+ ret = of_property_read_string(pdev->dev.of_node, "firmware-name", -+ &fw_name); -+ if (ret < 0) -+ return ret; -+ -+ rproc = devm_rproc_alloc(&pdev->dev, desc->ss_name, &wcss_sec_ops, -+ fw_name, sizeof(*wcss)); -+ if (!rproc) { -+ dev_err(&pdev->dev, "failed to allocate rproc\n"); -+ return -ENOMEM; -+ } -+ -+ wcss = rproc->priv; -+ wcss->dev = &pdev->dev; -+ wcss->desc = desc; -+ -+ ret = wcss_sec_alloc_memory_region(wcss); -+ if (ret) -+ return ret; -+ -+ sleep_clk = devm_clk_get_optional_enabled(&pdev->dev, "sleep"); -+ if (IS_ERR(sleep_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(sleep_clk), -+ "Failed to get sleep clock\n"); -+ -+ int_clk = devm_clk_get_optional_enabled(&pdev->dev, "interconnect"); -+ if (IS_ERR(int_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(int_clk), -+ "Failed to get interconnect clock\n"); -+ -+ ret = qcom_q6v5_init(&wcss->q6, pdev, rproc, -+ WCSS_CRASH_REASON, NULL, NULL); -+ if (ret) -+ return ret; -+ -+ qcom_add_glink_subdev(rproc, &wcss->glink_subdev, desc->ss_name); -+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, desc->ss_name); -+ -+ rproc->auto_boot = false; -+ rproc->dump_conf = RPROC_COREDUMP_INLINE; -+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); -+ -+ if (desc->use_tmelcom) { -+ wcss->mbox_client.dev = wcss->dev; -+ wcss->mbox_client.knows_txdone = true; -+ wcss->mbox_client.tx_block = true; -+ wcss->mbox_chan = mbox_request_channel(&wcss->mbox_client, 0); -+ if (IS_ERR_OR_NULL(wcss->mbox_chan)) -+ return dev_err_probe(wcss->dev, PTR_ERR(wcss->mbox_chan), -+ "mbox chan for IPC is missing\n"); -+ } -+ -+ ret = devm_rproc_add(&pdev->dev, rproc); -+ if (ret) -+ return ret; -+ -+ platform_set_drvdata(pdev, rproc); -+ -+ return 0; -+} -+ -+static void wcss_sec_remove(struct platform_device *pdev) -+{ -+ struct rproc *rproc = platform_get_drvdata(pdev); -+ struct wcss_sec *wcss = rproc->priv; -+ -+ mbox_free_channel(wcss->mbox_chan); -+ qcom_remove_glink_subdev(rproc, &wcss->glink_subdev); -+ qcom_remove_ssr_subdev(rproc, &wcss->ssr_subdev); -+ qcom_q6v5_deinit(&wcss->q6); -+} -+ -+static const struct wcss_data wcss_sec_ipq5332_res_init = { -+ .pasid = MPD_WCSS_PAS_ID, -+ .ss_name = "q6wcss", -+}; -+ -+static const struct wcss_data wcss_sec_ipq5424_res_init = { -+ .pasid = MPD_WCSS_PAS_ID, -+ .ss_name = "q6wcss", -+ .use_tmelcom = true, -+}; -+ -+static const struct wcss_data wcss_sec_ipq9574_res_init = { -+ .pasid = WCSS_PAS_ID, -+ .ss_name = "q6wcss", -+}; -+ -+static const struct of_device_id wcss_sec_of_match[] = { -+ { .compatible = "qcom,ipq5018-wcss-sec-pil", .data = &wcss_sec_ipq5332_res_init }, -+ { .compatible = "qcom,ipq5332-wcss-sec-pil", .data = &wcss_sec_ipq5332_res_init }, -+ { .compatible = "qcom,ipq5424-wcss-sec-pil", .data = &wcss_sec_ipq5424_res_init }, -+ { .compatible = "qcom,ipq9574-wcss-sec-pil", .data = &wcss_sec_ipq9574_res_init }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, wcss_sec_of_match); -+ -+static struct platform_driver wcss_sec_driver = { -+ .probe = wcss_sec_probe, -+ .remove = wcss_sec_remove, -+ .driver = { -+ .name = "qcom-wcss-secure-pil", -+ .of_match_table = wcss_sec_of_match, -+ }, -+}; -+module_platform_driver(wcss_sec_driver); -+ -+MODULE_DESCRIPTION("Hexagon WCSS Secure Peripheral Image Loader"); -+MODULE_LICENSE("GPL"); ---- a/include/linux/remoteproc.h -+++ b/include/linux/remoteproc.h -@@ -472,6 +472,7 @@ enum rproc_dump_mechanism { - * @node: list node related to the rproc segment list - * @da: device address of the segment - * @size: size of the segment -+ * @io_ptr: ptr to store the ioremapped dump segment - * @priv: private data associated with the dump_segment - * @dump: custom dump function to fill device memory segment associated - * with coredump -@@ -483,6 +484,7 @@ struct rproc_dump_segment { - dma_addr_t da; - size_t size; - -+ void *io_ptr; - void *priv; - void (*dump)(struct rproc *rproc, struct rproc_dump_segment *segment, - void *dest, size_t offset, size_t size); diff --git a/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch b/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch deleted file mode 100644 index ee1c5738fd6..00000000000 --- a/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 553461c1f1ae9b2ac50bce5ed5de15092cc04d29 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Tue, 21 Oct 2025 15:38:32 +0400 -Subject: [PATCH v6 5/8] arm64: dts: qcom: ipq5018: add nodes to bringup q6 -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20251021-ipq5018-wifi-v6-5-c55c547df6fc@outlook.com> - -Enable nodes required for q6 remoteproc bring up. - -Signed-off-by: George Moussalem ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 62 +++++++++++++++++++++++++++++++++++ - 1 file changed, 62 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -158,6 +158,11 @@ - reg = <0x0 0x4ac00000 0x0 0x200000>; - no-map; - }; -+ -+ q6_region: wcss@4b000000 { -+ reg = <0x0 0x4b000000 0x0 0x1b00000>; -+ no-map; -+ }; - }; - - soc: soc@0 { -@@ -734,6 +739,41 @@ - }; - }; - -+ q6v5_wcss: remoteproc@cd00000 { -+ compatible = "qcom,ipq5018-wcss-sec-pil"; -+ reg = <0x0cd00000 0x10000>; -+ firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mbn"; -+ interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>, -+ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>; -+ interrupt-names = "wdog", -+ "fatal", -+ "ready", -+ "handover", -+ "stop-ack"; -+ -+ clocks = <&gcc GCC_SLEEP_CLK_SRC>, -+ <&gcc GCC_SYS_NOC_WCSS_AHB_CLK>; -+ clock-names = "sleep", -+ "interconnect"; -+ -+ qcom,smem-states = <&wcss_smp2p_out 0>, -+ <&wcss_smp2p_out 1>; -+ qcom,smem-state-names = "shutdown", -+ "stop"; -+ -+ memory-region = <&q6_region>; -+ -+ glink-edge { -+ interrupts = ; -+ label = "rtr"; -+ qcom,remote-pid = <1>; -+ mboxes = <&apcs_glb 8>; -+ }; -+ }; -+ - pcie1: pcie@80000000 { - compatible = "qcom,pcie-ipq5018"; - reg = <0x80000000 0xf1d>, -@@ -1008,4 +1048,28 @@ - , - ; - }; -+ -+ wcss: smp2p-wcss { -+ compatible = "qcom,smp2p"; -+ qcom,smem = <435>, <428>; -+ -+ interrupt-parent = <&intc>; -+ interrupts = ; -+ -+ mboxes = <&apcs_glb 9>; -+ -+ qcom,local-pid = <0>; -+ qcom,remote-pid = <1>; -+ -+ wcss_smp2p_out: master-kernel { -+ qcom,entry-name = "master-kernel"; -+ #qcom,smem-state-cells = <1>; -+ }; -+ -+ wcss_smp2p_in: slave-kernel { -+ qcom,entry-name = "slave-kernel"; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ }; - }; diff --git a/target/linux/qualcommax/patches-6.12/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch b/target/linux/qualcommax/patches-6.12/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch deleted file mode 100644 index 476e5f6ba15..00000000000 --- a/target/linux/qualcommax/patches-6.12/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch +++ /dev/null @@ -1,65 +0,0 @@ -From: George Moussalem -Subject: [PATCH] pinctrl: qcom: IPQ5018: update pwm groups -Date: Wed, 27 Nov 2024 09:14:11 +0400 - -GPIO 1, 30, and 46 are used to control PWM1, PWM3, and PWM0 respectively which -in turn drive the PWM led, so let's update the pwm# and pingroups accordingly. - -Signed-off-by: George Moussalem ---- ---- a/drivers/pinctrl/qcom/pinctrl-ipq5018.c -+++ b/drivers/pinctrl/qcom/pinctrl-ipq5018.c -@@ -541,7 +541,7 @@ static const char * const qdss_tracectl_ - }; - - static const char * const pwm0_groups[] = { -- "gpio42", -+ "gpio42", "gpio46", - }; - - static const char * const qdss_cti_trig_out_b0_groups[] = { -@@ -549,7 +549,7 @@ static const char * const qdss_cti_trig_ - }; - - static const char * const pwm1_groups[] = { -- "gpio43", -+ "gpio43", "gpio1", - }; - - static const char * const qdss_cti_trig_in_b0_groups[] = { -@@ -565,7 +565,7 @@ static const char * const qdss_cti_trig_ - }; - - static const char * const pwm3_groups[] = { -- "gpio45", -+ "gpio45", "gpio30", - }; - - static const char * const qdss_cti_trig_in_b1_groups[] = { -@@ -679,7 +679,7 @@ static const struct pinfunction ipq5018_ - - static const struct msm_pingroup ipq5018_groups[] = { - PINGROUP(0, atest_char, _, qdss_cti_trig_out_a0, wci_txd, wci_rxd, xfem, _, _, _), -- PINGROUP(1, atest_char, _, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _), -+ PINGROUP(1, atest_char, pwm1, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _), - PINGROUP(2, atest_char, _, qdss_cti_trig_out_a1, wci_txd, wci_rxd, xfem, _, _, _), - PINGROUP(3, atest_char, _, qdss_cti_trig_in_a1, wci_txd, wci_rxd, xfem, _, _, _), - PINGROUP(4, sdc1_data, qspi_data, blsp1_spi1, btss, dbg_out, qdss_traceclk_a, _, burn0, _), -@@ -708,7 +708,7 @@ static const struct msm_pingroup ipq5018 - PINGROUP(27, audio_txmclk, wsa_swrm, audio_txmclk, blsp2_spi, btss, _, qdss_tracedata_b, _, _), - PINGROUP(28, audio_txbclk, wsa_swrm, blsp0_uart1, btss, qdss_tracedata_b, _, _, _, _), - PINGROUP(29, audio_txfsync, _, blsp0_uart1, _, qdss_tracedata_b, _, _, _, _), -- PINGROUP(30, audio_txd, led2, led0, _, _, _, _, _, _), -+ PINGROUP(30, audio_txd, led2, led0, pwm3, _, _, _, _, _), - PINGROUP(31, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _), - PINGROUP(32, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _), - PINGROUP(33, blsp2_i2c0, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _), -@@ -724,7 +724,7 @@ static const struct msm_pingroup ipq5018 - PINGROUP(43, pwm1, qdss_cti_trig_in_b0, wci_txd, wci_rxd, xfem, _, _, _, _), - PINGROUP(44, pwm2, qdss_cti_trig_out_b1, wci_txd, wci_rxd, xfem, _, _, _, _), - PINGROUP(45, pwm3, qdss_cti_trig_in_b1, wci_txd, wci_rxd, xfem, _, _, _, _), -- PINGROUP(46, led0, _, _, _, _, _, _, _, _), -+ PINGROUP(46, led0, pwm0, _, _, _, _, _, _, _), - }; - - static const struct msm_pinctrl_soc_data ipq5018_pinctrl = { diff --git a/target/linux/qualcommax/patches-6.12/0400-mtd-rawnand-add-support-for-TH58NYG3S0HBAI4.patch b/target/linux/qualcommax/patches-6.12/0400-mtd-rawnand-add-support-for-TH58NYG3S0HBAI4.patch deleted file mode 100644 index b393a47707d..00000000000 --- a/target/linux/qualcommax/patches-6.12/0400-mtd-rawnand-add-support-for-TH58NYG3S0HBAI4.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 8d8b37d3af2bdccf0a37d2017d876bfc6ce42552 Mon Sep 17 00:00:00 2001 -From: Chukun Pan -Date: Fri, 20 Oct 2023 23:18:21 +0800 -Subject: [PATCH 1/1] mtd: rawnand: add support for TH58NYG3S0HBAI4 NAND flash - -The Toshiba TH58NYG3S0HBAI4 is detected with 128 byte OOB while the flash -has 256 bytes OOB. Since it is not an ONFI compliant NAND, the model name -cannot be read from anywhere, add a static NAND ID entry to correct this. - -However, the NAND ID of this flash is inconsistent with the datasheet. -The actual NAND ID is only 4 ID bytes, the last ID byte is missing. - -Datasheet available at (the ID table is on page 50): -https://europe.kioxia.com/content/dam/kioxia/newidr/productinfo/datasheet/201910/DST_TH58NYG3S0HBAI4-TDE_EN_31565.pdf - -Datasheet NAND ID: {0x98, 0xa3, 0x91, 0x26, 0x76} -Actual NAND ID: {0x98, 0xa3, 0x91, 0x26} - -It seems that this flash may be counterfeit, but another Toshiba flash -also has the same problem. Maybe the driver has a bug, or some Toshiba -nand flash is like this. Anyway, add a static NAND ID entry with only -4 ID bytes as a hack to make sure it works. - -Tested on Arcadyan AW1000 flashed with OpenWrt. - -Signed-off-by: Chukun Pan - -With 0411-mtd-rawnand-qpic-only-support-max-4-bytes-ID.patch, we can -define 5 bytes ID here. - -Signed-off-by: Ziyang Huang ---- - drivers/mtd/nand/raw/nand_ids.c | 3 +++ - 1 file changed, 3 insertions(+) - ---- a/drivers/mtd/nand/raw/nand_ids.c -+++ b/drivers/mtd/nand/raw/nand_ids.c -@@ -58,6 +58,9 @@ struct nand_flash_dev nand_flash_ids[] = - { .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} }, - SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664, - NAND_ECC_INFO(40, SZ_1K) }, -+ {"TH58NYG3S0HBAI4 8G 1.8V 8-bit", /* Last ID bytes missing */ -+ { .id = {0x98, 0xa3, 0x91, 0x26, 0x76} }, -+ SZ_4K, SZ_1K, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512) }, - {"TH58NVG2S3HBAI4 4G 3.3V 8-bit", - { .id = {0x98, 0xdc, 0x91, 0x15, 0x76} }, - SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) }, diff --git a/target/linux/qualcommax/patches-6.12/0401-spi-spi-qpic-snand-default-to-4-bit-ECC.patch b/target/linux/qualcommax/patches-6.12/0401-spi-spi-qpic-snand-default-to-4-bit-ECC.patch deleted file mode 100644 index 156d4118c76..00000000000 --- a/target/linux/qualcommax/patches-6.12/0401-spi-spi-qpic-snand-default-to-4-bit-ECC.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 2e1ab53e8fcf708db51f560f7846802d406ee57d Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Thu, 15 May 2025 22:47:43 +0200 -Subject: [PATCH] spi: spi-qpic-snand: default to 4-bit ECC - -There are NAND IC-s that define 1-bit ECC as the minimal strength, -however that is unsupported by QPIC-SNAND as it only supports 4 or 8 bit -ECC. - -Since most of these chips also support 4-bit ECC just fine, instead of -erroring out if 1-bit ECC is requested lets instead default to 4-bit ECC. - -Fixes: 01b72ce61e8f ("qualcommax: ipq50xx: remove ECC user config from board files") -Signed-off-by: George Moussalem -Signed-off-by: Robert Marko ---- - drivers/spi/spi-qpic-snand.c | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - ---- a/drivers/spi/spi-qpic-snand.c -+++ b/drivers/spi/spi-qpic-snand.c -@@ -304,6 +304,24 @@ static int qcom_spi_ecc_init_ctx_pipelin - ecc_cfg->spare_bytes = 2; - break; - -+ case 1: -+ case 2: -+ /* -+ * Many chips have set a minimum ECC strength requirement -+ * lower than 4-bits but also support higher strength, so -+ * check if ecc_cfg was set by chip reqs and try 4-bits. -+ */ -+ if (reqs->strength) { -+ dev_warn(snandc->dev, -+ "ECC strength requirement of %u-bit(s) is unsupported, trying 4-bits\n", -+ reqs->strength); -+ ecc_cfg->ecc_mode = ECC_MODE_4BIT; -+ ecc_cfg->ecc_bytes_hw = 7; -+ ecc_cfg->spare_bytes = 4; -+ break; -+ } else -+ fallthrough; -+ - default: - dev_err(snandc->dev, - "only 4 or 8 bits ECC strength is supported\n"); diff --git a/target/linux/qualcommax/patches-6.12/0411-mtd-rawnand-qpic-only-support-max-4-bytes-ID.patch b/target/linux/qualcommax/patches-6.12/0411-mtd-rawnand-qpic-only-support-max-4-bytes-ID.patch deleted file mode 100644 index 6bb112326f2..00000000000 --- a/target/linux/qualcommax/patches-6.12/0411-mtd-rawnand-qpic-only-support-max-4-bytes-ID.patch +++ /dev/null @@ -1,25 +0,0 @@ -From ce4d8b3c5e866a8c515608cf623990e1471a69c7 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Tue, 2 Dec 2025 22:02:30 +0800 -Subject: [PATCH 1/2] mtd: rawnand: qpic only support max 4 bytes ID - -QPIC use a reg (NAND_READ_ID) to transport the ID, so the max bytes of ID is a word (4Bytes). - -https://github.com/torvalds/linux/blob/3a8660878839faadb4f1a6dd72c3179c1df56787/drivers/mtd/nand/raw/qcom_nandc.c#L1783 - -Signed-off-by: Ziyang Huang ---- - drivers/mtd/nand/raw/nand_base.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/mtd/nand/raw/nand_base.c -+++ b/drivers/mtd/nand/raw/nand_base.c -@@ -4965,7 +4965,7 @@ static bool find_full_id_nand(struct nan - - memorg = nanddev_get_memorg(&chip->base); - -- if (!strncmp(type->id, id_data, type->id_len)) { -+ if (!strncmp(type->id, id_data, min(4, type->id_len))) { - memorg->pagesize = type->pagesize; - mtd->writesize = memorg->pagesize; - memorg->pages_per_eraseblock = type->erasesize / diff --git a/target/linux/qualcommax/patches-6.12/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch b/target/linux/qualcommax/patches-6.12/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch deleted file mode 100644 index 35943a660c3..00000000000 --- a/target/linux/qualcommax/patches-6.12/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch +++ /dev/null @@ -1,25 +0,0 @@ -From cf5225a6e279995a574a5ed97cb227b5e076ed8e Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:11 +0800 -Subject: [PATCH 2/2] mtd: spinand: qpic only support max 4 bytes ID - -QPIC use a reg (NAND_READ_ID) to transport the ID, so the max bytes of ID is a word (4Bytes). - -https://github.com/torvalds/linux/blob/4a26e7032d7d57c998598c08a034872d6f0d3945/drivers/spi/spi-qpic-snand.c#L1392-L1393 - -Signed-off-by: Ziyang Huang ---- - drivers/mtd/nand/spi/core.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/mtd/nand/spi/core.c -+++ b/drivers/mtd/nand/spi/core.c -@@ -1337,7 +1337,7 @@ int spinand_match_and_init(struct spinan - if (rdid_method != info->devid.method) - continue; - -- if (memcmp(id + 1, info->devid.id, info->devid.len)) -+ if (memcmp(id + 1, info->devid.id, min(3, info->devid.len))) - continue; - - nand->memorg = table[i].memorg; diff --git a/target/linux/qualcommax/patches-6.12/0715-arm64-dts-qcom-ipq5018-add-vendor-compatible-to-mdio-node.patch b/target/linux/qualcommax/patches-6.12/0715-arm64-dts-qcom-ipq5018-add-vendor-compatible-to-mdio-node.patch deleted file mode 100644 index 7250f934249..00000000000 --- a/target/linux/qualcommax/patches-6.12/0715-arm64-dts-qcom-ipq5018-add-vendor-compatible-to-mdio-node.patch +++ /dev/null @@ -1,20 +0,0 @@ -From: George Moussalem -Date: Wed, 28 May 2025 08:37:28 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: Add vendor compatible to mdio bus - -QCA SSDK needs to register the MDIO bus in its driver. Without a proper reference, -it will crash. Since it tries to look up the MDIO bus using compatible strings that -aren't upstreamed, add the vendor compatible. - -Signed-off-by: George Moussalem ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -219,7 +219,7 @@ - }; - - mdio0: mdio@88000 { -- compatible = "qcom,ipq5018-mdio"; -+ compatible = "qcom,ipq5018-mdio", "qcom,ipq40xx-mdio"; - reg = <0x00088000 0x64>, - <0x019475c4 0x4>; - #address-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.12/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch b/target/linux/qualcommax/patches-6.12/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch deleted file mode 100644 index c6225f62b3e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch +++ /dev/null @@ -1,35 +0,0 @@ -From f71366e0530db2c5cecbbbb6edfbf7344bd6f83b Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH 1/2] clk: gcc-ipq5018: remove the unsupported clk - combination for gmac - -Comment out the unsupported clock combination in the frequency table -for GMAC1. - -Signed-off-by: Ziyang Huang -Signed-off-by: George Moussalem ---- - drivers/clk/qcom/gcc-ipq5018.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq5018.c -+++ b/drivers/clk/qcom/gcc-ipq5018.c -@@ -678,7 +678,7 @@ static const struct freq_tbl ftbl_gmac1_ - F(2500000, P_UNIPHY_RX, 12.5, 0, 0), - F(24000000, P_XO, 1, 0, 0), - F(25000000, P_UNIPHY_RX, 2.5, 0, 0), -- F(125000000, P_UNIPHY_RX, 2.5, 0, 0), -+ /* F(125000000, P_UNIPHY_RX, 2.5, 0, 0), */ - F(125000000, P_UNIPHY_RX, 1, 0, 0), - F(312500000, P_UNIPHY_RX, 1, 0, 0), - { } -@@ -718,7 +718,7 @@ static const struct freq_tbl ftbl_gmac1_ - F(2500000, P_UNIPHY_TX, 12.5, 0, 0), - F(24000000, P_XO, 1, 0, 0), - F(25000000, P_UNIPHY_TX, 2.5, 0, 0), -- F(125000000, P_UNIPHY_TX, 2.5, 0, 0), -+ /* F(125000000, P_UNIPHY_TX, 2.5, 0, 0), */ - F(125000000, P_UNIPHY_TX, 1, 0, 0), - F(312500000, P_UNIPHY_TX, 1, 0, 0), - { } diff --git a/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch b/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch deleted file mode 100644 index f670ef442b0..00000000000 --- a/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: George Moussalem -Date: Fri, 30 May 2025 09:12:23 +0400 -Subject: [PATCH] clk: qcom: gcc-ipq5018: fix UNIPHY soft reset issue - -The SOFT reset is supposed to trigger a resets across the SYS, RX, and -TX lines of the IPQ5018 UNIPHY. So let's set the bitmask of the reset -definition accordingly in the GCC as per the downstream driver. - -Link: https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/036bdc62aca561e8ff94a29c447fc400de2b7304 - -Signed-off-by: George Moussalem ---- a/drivers/clk/qcom/gcc-ipq5018.c -+++ b/drivers/clk/qcom/gcc-ipq5018.c -@@ -3648,7 +3648,7 @@ static const struct qcom_reset_map gcc_i - [GCC_UNIPHY_SYS_ARES] = { 0x56104, 1 }, - [GCC_UNIPHY_RX_ARES] = { 0x56104, 4 }, - [GCC_UNIPHY_TX_ARES] = { 0x56104, 5 }, -- [GCC_UNIPHY_SOFT_RESET] = {0x56104, 0 }, -+ [GCC_UNIPHY_SOFT_RESET] = {0x56104, .bitmask = 0x32 }, - [GCC_USB0_BCR] = { 0x3e070, 0 }, - [GCC_USB0_PHY_BCR] = { 0x3e034, 0 }, - [GCC_WCSS_BCR] = { 0x18000, 0 }, diff --git a/target/linux/qualcommax/patches-6.12/0760-net-phy-aquantia-enable-USXGMII-MAC-autoneg.patch b/target/linux/qualcommax/patches-6.12/0760-net-phy-aquantia-enable-USXGMII-MAC-autoneg.patch deleted file mode 100644 index 6ff9b323381..00000000000 --- a/target/linux/qualcommax/patches-6.12/0760-net-phy-aquantia-enable-USXGMII-MAC-autoneg.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 54280bff5b17407e8b23cdeafdc63f4d43ad1ee5 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Thu, 27 Nov 2025 13:49:06 +0100 -Subject: [PATCH] net: phy: aquantia: enable USXGMII MAC autoneg - -USXGMII MAC autoneg control bit is always set disabled by default, in order -for USXGMII to work properly we must enable it. - -Signed-off-by: Robert Marko ---- - drivers/net/phy/aquantia/aquantia_main.c | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/drivers/net/phy/aquantia/aquantia_main.c -+++ b/drivers/net/phy/aquantia/aquantia_main.c -@@ -46,6 +46,9 @@ - #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7 - #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10 - -+#define MDIO_PHYXS_VEND_PROV2 0xc441 -+#define MDIO_PHYXS_VEND_PROV2_USX_AUTONEG_MAC BIT(3) -+ - #define MDIO_AN_VEND_PROV 0xc400 - #define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) - #define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) -@@ -440,6 +443,10 @@ static int aqr107_read_status(struct phy - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: - phydev->interface = PHY_INTERFACE_MODE_USXGMII; -+ phy_modify_mmd(phydev, MDIO_MMD_PHYXS, -+ MDIO_PHYXS_VEND_PROV2, -+ MDIO_PHYXS_VEND_PROV2_USX_AUTONEG_MAC, -+ MDIO_PHYXS_VEND_PROV2_USX_AUTONEG_MAC); - break; - case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: - phydev->interface = PHY_INTERFACE_MODE_XAUI; diff --git a/target/linux/qualcommax/patches-6.12/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch b/target/linux/qualcommax/patches-6.12/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch deleted file mode 100644 index 81248e517a5..00000000000 --- a/target/linux/qualcommax/patches-6.12/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch +++ /dev/null @@ -1,202 +0,0 @@ -From: Manikanta Mylavarapu -Date: Fri, 10 Nov 2023 14:49:29 +0530 -Subject: [PATCH] dt-bindings: remoteproc: qcom: Add support for multipd model - -Add new binding document for multipd model remoteproc. -IPQ5332, IPQ9574 follows multipd model. - -Signed-off-by: Manikanta Mylavarapu -Reviewed-by: Krzysztof Kozlowski ---- ---- /dev/null -+++ b/Documentation/devicetree/bindings/remoteproc/qcom,multipd-pil.yaml -@@ -0,0 +1,189 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/remoteproc/qcom,multipd-pil.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Qualcomm Multipd Secure Peripheral Image Loader -+ -+maintainers: -+ - Manikanta Mylavarapu -+ -+description: -+ Multipd Peripheral Image Loader loads firmware and boots Q6 protection domain, -+ WCSS protection domain remoteproc's on the Qualcomm IPQ9574, IPQ5332 SoC. -+ Protection domain is similar to process in Linux. Here QDSP6 processor runs -+ each wifi radio functionality on a separate process. One process can't access -+ other process resources, so this is termed as PD i.e protection domain. -+ -+properties: -+ compatible: -+ enum: -+ - qcom,ipq5332-q6-mpd -+ - qcom,ipq9574-q6-mpd -+ -+ reg: -+ maxItems: 1 -+ -+ firmware-name: -+ maxItems: 2 -+ -+ interrupts: -+ items: -+ - description: Watchdog interrupt -+ - description: Fatal interrupt -+ - description: Ready interrupt -+ - description: Handover interrupt -+ - description: Stop acknowledge interrupt -+ -+ interrupt-names: -+ items: -+ - const: wdog -+ - const: fatal -+ - const: ready -+ - const: handover -+ - const: stop-ack -+ -+ qcom,smem-states: -+ $ref: /schemas/types.yaml#/definitions/phandle-array -+ description: States used by the AP to signal the remote processor -+ items: -+ - description: Shutdown Q6 -+ - description: Stop Q6 -+ -+ qcom,smem-state-names: -+ description: -+ Names of the states used by the AP to signal the remote processor -+ items: -+ - const: shutdown -+ - const: stop -+ -+ memory-region: -+ items: -+ - description: Q6 reserved region -+ -+ glink-edge: -+ $ref: /schemas/remoteproc/qcom,glink-edge.yaml# -+ description: -+ Qualcomm G-Link subnode which represents communication edge, channels -+ and devices related to the Modem. -+ unevaluatedProperties: false -+ -+patternProperties: -+ "^pd-1|pd-2|pd-3": -+ type: object -+ description: -+ WCSS means 'wireless connectivity sub system', in simple words it's a -+ wifi radio block. In multipd model, Q6 root protection domain will -+ provide services to WCSS user protection domain. In other words WCSS -+ protection domains depends on Q6 protection domain i.e Q6 should be up -+ before WCSS, similarly Q6 should shut down after all WCSS domains are -+ down. It can be achieved by building parent-child topology between Q6 -+ and WCSS domain's i.e keeping wcss node as sub node of Q6 device node. -+ -+ properties: -+ firmware-name: -+ maxItems: 1 -+ -+ interrupts: -+ items: -+ - description: Fatal interrupt -+ - description: Ready interrupt -+ - description: Spawn acknowledge interrupt -+ - description: Stop acknowledge interrupt -+ -+ interrupt-names: -+ items: -+ - const: fatal -+ - const: ready -+ - const: spawn-ack -+ - const: stop-ack -+ -+ qcom,smem-states: -+ $ref: /schemas/types.yaml#/definitions/phandle-array -+ description: States used by the AP to signal the remote processor -+ items: -+ - description: Shutdown WCSS pd -+ - description: Stop WCSS pd -+ - description: Spawn WCSS pd -+ -+ qcom,smem-state-names: -+ description: -+ Names of the states used by the AP to signal the remote processor -+ items: -+ - const: shutdown -+ - const: stop -+ - const: spawn -+ -+ required: -+ - firmware-name -+ - interrupts -+ - interrupt-names -+ - qcom,smem-states -+ - qcom,smem-state-names -+ -+ additionalProperties: false -+ -+required: -+ - compatible -+ - firmware-name -+ - reg -+ - interrupts -+ - interrupt-names -+ - qcom,smem-states -+ - qcom,smem-state-names -+ - memory-region -+ -+additionalProperties: false -+ -+examples: -+ - | -+ #include -+ q6v5_wcss: remoteproc@d100000 { -+ compatible = "qcom,ipq5332-q6-mpd"; -+ reg = <0xd100000 0x4040>; -+ firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw0.mdt", -+ "ath11k/IPQ5332/hw1.0/iu_fw.mdt"; -+ interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>, -+ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>; -+ interrupt-names = "wdog", -+ "fatal", -+ "ready", -+ "handover", -+ "stop-ack"; -+ -+ qcom,smem-states = <&wcss_smp2p_out 0>, -+ <&wcss_smp2p_out 1>; -+ qcom,smem-state-names = "shutdown", -+ "stop"; -+ -+ memory-region = <&q6_region>; -+ -+ glink-edge { -+ interrupts = ; -+ label = "rtr"; -+ qcom,remote-pid = <1>; -+ mboxes = <&apcs_glb 8>; -+ }; -+ -+ pd-1 { -+ firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw1.mdt"; -+ interrupts-extended = <&wcss_smp2p_in 8 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 9 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 12 IRQ_TYPE_NONE>, -+ <&wcss_smp2p_in 11 IRQ_TYPE_NONE>; -+ interrupt-names = "fatal", -+ "ready", -+ "spawn-ack", -+ "stop-ack"; -+ qcom,smem-states = <&wcss_smp2p_out 8>, -+ <&wcss_smp2p_out 9>, -+ <&wcss_smp2p_out 10>; -+ qcom,smem-state-names = "shutdown", -+ "stop", -+ "spawn"; -+ }; -+ }; diff --git a/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch b/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch deleted file mode 100644 index 438e553e2e4..00000000000 --- a/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 217fbbc122663c5a3dac752cebef44fb3e0cc179 Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -Date: Fri, 10 Nov 2023 14:49:35 +0530 -Subject: [PATCH] firmware: qcom_scm: ipq5332: add msa lock/unlock - support - -IPQ5332 user pd remoteproc firmwares need to be locked -with MSA(modem secure access) features. This patch add -support to lock/unlock MSA features. - -Signed-off-by: Manikanta Mylavarapu ---- - drivers/firmware/qcom//qcom_scm.c | 78 ++++++++++++++++++++++++++ - drivers/firmware/qcom//qcom_scm.h | 2 + - include/linux/firmware/qcom/qcom_scm.h | 2 + - 3 files changed, 82 insertions(+) - ---- a/include/linux/firmware/qcom/qcom_scm.h -+++ b/include/linux/firmware/qcom/qcom_scm.h -@@ -79,6 +79,8 @@ int qcom_scm_pas_mem_setup(u32 periphera - int qcom_scm_pas_auth_and_reset(u32 peripheral); - int qcom_scm_pas_shutdown(u32 peripheral); - bool qcom_scm_pas_supported(u32 peripheral); -+int qcom_scm_msa_lock(u32 peripheral); -+int qcom_scm_msa_unlock(u32 peripheral); - - int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); - int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); ---- a/drivers/firmware/qcom/qcom_scm.c -+++ b/drivers/firmware/qcom/qcom_scm.c -@@ -807,6 +807,84 @@ bool qcom_scm_pas_supported(u32 peripher - } - EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); - -+/** -+ * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA -+ * -+ * @peripheral: peripheral id -+ * -+ * Return 0 on success. -+ */ -+int qcom_scm_msa_lock(u32 peripheral) -+{ -+ int ret; -+ struct qcom_scm_desc desc = { -+ .svc = QCOM_SCM_SVC_PIL, -+ .cmd = QCOM_SCM_MSA_LOCK, -+ .arginfo = QCOM_SCM_ARGS(1), -+ .args[0] = peripheral, -+ .owner = ARM_SMCCC_OWNER_SIP, -+ }; -+ struct qcom_scm_res res; -+ -+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, -+ QCOM_SCM_MSA_LOCK)) -+ return 0; -+ -+ ret = qcom_scm_clk_enable(); -+ if (ret) -+ return ret; -+ -+ ret = qcom_scm_bw_enable(); -+ if (ret) -+ return ret; -+ -+ ret = qcom_scm_call(__scm->dev, &desc, &res); -+ qcom_scm_bw_disable(); -+ qcom_scm_clk_disable(); -+ -+ return ret ? : res.result[0]; -+} -+EXPORT_SYMBOL_GPL(qcom_scm_msa_lock); -+ -+/** -+ * qcom_scm_msa_unlock() - Unlock given peripheral MSA firmware region -+ * -+ * @peripheral: peripheral id -+ * -+ * Return 0 on success. -+ */ -+int qcom_scm_msa_unlock(u32 peripheral) -+{ -+ int ret; -+ struct qcom_scm_desc desc = { -+ .svc = QCOM_SCM_SVC_PIL, -+ .cmd = QCOM_SCM_MSA_UNLOCK, -+ .arginfo = QCOM_SCM_ARGS(1), -+ .args[0] = peripheral, -+ .owner = ARM_SMCCC_OWNER_SIP, -+ }; -+ struct qcom_scm_res res; -+ -+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, -+ QCOM_SCM_MSA_UNLOCK)) -+ return 0; -+ -+ ret = qcom_scm_clk_enable(); -+ if (ret) -+ return ret; -+ -+ ret = qcom_scm_bw_enable(); -+ if (ret) -+ return ret; -+ -+ ret = qcom_scm_call(__scm->dev, &desc, &res); -+ qcom_scm_bw_disable(); -+ qcom_scm_clk_disable(); -+ -+ return ret ? : res.result[0]; -+} -+EXPORT_SYMBOL_GPL(qcom_scm_msa_unlock); -+ - static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) - { - struct qcom_scm_desc desc = { ---- a/drivers/firmware/qcom/qcom_scm.h -+++ b/drivers/firmware/qcom/qcom_scm.h -@@ -102,6 +102,8 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm - #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 - #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a - #define QCOM_SCM_PIL_PAS_INIT_IMAGE_V2 0x1a -+#define QCOM_SCM_MSA_LOCK 0x24 -+#define QCOM_SCM_MSA_UNLOCK 0x25 - - #define QCOM_SCM_SVC_IO 0x05 - #define QCOM_SCM_IO_READ 0x01 diff --git a/target/linux/qualcommax/patches-6.12/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch b/target/linux/qualcommax/patches-6.12/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch deleted file mode 100644 index 1dae38aeaa2..00000000000 --- a/target/linux/qualcommax/patches-6.12/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch +++ /dev/null @@ -1,148 +0,0 @@ -From cae691d32306966065df869fa7424728d1b16b14 Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -Date: Fri, 10 Nov 2023 14:49:36 +0530 -Subject: [PATCH] remoteproc: qcom: q6v5: Add multipd interrupts support - -In multipd model, root & user pd remoteproc's interrupts are -different. User pd needs additional interrupts like spawn. -Instead of going with qcom_q6v5_init(), we defined a new -function to register userpd rproc interrupts in mpd driver. -Since userpd rproc uses some of common interrupts like fatal, -ready, static is removed from ISR handler and used in userpd -interrupt registration. - -Signed-off-by: Manikanta Mylavarapu ---- - drivers/remoteproc/qcom_q6v5.c | 41 +++++++++++++++++++++++++++++++--- - drivers/remoteproc/qcom_q6v5.h | 11 +++++++++ - 2 files changed, 49 insertions(+), 3 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5.c -+++ b/drivers/remoteproc/qcom_q6v5.c -@@ -112,7 +112,7 @@ static irqreturn_t q6v5_wdog_interrupt(i - return IRQ_HANDLED; - } - --static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) -+irqreturn_t q6v5_fatal_interrupt(int irq, void *data) - { - struct qcom_q6v5 *q6v5 = data; - size_t len; -@@ -132,8 +132,9 @@ static irqreturn_t q6v5_fatal_interrupt( - - return IRQ_HANDLED; - } -+EXPORT_SYMBOL_GPL(q6v5_fatal_interrupt); - --static irqreturn_t q6v5_ready_interrupt(int irq, void *data) -+irqreturn_t q6v5_ready_interrupt(int irq, void *data) - { - struct qcom_q6v5 *q6v5 = data; - -@@ -141,6 +142,7 @@ static irqreturn_t q6v5_ready_interrupt( - - return IRQ_HANDLED; - } -+EXPORT_SYMBOL_GPL(q6v5_ready_interrupt); - - /** - * qcom_q6v5_wait_for_start() - wait for remote processor start signal -@@ -179,7 +181,17 @@ static irqreturn_t q6v5_handover_interru - return IRQ_HANDLED; - } - --static irqreturn_t q6v5_stop_interrupt(int irq, void *data) -+irqreturn_t q6v5_spawn_interrupt(int irq, void *data) -+{ -+ struct qcom_q6v5 *q6v5 = data; -+ -+ complete(&q6v5->spawn_done); -+ -+ return IRQ_HANDLED; -+} -+EXPORT_SYMBOL_GPL(q6v5_spawn_interrupt); -+ -+irqreturn_t q6v5_stop_interrupt(int irq, void *data) - { - struct qcom_q6v5 *q6v5 = data; - -@@ -187,6 +199,7 @@ static irqreturn_t q6v5_stop_interrupt(i - - return IRQ_HANDLED; - } -+EXPORT_SYMBOL_GPL(q6v5_stop_interrupt); - - /** - * qcom_q6v5_request_stop() - request the remote processor to stop -@@ -217,6 +230,28 @@ int qcom_q6v5_request_stop(struct qcom_q - EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); - - /** -+ * qcom_q6v5_request_spawn() - request the remote processor to spawn -+ * @q6v5: reference to qcom_q6v5 context -+ * -+ * Return: 0 on success, negative errno on failure -+ */ -+int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5) -+{ -+ int ret; -+ -+ ret = qcom_smem_state_update_bits(q6v5->spawn_state, -+ BIT(q6v5->spawn_bit), BIT(q6v5->spawn_bit)); -+ -+ ret = wait_for_completion_timeout(&q6v5->spawn_done, 5 * HZ); -+ -+ qcom_smem_state_update_bits(q6v5->spawn_state, -+ BIT(q6v5->spawn_bit), 0); -+ -+ return ret == 0 ? -ETIMEDOUT : 0; -+} -+EXPORT_SYMBOL_GPL(qcom_q6v5_request_spawn); -+ -+/** - * qcom_q6v5_panic() - panic handler to invoke a stop on the remote - * @q6v5: reference to qcom_q6v5 context - * ---- a/drivers/remoteproc/qcom_q6v5.h -+++ b/drivers/remoteproc/qcom_q6v5.h -@@ -18,21 +18,27 @@ struct qcom_q6v5 { - - struct qcom_smem_state *state; - struct qmp *qmp; -+ struct qcom_smem_state *shutdown_state; -+ struct qcom_smem_state *spawn_state; - - struct icc_path *path; - - unsigned stop_bit; -+ unsigned shutdown_bit; -+ unsigned spawn_bit; - - int wdog_irq; - int fatal_irq; - int ready_irq; - int handover_irq; - int stop_irq; -+ int spawn_irq; - - bool handover_issued; - - struct completion start_done; - struct completion stop_done; -+ struct completion spawn_done; - - int crash_reason; - -@@ -50,7 +56,12 @@ void qcom_q6v5_deinit(struct qcom_q6v5 * - int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); - int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); - int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon); -+int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5); - int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout); - unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5); -+irqreturn_t q6v5_fatal_interrupt(int irq, void *data); -+irqreturn_t q6v5_ready_interrupt(int irq, void *data); -+irqreturn_t q6v5_spawn_interrupt(int irq, void *data); -+irqreturn_t q6v5_stop_interrupt(int irq, void *data); - - #endif diff --git a/target/linux/qualcommax/patches-6.12/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch b/target/linux/qualcommax/patches-6.12/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch deleted file mode 100644 index 98b49818036..00000000000 --- a/target/linux/qualcommax/patches-6.12/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch +++ /dev/null @@ -1,895 +0,0 @@ -From 3f61ff1fb5c90f8b6c28a3a2b4a29203000ee585 Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -Date: Fri, 10 Nov 2023 14:49:37 +0530 -Subject: [PATCH] remoteproc: qcom: Add Hexagon based multipd rproc - driver - -It adds support to bring up remoteproc's on multipd model. -Pd means protection domain. It's similar to process in Linux. -Here QDSP6 processor runs each wifi radio functionality on a -separate process. One process can't access other process -resources, so this is termed as PD i.e protection domain. - -Here we have two pd's called root and user pd. We can correlate -Root pd as root and user pd as user in linux. Root pd has more -privileges than user pd. Root will provide services to user pd. - ->From remoteproc driver perspective, root pd corresponds to QDSP6 -processor bring up and user pd corresponds to Wifi radio (WCSS) -bring up. - -Here WCSS(user) PD is dependent on Q6(root) PD, so first -q6 pd should be up before wcss pd. After wcss pd goes down, -q6 pd should be turned off. - - APPS QDSP6 -------- ------------- -| | Crash notification | | ------ -| |<---------------------|----------|-------|User| -| | | | |->|PD1 | -| | | ------- | | ------ -| | | | | | | -|Root | Start/stop Q6 | | R | | | -|PD |<---------------------|->| | | | -|rproc| Crash notification | | O | | | -| | | | | | | -|User |Start/stop UserPD1 | | O | | | -|PD1 |----------------------|->| |-|----| -|rproc| | | T | | | -| | | | | | | -|User |Start/stop UserPD2 | | P | | | -|PD2 |----------------------|->| |-|----| -|rproc| | | D | | | -| | | ------- | | ------ -| | Crash notification | | |->|User| -| |<---------------------|----------|-------|PD2 | -------- | | ------ - ------------ - -IPQ5332, IPQ9574 supports multipd remoteproc driver. - -Signed-off-by: Manikanta Mylavarapu ---- - drivers/remoteproc/Kconfig | 19 + - drivers/remoteproc/Makefile | 1 + - drivers/remoteproc/qcom_q6v5_mpd.c | 802 +++++++++++++++++++++++++++++ - 3 files changed, 822 insertions(+) - create mode 100644 drivers/remoteproc/qcom_q6v5_mpd.c - ---- a/drivers/remoteproc/Kconfig -+++ b/drivers/remoteproc/Kconfig -@@ -235,6 +235,25 @@ config QCOM_Q6V5_PAS - CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and - SLPI (Sensor Low Power Island). - -+config QCOM_Q6V5_MPD -+ tristate "Qualcomm Hexagon based MPD model Peripheral Image Loader" -+ depends on OF && ARCH_QCOM -+ depends on QCOM_SMEM -+ depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n -+ depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n -+ depends on QCOM_SYSMON || QCOM_SYSMON=n -+ depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n -+ depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n -+ select QCOM_MDT_LOADER -+ select QCOM_PIL_INFO -+ select QCOM_Q6V5_COMMON -+ select QCOM_RPROC_COMMON -+ select QCOM_SCM -+ help -+ Say y here to support the Qualcomm Secure Peripheral Image Loader -+ for the Hexagon based MultiPD model remote processors on e.g. IPQ5018. -+ This is trustZone wireless subsystem. -+ - config QCOM_Q6V5_WCSS - tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader" - depends on OF && ARCH_QCOM ---- a/drivers/remoteproc/Makefile -+++ b/drivers/remoteproc/Makefile -@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil - obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o - obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o - obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o -+obj-$(CONFIG_QCOM_Q6V5_MPD) += qcom_q6v5_mpd.o - obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o - obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o - obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o ---- /dev/null -+++ b/drivers/remoteproc/qcom_q6v5_mpd.c -@@ -0,0 +1,796 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2016-2018 Linaro Ltd. -+ * Copyright (C) 2014 Sony Mobile Communications AB -+ * Copyright (c) 2012-2018, 2021 The Linux Foundation. All rights reserved. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "qcom_common.h" -+#include "qcom_q6v5.h" -+ -+#include "remoteproc_internal.h" -+ -+#define WCSS_CRASH_REASON 421 -+#define WCSS_SMEM_HOST 1 -+ -+#define WCNSS_PAS_ID 6 -+#define MPD_WCNSS_PAS_ID 0xD -+ -+#define BUF_SIZE 35 -+ -+#define MAX_FIRMWARE 3 -+ -+#define RPD_SWID MPD_WCNSS_PAS_ID -+#define UPD_SWID 0x12 -+#define REMOTE_PID 1 -+#define UPD_BOOT_INFO_SMEM_SIZE 4096 -+#define UPD_BOOT_INFO_HEADER_TYPE 0x2 -+#define UPD_BOOT_INFO_SMEM_ID 507 -+#define VERSION2 2 -+ -+static LIST_HEAD(upd_rproc_list); -+enum { -+ Q6_IPQ, -+ WCSS_IPQ, -+}; -+ -+/** -+ * struct userpd_boot_info_header - header of user pd bootinfo -+ * @type: type of bootinfo passing over smem -+ * @length: length of header in bytes -+ */ -+struct userpd_boot_info_header { -+ u8 type; -+ u8 length; -+}; -+ -+/** -+ * struct userpd_boot_info - holds info required to boot user pd -+ * @header: pointer to header -+ * @pid: unique id represents each user pd process -+ * @bootaddr: load address of user pd firmware -+ * @data_size: user pd firmware memory size -+ */ -+struct userpd_boot_info { -+ struct userpd_boot_info_header header; -+ u8 pid; -+ u32 bootaddr; -+ u32 data_size; -+} __packed; -+ -+struct q6_wcss { -+ struct device *dev; -+ struct qcom_rproc_glink glink_subdev; -+ struct qcom_rproc_ssr ssr_subdev; -+ struct qcom_q6v5 q6; -+ phys_addr_t mem_phys; -+ phys_addr_t mem_reloc; -+ void *mem_region; -+ size_t mem_size; -+ u8 pd_asid; -+ const struct wcss_data *desc; -+ const char **firmware; -+ u32 version; -+}; -+ -+struct wcss_data { -+ u32 pasid; -+ bool share_upd_info_to_q6; -+}; -+ -+/** -+ * qcom_get_pd_asid() - get the pd asid number from PD spawn bit -+ * @rproc: rproc handle -+ * -+ * Returns asid on success -+ */ -+static u8 qcom_get_pd_asid(struct rproc *rproc) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ u8 bit = wcss->q6.spawn_bit; -+ -+ return bit / 8; -+} -+ -+static int q6_wcss_start(struct rproc *rproc) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ int ret; -+ const struct wcss_data *desc = wcss->desc; -+ -+ qcom_q6v5_prepare(&wcss->q6); -+ -+ ret = qcom_scm_pas_auth_and_reset(desc->pasid); -+ if (ret) { -+ dev_err(wcss->dev, "wcss_reset failed\n"); -+ return ret; -+ } -+ -+ ret = qcom_q6v5_wait_for_start(&wcss->q6, 5 * HZ); -+ if (ret == -ETIMEDOUT) -+ dev_err(wcss->dev, "start timed out\n"); -+ -+ return ret; -+} -+ -+static int q6_wcss_spawn_pd(struct rproc *rproc) -+{ -+ int ret; -+ struct q6_wcss *wcss = rproc->priv; -+ -+ ret = qcom_q6v5_request_spawn(&wcss->q6); -+ if (ret == -ETIMEDOUT) { -+ dev_err(wcss->dev, "%s spawn timedout\n", rproc->name); -+ return ret; -+ } -+ -+ ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000)); -+ if (ret == -ETIMEDOUT) { -+ dev_err(wcss->dev, "%s start timedout\n", rproc->name); -+ wcss->q6.running = false; -+ return ret; -+ } -+ wcss->q6.running = true; -+ return ret; -+} -+ -+static int wcss_pd_start(struct rproc *rproc) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; -+ int ret; -+ -+ ret = qcom_scm_msa_lock(pasid); -+ if (ret) { -+ dev_err(wcss->dev, "failed to power up pd\n"); -+ return ret; -+ } -+ -+ if (wcss->q6.spawn_bit) { -+ ret = q6_wcss_spawn_pd(rproc); -+ if (ret) -+ return ret; -+ } -+ -+ return ret; -+} -+ -+static int q6_wcss_stop(struct rproc *rproc) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ const struct wcss_data *desc = wcss->desc; -+ int ret; -+ -+ ret = qcom_scm_pas_shutdown(desc->pasid); -+ if (ret) { -+ dev_err(wcss->dev, "not able to shutdown\n"); -+ return ret; -+ } -+ qcom_q6v5_unprepare(&wcss->q6); -+ -+ return 0; -+} -+ -+/** -+ * wcss_pd_stop() - Stop WCSS user pd -+ * @rproc: rproc handle -+ * -+ * Stop root pd after user pd down. Root pd -+ * is used to provide services to user pd, so -+ * keeping root pd alive when user pd is down -+ * is invalid. -+ * --------------------------------------------- -+ * -+ * ----------- -+ * |-------->| User PD1 | -+ * | ----------- -+ * | -+ * | -+ * ----- | ----------- -+ * | Q6 |---------------->| User Pd2 | -+ * ----- | ----------- -+ * | -+ * | -+ * | ----------- -+ * |--------->| User Pd3 | -+ * ----------- -+ * ---------------------------------------------- -+ */ -+static int wcss_pd_stop(struct rproc *rproc) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); -+ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; -+ int ret; -+ -+ if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) { -+ ret = qcom_q6v5_request_stop(&wcss->q6, NULL); -+ if (ret) { -+ dev_err(&rproc->dev, "pd not stopped\n"); -+ return ret; -+ } -+ } -+ -+ ret = qcom_scm_msa_unlock(pasid); -+ if (ret) { -+ dev_err(wcss->dev, "failed to power down pd\n"); -+ return ret; -+ } -+ -+ rproc_shutdown(rpd_rproc); -+ -+ return 0; -+} -+ -+static void *q6_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, -+ bool *is_iomem) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ int offset; -+ -+ offset = da - wcss->mem_reloc; -+ if (offset < 0 || offset + len > wcss->mem_size) -+ return NULL; -+ -+ return wcss->mem_region + offset; -+} -+ -+/** -+ * share_upd_bootinfo_to_q6() - Share userpd boot info to Q6 root pd -+ * @rproc: rproc handle -+ * -+ * Q6 needs user pd parameters like loadaddress and -+ * PIL size to authenticate user pd with underlying -+ * security software. If authenticatoin success then -+ * only Q6 spawns user pd and sends spawn ack to rproc -+ * driver. This API is passing userpd boot info to Q6 -+ * over SMEM. -+ * -+ * User pd boot-info format mentioned below -+ *
-+ * -+ * -+ * Returns 0 on success else negative value on failure. -+ */ -+static int share_upd_bootinfo_to_q6(struct rproc *rproc) -+{ -+ int ret; -+ size_t size; -+ u16 cnt = 0, version; -+ void *ptr; -+ struct q6_wcss *wcss = rproc->priv, *upd_wcss; -+ struct rproc *upd_rproc; -+ struct userpd_boot_info upd_bootinfo = {0}; -+ const struct firmware *fw; -+ -+ ret = qcom_smem_alloc(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, -+ UPD_BOOT_INFO_SMEM_SIZE); -+ if (ret && ret != -EEXIST) { -+ dev_err(wcss->dev, -+ "failed to allocate q6 bootinfo smem segment\n"); -+ return ret; -+ } -+ -+ ptr = qcom_smem_get(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, &size); -+ if (IS_ERR(ptr) || size != UPD_BOOT_INFO_SMEM_SIZE) { -+ dev_err(wcss->dev, -+ "Unable to acquire smp2p item(%d) ret:%ld\n", -+ UPD_BOOT_INFO_SMEM_ID, PTR_ERR(ptr)); -+ return PTR_ERR(ptr); -+ } -+ -+ /*Version*/ -+ version = VERSION2; -+ memcpy_toio(ptr, &version, sizeof(version)); -+ ptr += sizeof(version); -+ -+ list_for_each_entry(upd_rproc, &upd_rproc_list, node) -+ cnt++; -+ -+ /* No of elements */ -+ cnt = (sizeof(upd_bootinfo) * cnt); -+ memcpy_toio(ptr, &cnt, sizeof(u16)); -+ ptr += sizeof(u16); -+ -+ list_for_each_entry(upd_rproc, &upd_rproc_list, node) { -+ upd_wcss = upd_rproc->priv; -+ -+ /* TYPE */ -+ upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE; -+ -+ /* LENGTH */ -+ upd_bootinfo.header.length = -+ sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header); -+ -+ /* Process ID */ -+ upd_bootinfo.pid = upd_wcss->pd_asid + 1; -+ -+ ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev); -+ if (ret < 0) { -+ dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* Load address */ -+ upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw); -+ -+ /* Firmware mem size */ -+ upd_bootinfo.data_size = qcom_mdt_get_size(fw); -+ -+ release_firmware(fw); -+ -+ /* copy into smem */ -+ memcpy_toio(ptr, &upd_bootinfo, sizeof(upd_bootinfo)); -+ ptr += sizeof(upd_bootinfo); -+ } -+ return 0; -+} -+ -+static int q6_wcss_load(struct rproc *rproc, const struct firmware *fw) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ const struct firmware *fw_hdl; -+ int ret; -+ const struct wcss_data *desc = wcss->desc; -+ int loop; -+ -+ /* Share user pd boot info to Q6 remote processor */ -+ if (desc->share_upd_info_to_q6) { -+ ret = share_upd_bootinfo_to_q6(rproc); -+ if (ret) { -+ dev_err(wcss->dev, -+ "user pd boot info sharing with q6 failed %d\n", -+ ret); -+ return ret; -+ } -+ } -+ -+ ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, -+ desc->pasid, wcss->mem_region, -+ wcss->mem_phys, wcss->mem_size, -+ &wcss->mem_reloc); -+ if (ret) -+ return ret; -+ -+ for (loop = 1; loop < MAX_FIRMWARE; loop++) { -+ if (!wcss->firmware[loop]) -+ continue; -+ -+ ret = request_firmware(&fw_hdl, wcss->firmware[loop], -+ wcss->dev); -+ if (ret) -+ continue; -+ -+ ret = qcom_mdt_load_no_init(wcss->dev, fw_hdl, -+ wcss->firmware[loop], 0, -+ wcss->mem_region, -+ wcss->mem_phys, -+ wcss->mem_size, -+ &wcss->mem_reloc); -+ -+ release_firmware(fw_hdl); -+ -+ if (ret) { -+ dev_err(wcss->dev, -+ "can't load %s ret:%d\n", wcss->firmware[loop], ret); -+ return ret; -+ } -+ } -+ return 0; -+} -+ -+/** -+ * wcss_pd_load() - Load WCSS user pd firmware -+ * @rproc: rproc handle -+ * @fw: firmware handle -+ * -+ * User pd get services from root pd. So first -+ * bring up root pd and then load userpd firmware. -+ * --------------------------------------------- -+ * -+ * ----------- -+ * |-------->| User PD1 | -+ * | ----------- -+ * | -+ * | -+ * ----- | ----------- -+ * | Q6 |---------------->| User Pd2 | -+ * ----- | ----------- -+ * | -+ * | -+ * | ----------- -+ * |--------->| User Pd3 | -+ * ----------- -+ * ---------------------------------------------- -+ * -+ */ -+static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); -+ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; -+ int ret; -+ -+ ret = rproc_boot(rpd_rproc); -+ if (ret) -+ return ret; -+ -+ return qcom_mdt_load(wcss->dev, fw, rproc->firmware, -+ pasid, wcss->mem_region, -+ wcss->mem_phys, wcss->mem_size, -+ &wcss->mem_reloc); -+} -+ -+static unsigned long q6_wcss_panic(struct rproc *rproc) -+{ -+ struct q6_wcss *wcss = rproc->priv; -+ -+ return qcom_q6v5_panic(&wcss->q6); -+} -+ -+static const struct rproc_ops wcss_ops = { -+ .start = wcss_pd_start, -+ .stop = wcss_pd_stop, -+ .load = wcss_pd_load, -+ .get_boot_addr = rproc_elf_get_boot_addr, -+}; -+ -+static const struct rproc_ops q6_wcss_ops = { -+ .start = q6_wcss_start, -+ .stop = q6_wcss_stop, -+ .da_to_va = q6_wcss_da_to_va, -+ .load = q6_wcss_load, -+ .get_boot_addr = rproc_elf_get_boot_addr, -+ .panic = q6_wcss_panic, -+}; -+ -+static int q6_alloc_memory_region(struct q6_wcss *wcss) -+{ -+ struct reserved_mem *rmem = NULL; -+ struct device_node *node; -+ struct device *dev = wcss->dev; -+ -+ if (wcss->version == Q6_IPQ) { -+ node = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (node) -+ rmem = of_reserved_mem_lookup(node); -+ -+ of_node_put(node); -+ -+ if (!rmem) { -+ dev_err(dev, "unable to acquire memory-region\n"); -+ return -EINVAL; -+ } -+ } else { -+ struct rproc *rpd_rproc = dev_get_drvdata(dev->parent); -+ struct q6_wcss *rpd_wcss = rpd_rproc->priv; -+ -+ wcss->mem_phys = rpd_wcss->mem_phys; -+ wcss->mem_reloc = rpd_wcss->mem_reloc; -+ wcss->mem_size = rpd_wcss->mem_size; -+ wcss->mem_region = rpd_wcss->mem_region; -+ return 0; -+ } -+ -+ wcss->mem_phys = rmem->base; -+ wcss->mem_reloc = rmem->base; -+ wcss->mem_size = rmem->size; -+ wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size); -+ if (!wcss->mem_region) { -+ dev_err(dev, "unable to map memory region: %pa+%pa\n", -+ &rmem->base, &rmem->size); -+ return -EBUSY; -+ } -+ -+ return 0; -+} -+ -+static int q6_get_inbound_irq(struct qcom_q6v5 *q6, -+ struct platform_device *pdev, -+ const char *int_name, -+ int index, int *pirq, -+ irqreturn_t (*handler)(int irq, void *data)) -+{ -+ int ret, irq; -+ char *interrupt, *tmp = (char *)int_name; -+ struct q6_wcss *wcss = q6->rproc->priv; -+ -+ irq = platform_get_irq(pdev, index); -+ if (irq < 0) -+ return irq; -+ -+ *pirq = irq; -+ -+ interrupt = devm_kzalloc(&pdev->dev, BUF_SIZE, GFP_KERNEL); -+ if (!interrupt) -+ return -ENOMEM; -+ -+ snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp); -+ -+ ret = devm_request_threaded_irq(&pdev->dev, *pirq, -+ NULL, handler, -+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, -+ interrupt, q6); -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "failed to acquire %s irq\n", interrupt); -+ return 0; -+} -+ -+static int q6_get_outbound_irq(struct qcom_q6v5 *q6, -+ struct platform_device *pdev, -+ const char *int_name) -+{ -+ struct qcom_smem_state *tmp_state; -+ unsigned bit; -+ -+ tmp_state = qcom_smem_state_get(&pdev->dev, int_name, &bit); -+ if (IS_ERR(tmp_state)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(tmp_state), -+ "failed to acquire %s state\n", int_name); -+ -+ if (!strcmp(int_name, "stop")) { -+ q6->state = tmp_state; -+ q6->stop_bit = bit; -+ } else if (!strcmp(int_name, "spawn")) { -+ q6->spawn_state = tmp_state; -+ q6->spawn_bit = bit; -+ } -+ -+ return 0; -+} -+ -+static int init_irq(struct qcom_q6v5 *q6, -+ struct platform_device *pdev, struct rproc *rproc, -+ int crash_reason, const char *load_state, -+ void (*handover)(struct qcom_q6v5 *q6)) -+{ -+ int ret; -+ struct q6_wcss *wcss = rproc->priv; -+ -+ q6->rproc = rproc; -+ q6->dev = &pdev->dev; -+ q6->crash_reason = crash_reason; -+ q6->handover = handover; -+ -+ init_completion(&q6->start_done); -+ init_completion(&q6->stop_done); -+ init_completion(&q6->spawn_done); -+ -+ ret = q6_get_outbound_irq(q6, pdev, "stop"); -+ if (ret) -+ return ret; -+ -+ ret = q6_get_outbound_irq(q6, pdev, "spawn"); -+ if (ret) -+ return ret; -+ -+ /* Get pd_asid to prepare interrupt names */ -+ wcss->pd_asid = qcom_get_pd_asid(rproc); -+ -+ ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq, -+ q6v5_fatal_interrupt); -+ if (ret) -+ return ret; -+ -+ ret = q6_get_inbound_irq(q6, pdev, "ready", 1, &q6->ready_irq, -+ q6v5_ready_interrupt); -+ if (ret) -+ return ret; -+ -+ ret = q6_get_inbound_irq(q6, pdev, "stop-ack", 3, &q6->stop_irq, -+ q6v5_stop_interrupt); -+ if (ret) -+ return ret; -+ -+ ret = q6_get_inbound_irq(q6, pdev, "spawn-ack", 2, &q6->spawn_irq, -+ q6v5_spawn_interrupt); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static void q6_release_resources(void) -+{ -+ struct rproc *upd_rproc; -+ -+ /* Release userpd resources */ -+ list_for_each_entry(upd_rproc, &upd_rproc_list, node) { -+ rproc_del(upd_rproc); -+ rproc_free(upd_rproc); -+ } -+} -+ -+static int q6_register_userpd(struct platform_device *pdev, -+ struct device_node *userpd_np) -+{ -+ struct q6_wcss *wcss; -+ struct rproc *rproc = NULL; -+ int ret; -+ struct platform_device *userpd_pdev; -+ const char *firmware_name = NULL; -+ const char *label = NULL; -+ -+ ret = of_property_read_string(userpd_np, "firmware-name", -+ &firmware_name); -+ if (ret < 0) { -+ /* All userpd's who want to register as rproc must have firmware. -+ * Other than userpd like glink they don't need any firmware. -+ * So for glink child simply return success. -+ */ -+ if (ret == -EINVAL) { -+ /* Confirming userpd_np is glink node or not */ -+ if (!of_property_read_string(userpd_np, "label", &label)) -+ return 0; -+ } -+ return ret; -+ } -+ -+ dev_info(&pdev->dev, "%s node found\n", userpd_np->name); -+ -+ userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name, -+ &pdev->dev); -+ if (!userpd_pdev) -+ return dev_err_probe(&pdev->dev, -ENODEV, -+ "failed to create %s platform device\n", -+ userpd_np->name); -+ -+ userpd_pdev->dev.driver = pdev->dev.driver; -+ rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops, -+ firmware_name, sizeof(*wcss)); -+ if (!rproc) { -+ ret = -ENOMEM; -+ goto free_rproc; -+ } -+ -+ wcss = rproc->priv; -+ wcss->dev = &userpd_pdev->dev; -+ wcss->version = WCSS_IPQ; -+ -+ ret = q6_alloc_memory_region(wcss); -+ if (ret) -+ goto free_rproc; -+ -+ ret = init_irq(&wcss->q6, userpd_pdev, rproc, -+ WCSS_CRASH_REASON, NULL, NULL); -+ if (ret) -+ goto free_rproc; -+ -+ rproc->auto_boot = false; -+ ret = rproc_add(rproc); -+ if (ret) -+ goto free_rproc; -+ -+ list_add(&rproc->node, &upd_rproc_list); -+ platform_set_drvdata(userpd_pdev, rproc); -+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name); -+ return 0; -+ -+free_rproc: -+ kfree(rproc); -+ return ret; -+} -+ -+static int q6_wcss_probe(struct platform_device *pdev) -+{ -+ struct q6_wcss *wcss; -+ struct rproc *rproc; -+ int ret; -+ const char **firmware; -+ struct device_node *userpd_np; -+ const struct rproc_ops *ops = &q6_wcss_ops; -+ -+ firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE, -+ sizeof(*firmware), GFP_KERNEL); -+ if (!firmware) -+ return -ENOMEM; -+ -+ ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name", -+ firmware, MAX_FIRMWARE); -+ if (ret < 0) -+ return ret; -+ -+ rproc = rproc_alloc(&pdev->dev, pdev->name, ops, -+ firmware[0], sizeof(*wcss)); -+ if (!rproc) -+ return -ENOMEM; -+ -+ wcss = rproc->priv; -+ wcss->dev = &pdev->dev; -+ wcss->desc = of_device_get_match_data(&pdev->dev); -+ wcss->firmware = firmware; -+ wcss->version = Q6_IPQ; -+ -+ ret = q6_alloc_memory_region(wcss); -+ if (ret) -+ goto free_rproc; -+ -+ ret = qcom_q6v5_init(&wcss->q6, pdev, rproc, -+ WCSS_CRASH_REASON, NULL, NULL); -+ if (ret) -+ goto free_rproc; -+ -+ qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss"); -+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); -+ -+ rproc->auto_boot = false; -+ ret = rproc_add(rproc); -+ if (ret) -+ goto free_rproc; -+ -+ platform_set_drvdata(pdev, rproc); -+ -+ /* Iterate over userpd child's and register with rproc */ -+ for_each_available_child_of_node(pdev->dev.of_node, userpd_np) { -+ ret = q6_register_userpd(pdev, userpd_np); -+ if (ret) { -+ /* release resources of successfully allocated userpd rproc's */ -+ q6_release_resources(); -+ return dev_err_probe(&pdev->dev, ret, -+ "Failed to register userpd(%s)\n", -+ userpd_np->name); -+ } -+ } -+ return 0; -+ -+free_rproc: -+ rproc_free(rproc); -+ -+ return ret; -+} -+ -+static void q6_wcss_remove(struct platform_device *pdev) -+{ -+ struct rproc *rproc = platform_get_drvdata(pdev); -+ struct q6_wcss *wcss = rproc->priv; -+ -+ qcom_q6v5_deinit(&wcss->q6); -+ -+ rproc_del(rproc); -+ rproc_free(rproc); -+} -+ -+static const struct wcss_data q6_ipq5332_res_init = { -+ .pasid = MPD_WCNSS_PAS_ID, -+ .share_upd_info_to_q6 = true, -+}; -+ -+static const struct wcss_data q6_ipq9574_res_init = { -+ .pasid = WCNSS_PAS_ID, -+}; -+ -+static const struct of_device_id q6_wcss_of_match[] = { -+ { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init }, -+ { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, q6_wcss_of_match); -+ -+static struct platform_driver q6_wcss_driver = { -+ .probe = q6_wcss_probe, -+ .remove_new = q6_wcss_remove, -+ .driver = { -+ .name = "qcom-q6-mpd", -+ .of_match_table = q6_wcss_of_match, -+ }, -+}; -+module_platform_driver(q6_wcss_driver); -+ -+MODULE_DESCRIPTION("Hexagon WCSS Multipd Peripheral Image Loader"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/qualcommax/patches-6.12/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch b/target/linux/qualcommax/patches-6.12/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch deleted file mode 100644 index 5f403585e0e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch +++ /dev/null @@ -1,321 +0,0 @@ -From 6c66dff196cbba8515380110dd3599cde31dd896 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH 1/2] rproc: qcom_q6v5_mpd: split q6_wcss to rootpd and - userpd - -Split the q6_wcss structure and create a separate userpd struct to clearly -differentiate between the process to bring up the QDSP6 processor vs -process(es) to bring up the Wifi radio(s) (WCSS) for better readability. - -Signed-off-by: Ziyang Huang -Signed-off-by: George Moussalem ---- - drivers/remoteproc/qcom_q6v5_mpd.c | 126 +++++++++++++---------------- - 1 file changed, 56 insertions(+), 70 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_mpd.c -+++ b/drivers/remoteproc/qcom_q6v5_mpd.c -@@ -45,10 +45,6 @@ - #define VERSION2 2 - - static LIST_HEAD(upd_rproc_list); --enum { -- Q6_IPQ, -- WCSS_IPQ, --}; - - /** - * struct userpd_boot_info_header - header of user pd bootinfo -@@ -83,10 +79,15 @@ struct q6_wcss { - phys_addr_t mem_reloc; - void *mem_region; - size_t mem_size; -- u8 pd_asid; - const struct wcss_data *desc; - const char **firmware; -- u32 version; -+}; -+ -+struct userpd { -+ u8 pd_asid; -+ struct device *dev; -+ struct qcom_rproc_ssr ssr_subdev; -+ struct qcom_q6v5 q6; - }; - - struct wcss_data { -@@ -102,8 +103,8 @@ struct wcss_data { - */ - static u8 qcom_get_pd_asid(struct rproc *rproc) - { -- struct q6_wcss *wcss = rproc->priv; -- u8 bit = wcss->q6.spawn_bit; -+ struct userpd *upd = rproc->priv; -+ u8 bit = upd->q6.spawn_bit; - - return bit / 8; - } -@@ -132,37 +133,37 @@ static int q6_wcss_start(struct rproc *r - static int q6_wcss_spawn_pd(struct rproc *rproc) - { - int ret; -- struct q6_wcss *wcss = rproc->priv; -+ struct userpd *upd = rproc->priv; - -- ret = qcom_q6v5_request_spawn(&wcss->q6); -+ ret = qcom_q6v5_request_spawn(&upd->q6); - if (ret == -ETIMEDOUT) { -- dev_err(wcss->dev, "%s spawn timedout\n", rproc->name); -+ dev_err(upd->dev, "%s spawn timedout\n", rproc->name); - return ret; - } - -- ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000)); -+ ret = qcom_q6v5_wait_for_start(&upd->q6, msecs_to_jiffies(10000)); - if (ret == -ETIMEDOUT) { -- dev_err(wcss->dev, "%s start timedout\n", rproc->name); -- wcss->q6.running = false; -+ dev_err(upd->dev, "%s start timedout\n", rproc->name); -+ upd->q6.running = false; - return ret; - } -- wcss->q6.running = true; -+ upd->q6.running = true; - return ret; - } - - static int wcss_pd_start(struct rproc *rproc) - { -- struct q6_wcss *wcss = rproc->priv; -- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; -+ struct userpd *upd = rproc->priv; -+ u32 pasid = (upd->pd_asid << 8) | UPD_SWID; - int ret; - - ret = qcom_scm_msa_lock(pasid); - if (ret) { -- dev_err(wcss->dev, "failed to power up pd\n"); -+ dev_err(upd->dev, "failed to power up pd\n"); - return ret; - } - -- if (wcss->q6.spawn_bit) { -+ if (upd->q6.spawn_bit) { - ret = q6_wcss_spawn_pd(rproc); - if (ret) - return ret; -@@ -214,22 +215,22 @@ static int q6_wcss_stop(struct rproc *rp - */ - static int wcss_pd_stop(struct rproc *rproc) - { -- struct q6_wcss *wcss = rproc->priv; -- struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); -- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; -+ struct userpd *upd = rproc->priv; -+ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); -+ u32 pasid = (upd->pd_asid << 8) | UPD_SWID; - int ret; - -- if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) { -- ret = qcom_q6v5_request_stop(&wcss->q6, NULL); -+ if (rproc->state != RPROC_CRASHED && upd->q6.stop_bit) { -+ ret = qcom_q6v5_request_stop(&upd->q6, NULL); - if (ret) { -- dev_err(&rproc->dev, "pd not stopped\n"); -+ dev_err(upd->dev, "pd not stopped\n"); - return ret; - } - } - - ret = qcom_scm_msa_unlock(pasid); - if (ret) { -- dev_err(wcss->dev, "failed to power down pd\n"); -+ dev_err(upd->dev, "failed to power down pd\n"); - return ret; - } - -@@ -274,7 +275,8 @@ static int share_upd_bootinfo_to_q6(stru - size_t size; - u16 cnt = 0, version; - void *ptr; -- struct q6_wcss *wcss = rproc->priv, *upd_wcss; -+ struct q6_wcss *wcss = rproc->priv; -+ struct userpd *upd; - struct rproc *upd_rproc; - struct userpd_boot_info upd_bootinfo = {0}; - const struct firmware *fw; -@@ -309,7 +311,7 @@ static int share_upd_bootinfo_to_q6(stru - ptr += sizeof(u16); - - list_for_each_entry(upd_rproc, &upd_rproc_list, node) { -- upd_wcss = upd_rproc->priv; -+ upd = upd_rproc->priv; - - /* TYPE */ - upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE; -@@ -319,11 +321,11 @@ static int share_upd_bootinfo_to_q6(stru - sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header); - - /* Process ID */ -- upd_bootinfo.pid = upd_wcss->pd_asid + 1; -+ upd_bootinfo.pid = upd->pd_asid + 1; - -- ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev); -+ ret = request_firmware(&fw, upd_rproc->firmware, upd->dev); - if (ret < 0) { -- dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret); -+ dev_err(upd->dev, "request_firmware failed: %d\n", ret); - return ret; - } - -@@ -422,19 +424,20 @@ static int q6_wcss_load(struct rproc *rp - */ - static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw) - { -- struct q6_wcss *wcss = rproc->priv; -- struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); -- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; -+ struct userpd *upd = rproc->priv; -+ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); -+ struct q6_wcss *wcss = rpd_rproc->priv; -+ u32 pasid = (upd->pd_asid << 8) | UPD_SWID; - int ret; - - ret = rproc_boot(rpd_rproc); - if (ret) - return ret; - -- return qcom_mdt_load(wcss->dev, fw, rproc->firmware, -+ return qcom_mdt_load(upd->dev, fw, rproc->firmware, - pasid, wcss->mem_region, - wcss->mem_phys, wcss->mem_size, -- &wcss->mem_reloc); -+ NULL); - } - - static unsigned long q6_wcss_panic(struct rproc *rproc) -@@ -466,26 +469,15 @@ static int q6_alloc_memory_region(struct - struct device_node *node; - struct device *dev = wcss->dev; - -- if (wcss->version == Q6_IPQ) { -- node = of_parse_phandle(dev->of_node, "memory-region", 0); -- if (node) -- rmem = of_reserved_mem_lookup(node); -- -- of_node_put(node); -- -- if (!rmem) { -- dev_err(dev, "unable to acquire memory-region\n"); -- return -EINVAL; -- } -- } else { -- struct rproc *rpd_rproc = dev_get_drvdata(dev->parent); -- struct q6_wcss *rpd_wcss = rpd_rproc->priv; -- -- wcss->mem_phys = rpd_wcss->mem_phys; -- wcss->mem_reloc = rpd_wcss->mem_reloc; -- wcss->mem_size = rpd_wcss->mem_size; -- wcss->mem_region = rpd_wcss->mem_region; -- return 0; -+ node = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (node) -+ rmem = of_reserved_mem_lookup(node); -+ -+ of_node_put(node); -+ -+ if (!rmem) { -+ dev_err(dev, "unable to acquire memory-region\n"); -+ return -EINVAL; - } - - wcss->mem_phys = rmem->base; -@@ -509,7 +501,7 @@ static int q6_get_inbound_irq(struct qco - { - int ret, irq; - char *interrupt, *tmp = (char *)int_name; -- struct q6_wcss *wcss = q6->rproc->priv; -+ struct userpd *upd = q6->rproc->priv; - - irq = platform_get_irq(pdev, index); - if (irq < 0) -@@ -521,7 +513,7 @@ static int q6_get_inbound_irq(struct qco - if (!interrupt) - return -ENOMEM; - -- snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp); -+ snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", upd->pd_asid, tmp); - - ret = devm_request_threaded_irq(&pdev->dev, *pirq, - NULL, handler, -@@ -562,7 +554,7 @@ static int init_irq(struct qcom_q6v5 *q6 - void (*handover)(struct qcom_q6v5 *q6)) - { - int ret; -- struct q6_wcss *wcss = rproc->priv; -+ struct userpd *upd = rproc->priv; - - q6->rproc = rproc; - q6->dev = &pdev->dev; -@@ -582,7 +574,7 @@ static int init_irq(struct qcom_q6v5 *q6 - return ret; - - /* Get pd_asid to prepare interrupt names */ -- wcss->pd_asid = qcom_get_pd_asid(rproc); -+ upd->pd_asid = qcom_get_pd_asid(rproc); - - ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq, - q6v5_fatal_interrupt); -@@ -620,7 +612,7 @@ static void q6_release_resources(void) - static int q6_register_userpd(struct platform_device *pdev, - struct device_node *userpd_np) - { -- struct q6_wcss *wcss; -+ struct userpd *upd; - struct rproc *rproc = NULL; - int ret; - struct platform_device *userpd_pdev; -@@ -653,21 +645,16 @@ static int q6_register_userpd(struct pla - - userpd_pdev->dev.driver = pdev->dev.driver; - rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops, -- firmware_name, sizeof(*wcss)); -+ firmware_name, sizeof(*upd)); - if (!rproc) { - ret = -ENOMEM; - goto free_rproc; - } - -- wcss = rproc->priv; -- wcss->dev = &userpd_pdev->dev; -- wcss->version = WCSS_IPQ; -- -- ret = q6_alloc_memory_region(wcss); -- if (ret) -- goto free_rproc; -+ upd = rproc->priv; -+ upd->dev = &userpd_pdev->dev; - -- ret = init_irq(&wcss->q6, userpd_pdev, rproc, -+ ret = init_irq(&upd->q6, userpd_pdev, rproc, - WCSS_CRASH_REASON, NULL, NULL); - if (ret) - goto free_rproc; -@@ -679,7 +666,7 @@ static int q6_register_userpd(struct pla - - list_add(&rproc->node, &upd_rproc_list); - platform_set_drvdata(userpd_pdev, rproc); -- qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name); -+ qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name); - return 0; - - free_rproc: -@@ -715,7 +702,6 @@ static int q6_wcss_probe(struct platform - wcss->dev = &pdev->dev; - wcss->desc = of_device_get_match_data(&pdev->dev); - wcss->firmware = firmware; -- wcss->version = Q6_IPQ; - - ret = q6_alloc_memory_region(wcss); - if (ret) diff --git a/target/linux/qualcommax/patches-6.12/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch b/target/linux/qualcommax/patches-6.12/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch deleted file mode 100644 index cd73dd0f1cc..00000000000 --- a/target/linux/qualcommax/patches-6.12/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch +++ /dev/null @@ -1,206 +0,0 @@ -From 0fa7bdb855247b738d1d227d6f4b3417ebdf21a8 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH 2/2] remoteproc: qcom_q6v5_mpd: fix incorrent use of - rproc->node - - 1.817524] list_add corruption. next->prev should be prev (ffffffc0814bbfc8), but was ffffffc0814bc358. (next=ffffff8003b56800). -[ 1.822435] WARNING: CPU: 1 PID: 24 at lib/list_debug.c:29 __list_add_valid_or_report+0x8c/0xdc -[ 1.833923] Modules linked in: -[ 1.842425] CPU: 1 PID: 24 Comm: kworker/u4:1 Not tainted 6.6.47 #0 -[ 1.845552] Hardware name: Qualcomm MP03 (DT) -[ 1.851716] Workqueue: events_unbound deferred_probe_work_func -[ 1.856229] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) -[ 1.861959] pc : __list_add_valid_or_report+0x8c/0xdc -[ 1.868816] lr : __list_add_valid_or_report+0x8c/0xdc -[ 1.874022] sp : ffffffc081603a50 -[ 1.879055] x29: ffffffc081603a50 x28: ffffff8000fa2810 x27: ffffff8003cba800 -[ 1.882358] x26: ffffff8000fa2800 x25: ffffff8003cbac80 x24: 0000000000000000 -[ 1.889476] x23: ffffffc08088b968 x22: ffffffc0814bb000 x21: ffffffc0814bbfc8 -[ 1.896593] x20: ffffffc08088b8e8 x19: ffffff8003cba800 x18: 00000000000000b1 -[ 1.903713] x17: 3863666262343138 x16: 3063666666666666 x15: ffffffc081416e20 -[ 1.910830] x14: 0000000000000213 x13: 00000000000000b1 x12: 00000000ffffffea -[ 1.917948] x11: 00000000ffffefff x10: ffffffc08146ee20 x9 : ffffffc081416dc8 -[ 1.925066] x8 : 0000000000017fe8 x7 : c0000000ffffefff x6 : 0000000000057fa8 -[ 1.932184] x5 : 0000000000000fff x4 : 0000000000000000 x3 : ffffffc081603850 -[ 1.939302] x2 : ffffffc081416d60 x1 : ffffffc081416d60 x0 : 0000000000000075 -[ 1.946422] Call trace: -[ 1.953535] __list_add_valid_or_report+0x8c/0xdc -[ 1.955793] rproc_add+0x1f4/0x25c -[ 1.960653] q6_wcss_probe+0x510/0x634 -[ 1.963950] platform_probe+0x68/0xc4 -[ 1.967684] really_probe+0x148/0x2b0 -[ 1.971417] __driver_probe_device+0x78/0x128 -[ 1.975063] driver_probe_device+0x40/0xdc -[ 1.979402] __device_attach_driver+0xb8/0xf8 -[ 1.983397] bus_for_each_drv+0x70/0xb8 -[ 1.987823] __device_attach+0xa0/0x184 -[ 1.991468] device_initial_probe+0x14/0x20 -[ 1.995289] bus_probe_device+0xac/0xb0 -[ 1.999455] deferred_probe_work_func+0xa4/0xec -[ 2.003275] process_one_work+0x178/0x2d4 -[ 2.007788] worker_thread+0x2ec/0x4d8 -[ 2.011954] kthread+0xdc/0xe0 -[ 2.015600] ret_from_fork+0x10/0x20 - -Signed-off-by: Ziyang Huang ---- - drivers/remoteproc/qcom_q6v5_mpd.c | 53 +++++++++++++++++------------- - 1 file changed, 30 insertions(+), 23 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_mpd.c -+++ b/drivers/remoteproc/qcom_q6v5_mpd.c -@@ -34,6 +34,7 @@ - - #define BUF_SIZE 35 - -+#define MAX_UPD 3 - #define MAX_FIRMWARE 3 - - #define RPD_SWID MPD_WCNSS_PAS_ID -@@ -44,8 +45,6 @@ - #define UPD_BOOT_INFO_SMEM_ID 507 - #define VERSION2 2 - --static LIST_HEAD(upd_rproc_list); -- - /** - * struct userpd_boot_info_header - header of user pd bootinfo - * @type: type of bootinfo passing over smem -@@ -81,6 +80,7 @@ struct q6_wcss { - size_t mem_size; - const struct wcss_data *desc; - const char **firmware; -+ struct userpd *upd[MAX_UPD]; - }; - - struct userpd { -@@ -271,13 +271,12 @@ static void *q6_wcss_da_to_va(struct rpr - */ - static int share_upd_bootinfo_to_q6(struct rproc *rproc) - { -- int ret; -+ int i, ret; - size_t size; - u16 cnt = 0, version; - void *ptr; - struct q6_wcss *wcss = rproc->priv; - struct userpd *upd; -- struct rproc *upd_rproc; - struct userpd_boot_info upd_bootinfo = {0}; - const struct firmware *fw; - -@@ -302,16 +301,19 @@ static int share_upd_bootinfo_to_q6(stru - memcpy_toio(ptr, &version, sizeof(version)); - ptr += sizeof(version); - -- list_for_each_entry(upd_rproc, &upd_rproc_list, node) -- cnt++; -+ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) -+ if (wcss->upd[i]) -+ cnt++; - - /* No of elements */ - cnt = (sizeof(upd_bootinfo) * cnt); - memcpy_toio(ptr, &cnt, sizeof(u16)); - ptr += sizeof(u16); - -- list_for_each_entry(upd_rproc, &upd_rproc_list, node) { -- upd = upd_rproc->priv; -+ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) { -+ upd = wcss->upd[i]; -+ if (!upd) -+ continue; - - /* TYPE */ - upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE; -@@ -323,14 +325,14 @@ static int share_upd_bootinfo_to_q6(stru - /* Process ID */ - upd_bootinfo.pid = upd->pd_asid + 1; - -- ret = request_firmware(&fw, upd_rproc->firmware, upd->dev); -+ ret = request_firmware(&fw, upd->q6.rproc->firmware, upd->dev); - if (ret < 0) { - dev_err(upd->dev, "request_firmware failed: %d\n", ret); - return ret; - } - - /* Load address */ -- upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw); -+ upd_bootinfo.bootaddr = rproc_get_boot_addr(upd->q6.rproc, fw); - - /* Firmware mem size */ - upd_bootinfo.data_size = qcom_mdt_get_size(fw); -@@ -598,18 +600,23 @@ static int init_irq(struct qcom_q6v5 *q6 - return 0; - } - --static void q6_release_resources(void) -+static void q6_release_resources(struct q6_wcss *wcss) - { -- struct rproc *upd_rproc; -+ struct userpd *upd; -+ int i; - - /* Release userpd resources */ -- list_for_each_entry(upd_rproc, &upd_rproc_list, node) { -- rproc_del(upd_rproc); -- rproc_free(upd_rproc); -+ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) { -+ upd = wcss->upd[i]; -+ if (!upd) -+ continue; -+ -+ rproc_del(upd->q6.rproc); -+ rproc_free(upd->q6.rproc); - } - } - --static int q6_register_userpd(struct platform_device *pdev, -+static int q6_register_userpd(struct q6_wcss *wcss, - struct device_node *userpd_np) - { - struct userpd *upd; -@@ -634,16 +641,16 @@ static int q6_register_userpd(struct pla - return ret; - } - -- dev_info(&pdev->dev, "%s node found\n", userpd_np->name); -+ dev_info(wcss->dev, "%s node found\n", userpd_np->name); - - userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name, -- &pdev->dev); -+ wcss->dev); - if (!userpd_pdev) -- return dev_err_probe(&pdev->dev, -ENODEV, -+ return dev_err_probe(wcss->dev, -ENODEV, - "failed to create %s platform device\n", - userpd_np->name); - -- userpd_pdev->dev.driver = pdev->dev.driver; -+ userpd_pdev->dev.driver = wcss->dev->driver; - rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops, - firmware_name, sizeof(*upd)); - if (!rproc) { -@@ -664,7 +671,7 @@ static int q6_register_userpd(struct pla - if (ret) - goto free_rproc; - -- list_add(&rproc->node, &upd_rproc_list); -+ wcss->upd[upd->pd_asid] = upd; - platform_set_drvdata(userpd_pdev, rproc); - qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name); - return 0; -@@ -724,10 +731,10 @@ static int q6_wcss_probe(struct platform - - /* Iterate over userpd child's and register with rproc */ - for_each_available_child_of_node(pdev->dev.of_node, userpd_np) { -- ret = q6_register_userpd(pdev, userpd_np); -+ ret = q6_register_userpd(wcss, userpd_np); - if (ret) { - /* release resources of successfully allocated userpd rproc's */ -- q6_release_resources(); -+ q6_release_resources(wcss); - return dev_err_probe(&pdev->dev, ret, - "Failed to register userpd(%s)\n", - userpd_np->name); diff --git a/target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch b/target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch deleted file mode 100644 index 29644e3fdb3..00000000000 --- a/target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch +++ /dev/null @@ -1,102 +0,0 @@ -From: George Moussalem -Date: Tue, 21 Oct 2025 15:26:31 +0400 -Subject: [PATCH] remoteproc: qcom: wcss-sec: add split firmware support - -The driver currently expects only one firmware file to be loaded as part -of bringing up WCSS. For IPQ5018 and QCN6122, multiple firmware files are -required, both the q6 and m3 firmware files. As such, add support for -loading up to 3 firmware files. - -Signed-off-by: George Moussalem ---- ---- a/drivers/remoteproc/qcom_q6v5_wcss_sec.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss_sec.c -@@ -25,6 +25,8 @@ - - #define Q6_WAIT_TIMEOUT (5 * HZ) - -+#define MAX_FIRMWARE 3 -+ - struct wcss_sec { - struct device *dev; - struct qcom_rproc_glink glink_subdev; -@@ -35,6 +37,7 @@ struct wcss_sec { - void *mem_region; - size_t mem_size; - const struct wcss_data *desc; -+ const char **firmware; - - struct mbox_client mbox_client; - struct mbox_chan *mbox_chan; -@@ -137,7 +140,8 @@ static int wcss_sec_load(struct rproc *r - { - struct wcss_sec *wcss = rproc->priv; - struct device *dev = wcss->dev; -- int ret; -+ const struct firmware *fw_hdl; -+ int i, ret; - - if (wcss->desc->use_tmelcom) { - wcss->metadata = qcom_mdt_read_metadata(fw, &wcss->metadata_len, -@@ -161,6 +165,28 @@ static int wcss_sec_load(struct rproc *r - wcss->mem_phys, wcss->mem_size, &wcss->mem_reloc); - if (ret) - return ret; -+ -+ for (i = 1; i < MAX_FIRMWARE; i++) { -+ if (!wcss->firmware[i]) -+ continue; -+ -+ ret = request_firmware(&fw_hdl, wcss->firmware[i], dev); -+ -+ if (ret) -+ continue; -+ -+ ret = qcom_mdt_load_no_init(dev, fw_hdl, wcss->firmware[i], 0, -+ wcss->mem_region, wcss->mem_phys, -+ wcss->mem_size, &wcss->mem_reloc); -+ -+ release_firmware(fw_hdl); -+ -+ if (ret) { -+ dev_err(dev, "error %d loading firmware %s\n", -+ ret, wcss->firmware[i]); -+ return ret; -+ } -+ } - } - - qcom_pil_info_store("wcss", wcss->mem_phys, wcss->mem_size); -@@ -291,17 +317,20 @@ static int wcss_sec_probe(struct platfor - struct wcss_sec *wcss; - struct clk *sleep_clk; - struct clk *int_clk; -- const char *fw_name = NULL; -+ const char **firmware = NULL; - const struct wcss_data *desc = of_device_get_match_data(&pdev->dev); - int ret; - -- ret = of_property_read_string(pdev->dev.of_node, "firmware-name", -- &fw_name); -+ firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE, -+ sizeof(*firmware), GFP_KERNEL); -+ -+ ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name", -+ firmware, MAX_FIRMWARE); - if (ret < 0) - return ret; - - rproc = devm_rproc_alloc(&pdev->dev, desc->ss_name, &wcss_sec_ops, -- fw_name, sizeof(*wcss)); -+ firmware[0], sizeof(*wcss)); - if (!rproc) { - dev_err(&pdev->dev, "failed to allocate rproc\n"); - return -ENOMEM; -@@ -310,6 +339,7 @@ static int wcss_sec_probe(struct platfor - wcss = rproc->priv; - wcss->dev = &pdev->dev; - wcss->desc = desc; -+ wcss->firmware = firmware; - - ret = wcss_sec_alloc_memory_region(wcss); - if (ret) diff --git a/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch b/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch deleted file mode 100644 index 760f76cd712..00000000000 --- a/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch +++ /dev/null @@ -1,128 +0,0 @@ -From 6553d598cdb507f7ede020f25da646ba084a23c6 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH] firmware: qcom_scm: support MPD - -Add SCM calls to power up / down the SoC's internal WiFi radio and to -load PIL segments to support the MPD architecture. - -Signed-off-by: Ziyang Huang -Signed-off-by: George Moussalem ---- - drivers/firmware/qcom//qcom_scm.c | 79 ++++++++++++++++++++++++++ - drivers/firmware/qcom//qcom_scm.h | 3 + - include/linux/firmware/qcom/qcom_scm.h | 3 + - 3 files changed, 85 insertions(+) - ---- a/include/linux/firmware/qcom/qcom_scm.h -+++ b/include/linux/firmware/qcom/qcom_scm.h -@@ -79,6 +79,9 @@ int qcom_scm_pas_mem_setup(u32 periphera - int qcom_scm_pas_auth_and_reset(u32 peripheral); - int qcom_scm_pas_shutdown(u32 peripheral); - bool qcom_scm_pas_supported(u32 peripheral); -+int qcom_scm_internal_wifi_powerup(u32 peripheral); -+int qcom_scm_internal_wifi_shutdown(u32 peripheral); -+int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt); - int qcom_scm_msa_lock(u32 peripheral); - int qcom_scm_msa_unlock(u32 peripheral); - ---- a/drivers/firmware/qcom/qcom_scm.c -+++ b/drivers/firmware/qcom/qcom_scm.c -@@ -808,6 +808,85 @@ bool qcom_scm_pas_supported(u32 peripher - EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); - - /** -+ * qcom_scm_internal_wifi_powerup() - Bring up internal wifi -+ * @peripheral: peripheral id -+ * -+ * Return 0 on success. -+ */ -+int qcom_scm_internal_wifi_powerup(u32 peripheral) -+{ -+ struct qcom_scm_desc desc = { -+ .svc = QCOM_SCM_SVC_PIL, -+ .cmd = QCOM_SCM_INTERNAL_WIFI_POWERUP, -+ .arginfo = QCOM_SCM_ARGS(1), -+ .args[0] = peripheral, -+ .owner = ARM_SMCCC_OWNER_SIP, -+ }; -+ struct qcom_scm_res res; -+ int ret; -+ -+ ret = qcom_scm_call(__scm->dev, &desc, &res); -+ -+ return ret ? : res.result[0]; -+} -+EXPORT_SYMBOL(qcom_scm_internal_wifi_powerup); -+ -+/** -+ * qcom_scm_internal_wifi_shutdown() - Shut down internal wifi -+ * @peripheral: peripheral id -+ * -+ * Returns 0 on success. -+ */ -+int qcom_scm_internal_wifi_shutdown(u32 peripheral) -+{ -+ struct qcom_scm_desc desc = { -+ .svc = QCOM_SCM_SVC_PIL, -+ .cmd = QCOM_SCM_INTERNAL_WIFI_SHUTDOWN, -+ .arginfo = QCOM_SCM_ARGS(1), -+ .args[0] = peripheral, -+ .owner = ARM_SMCCC_OWNER_SIP, -+ }; -+ struct qcom_scm_res res; -+ int ret; -+ -+ ret = qcom_scm_call(__scm->dev, &desc, &res); -+ -+ return ret ? : res.result[0]; -+} -+EXPORT_SYMBOL(qcom_scm_internal_wifi_shutdown); -+ -+/** -+ * qcom_scm_pas_load_segment() - copy userpd PIL segments data to dma blocks -+ * @peripheral: peripheral id -+ * @segment: segment id -+ * @dma: handle of dma region -+ * @seg_cnt: no of dma blocks -+ * -+ * Returns 0 if trustzone successfully loads userpd PIL segments from dma -+ * blocks to DDR -+ */ -+int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt) -+{ -+ struct qcom_scm_desc desc = { -+ .svc = QCOM_SCM_SVC_PIL, -+ .cmd = QCOM_SCM_PIL_PAS_LOAD_SEG, -+ .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL), -+ .args[0] = peripheral, -+ .args[1] = segment, -+ .args[2] = dma, -+ .args[3] = seg_cnt, -+ .owner = ARM_SMCCC_OWNER_SIP, -+ }; -+ struct qcom_scm_res res; -+ int ret; -+ -+ ret = qcom_scm_call(__scm->dev, &desc, &res); -+ -+ return ret ? : res.result[0]; -+} -+EXPORT_SYMBOL(qcom_scm_pas_load_segment); -+ -+/** - * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA - * - * @peripheral: peripheral id ---- a/drivers/firmware/qcom/qcom_scm.h -+++ b/drivers/firmware/qcom/qcom_scm.h -@@ -102,6 +102,9 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm - #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 - #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a - #define QCOM_SCM_PIL_PAS_INIT_IMAGE_V2 0x1a -+#define QCOM_SCM_INTERNAL_WIFI_POWERUP 0x17 -+#define QCOM_SCM_INTERNAL_WIFI_SHUTDOWN 0x18 -+#define QCOM_SCM_PIL_PAS_LOAD_SEG 0x19 - #define QCOM_SCM_MSA_LOCK 0x24 - #define QCOM_SCM_MSA_UNLOCK 0x25 - diff --git a/target/linux/qualcommax/patches-6.12/0812-soc-qcom-mdt_loader-support-MPD.patch b/target/linux/qualcommax/patches-6.12/0812-soc-qcom-mdt_loader-support-MPD.patch deleted file mode 100644 index c8d50199b0b..00000000000 --- a/target/linux/qualcommax/patches-6.12/0812-soc-qcom-mdt_loader-support-MPD.patch +++ /dev/null @@ -1,199 +0,0 @@ -From bf42d84868bc82a9cb334a33930f2d1da24f7070 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH] soc: qcom: mdt_loader: support MPD - -Add support for loading user PD specific PIL segments as required by the -MPD architecture. - -Signed-off-by: Ziyang Huang -Signed-off-by: George Moussalem ---- - drivers/soc/qcom/mdt_loader.c | 110 ++++++++++++++++++++++++++-- - include/linux/soc/qcom/mdt_loader.h | 5 ++ - 2 files changed, 110 insertions(+), 5 deletions(-) - ---- a/drivers/soc/qcom/mdt_loader.c -+++ b/drivers/soc/qcom/mdt_loader.c -@@ -17,6 +17,16 @@ - #include - #include - #include -+#include -+ -+#include "../../remoteproc/qcom_common.h" -+ -+#define QCOM_MDT_PF_ASID_MASK GENMASK(19, 16) -+ -+struct segment_load_args { -+ __le64 addr; -+ __le64 blk_size; -+}; - - static bool mdt_header_valid(const struct firmware *fw) - { -@@ -100,6 +110,56 @@ static ssize_t mdt_load_split_segment(vo - return ret; - } - -+static int mdt_load_split_segment_dma(int pas_id, unsigned int segment, -+ const struct elf32_phdr *phdrs, -+ const char *fw_name, -+ struct device *dev) -+{ -+ const struct elf32_phdr *phdr = &phdrs[segment]; -+ struct segment_load_args *args; -+ dma_addr_t *addrs; -+ void *ptr; -+ dma_addr_t dma_args, dma_addrs, dma_ptr; -+ int ret; -+ -+ args = dma_alloc_coherent(dev, sizeof(*args) + sizeof(*addrs), &dma_args, GFP_DMA); -+ if (!args) { -+ dev_err(dev, "Error in dma alloc regin: %ld\n", sizeof(*args)); -+ return -ENOMEM; -+ } -+ -+ addrs = (void *) args + sizeof(*args); -+ dma_addrs = dma_args + sizeof(*args); -+ -+ ptr = dma_alloc_coherent(dev, phdr->p_filesz, &dma_ptr, GFP_DMA); -+ if (!ptr) { -+ dev_err(dev, "Error in dma alloc ptr: %d\n", phdr->p_filesz); -+ return -ENOMEM; -+ } -+ -+ args->addr = dma_addrs; -+ args->blk_size = phdr->p_filesz; -+ -+ addrs[0] = dma_ptr; -+ -+ ret = mdt_load_split_segment(ptr, phdrs, segment, fw_name, dev); -+ if (ret < 0) { -+ dev_err(dev, "Error in mdt_load_split_segment: %d\n", ret); -+ return ret; -+ } -+ -+ ret = qcom_scm_pas_load_segment(pas_id, segment, dma_args, 1); -+ if (ret < 0) { -+ dev_err(dev, "Error in qcom_scm_pas_load_segment: %d\n", ret); -+ return ret; -+ } -+ -+ dma_free_coherent(dev, phdr->p_filesz, ptr, dma_ptr); -+ dma_free_coherent(dev, sizeof(*args) + sizeof(*addrs), args, dma_args); -+ -+ return 0; -+} -+ - /** - * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt - * @fw: firmware object for the mdt file -@@ -335,7 +395,8 @@ static bool qcom_mdt_bins_are_split(cons - static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, void *mem_region, - phys_addr_t mem_phys, size_t mem_size, -- phys_addr_t *reloc_base, bool pas_init) -+ phys_addr_t *reloc_base, bool pas_init, -+ bool dma_require, int pd_asid) - { - const struct elf32_phdr *phdrs; - const struct elf32_phdr *phdr; -@@ -392,6 +453,14 @@ static int __qcom_mdt_load(struct device - if (!mdt_phdr_valid(phdr)) - continue; - -+ /* -+ * While doing PD specific reloading, load only that PD -+ * specific writeable entries. Skip others -+ */ -+ if (pd_asid && (FIELD_GET(QCOM_MDT_PF_ASID_MASK, phdr->p_flags) != pd_asid || -+ (phdr->p_flags & PF_W) == 0)) -+ continue; -+ - offset = phdr->p_paddr - mem_reloc; - if (offset < 0 || offset + phdr->p_memsz > mem_size) { - dev_err(dev, "segment outside memory range\n"); -@@ -409,7 +478,11 @@ static int __qcom_mdt_load(struct device - - ptr = mem_region + offset; - -- if (phdr->p_filesz && !is_split) { -+ if (dma_require && phdr->p_filesz) { -+ ret = mdt_load_split_segment_dma(pas_id, i, phdrs, fw_name, dev); -+ if (ret) -+ break; -+ } else if (phdr->p_filesz && !is_split) { - /* Firmware is large enough to be non-split */ - if (phdr->p_offset + phdr->p_filesz > fw->size) { - dev_err(dev, "file %s segment %d would be truncated\n", -@@ -426,7 +499,7 @@ static int __qcom_mdt_load(struct device - break; - } - -- if (phdr->p_memsz > phdr->p_filesz) -+ if (!dma_require && phdr->p_memsz > phdr->p_filesz) - memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); - } - -@@ -461,7 +534,7 @@ int qcom_mdt_load(struct device *dev, co - return ret; - - return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, -- mem_size, reloc_base, true); -+ mem_size, reloc_base, true, false, 0); - } - EXPORT_SYMBOL_GPL(qcom_mdt_load); - -@@ -484,9 +557,36 @@ int qcom_mdt_load_no_init(struct device - size_t mem_size, phys_addr_t *reloc_base) - { - return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, -- mem_size, reloc_base, false); -+ mem_size, reloc_base, false, false, 0); - } - EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); - -+/** -+ * qcom_mdt_load_pd_seg() - load userpd specific PIL segements -+ * @dev: device handle to associate resources with -+ * @fw: firmware object for the mdt file -+ * @firmware: name of the firmware, for construction of segment file names -+ * @pas_id: PAS identifier -+ * @mem_region: allocated memory region to load firmware into -+ * @mem_phys: physical address of allocated memory region -+ * @mem_size: size of the allocated memory region -+ * @reloc_base: adjusted physical address after relocation -+ * -+ * Here userpd PIL segements are stitched with rootpd firmware. -+ * This function reloads userpd specific PIL segments during SSR -+ * of userpd. -+ * -+ * Returns 0 on success, negative errno otherwise. -+ */ -+int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw, -+ const char *firmware, int pas_id, int pd_asid, void *mem_region, -+ phys_addr_t mem_phys, size_t mem_size, -+ phys_addr_t *reloc_base) -+{ -+ return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, -+ mem_size, reloc_base, false, true, pd_asid); -+} -+EXPORT_SYMBOL_GPL(qcom_mdt_load_pd_seg); -+ - MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); - MODULE_LICENSE("GPL v2"); ---- a/include/linux/soc/qcom/mdt_loader.h -+++ b/include/linux/soc/qcom/mdt_loader.h -@@ -30,6 +30,11 @@ int qcom_mdt_load_no_init(struct device - void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, - const char *fw_name, struct device *dev); - -+int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw, -+ const char *firmware, int pas_id, int pd_asid, void *mem_region, -+ phys_addr_t mem_phys, size_t mem_size, -+ phys_addr_t *reloc_base); -+ - #else /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */ - - static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) diff --git a/target/linux/qualcommax/patches-6.12/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch b/target/linux/qualcommax/patches-6.12/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch deleted file mode 100644 index efa485570f0..00000000000 --- a/target/linux/qualcommax/patches-6.12/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch +++ /dev/null @@ -1,38 +0,0 @@ -From e83215d5d22946885fa388d375b12f1b991a43c1 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH 3/5] remoteproc: qcom_q6v5_mpd: enable clocks - -Signed-off-by: Ziyang Huang ---- - drivers/remoteproc/qcom_q6v5_mpd.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - ---- a/drivers/remoteproc/qcom_q6v5_mpd.c -+++ b/drivers/remoteproc/qcom_q6v5_mpd.c -@@ -78,6 +78,8 @@ struct q6_wcss { - phys_addr_t mem_reloc; - void *mem_region; - size_t mem_size; -+ struct clk_bulk_data *clks; -+ int num_clks; - const struct wcss_data *desc; - const char **firmware; - struct userpd *upd[MAX_UPD]; -@@ -714,6 +716,16 @@ static int q6_wcss_probe(struct platform - if (ret) - goto free_rproc; - -+ wcss->num_clks = devm_clk_bulk_get_all(wcss->dev, &wcss->clks); -+ if (wcss->num_clks < 0) -+ return dev_err_probe(wcss->dev, wcss->num_clks, -+ "failed to acquire clocks\n"); -+ -+ ret = clk_bulk_prepare_enable(wcss->num_clks, wcss->clks); -+ if (ret) -+ return dev_err_probe(wcss->dev, ret, -+ "failed to enable clocks\n"); -+ - ret = qcom_q6v5_init(&wcss->q6, pdev, rproc, - WCSS_CRASH_REASON, NULL, NULL); - if (ret) diff --git a/target/linux/qualcommax/patches-6.12/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch b/target/linux/qualcommax/patches-6.12/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch deleted file mode 100644 index e50417fda59..00000000000 --- a/target/linux/qualcommax/patches-6.12/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 4ae334127f073aa5f7c9209c9f0a17fd9e331db1 Mon Sep 17 00:00:00 2001 -From: Ziyang Huang -Date: Sun, 8 Sep 2024 16:40:12 +0800 -Subject: [PATCH] remoteproc: qcom_q6v5_mpd: support ipq5018 - -Signed-off-by: Ziyang Huang ---- - drivers/remoteproc/qcom_q6v5_mpd.c | 37 +++++++++++++++++++++++++++--- - 1 file changed, 34 insertions(+), 3 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_mpd.c -+++ b/drivers/remoteproc/qcom_q6v5_mpd.c -@@ -156,6 +156,8 @@ static int q6_wcss_spawn_pd(struct rproc - static int wcss_pd_start(struct rproc *rproc) - { - struct userpd *upd = rproc->priv; -+ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); -+ struct q6_wcss *wcss = rpd_rproc->priv; - u32 pasid = (upd->pd_asid << 8) | UPD_SWID; - int ret; - -@@ -171,6 +173,14 @@ static int wcss_pd_start(struct rproc *r - return ret; - } - -+ if (upd->pd_asid == 1) { -+ ret = qcom_scm_internal_wifi_powerup(wcss->desc->pasid); -+ if (ret) { -+ dev_err(upd->dev, "failed to power up internal radio\n"); -+ return ret; -+ } -+ } -+ - return ret; - } - -@@ -180,6 +190,12 @@ static int q6_wcss_stop(struct rproc *rp - const struct wcss_data *desc = wcss->desc; - int ret; - -+ ret = qcom_q6v5_request_stop(&wcss->q6, NULL); -+ if (ret) { -+ dev_err(wcss->dev, "pd not stopped\n"); -+ return ret; -+ } -+ - ret = qcom_scm_pas_shutdown(desc->pasid); - if (ret) { - dev_err(wcss->dev, "not able to shutdown\n"); -@@ -219,6 +235,7 @@ static int wcss_pd_stop(struct rproc *rp - { - struct userpd *upd = rproc->priv; - struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); -+ struct q6_wcss *wcss = rpd_rproc->priv; - u32 pasid = (upd->pd_asid << 8) | UPD_SWID; - int ret; - -@@ -230,6 +247,14 @@ static int wcss_pd_stop(struct rproc *rp - } - } - -+ if (upd->pd_asid == 1) { -+ ret = qcom_scm_internal_wifi_shutdown(wcss->desc->pasid); -+ if (ret) { -+ dev_err(upd->dev, "failed to power down internal radio\n"); -+ return ret; -+ } -+ } -+ - ret = qcom_scm_msa_unlock(pasid); - if (ret) { - dev_err(upd->dev, "failed to power down pd\n"); -@@ -431,15 +456,14 @@ static int wcss_pd_load(struct rproc *rp - struct userpd *upd = rproc->priv; - struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); - struct q6_wcss *wcss = rpd_rproc->priv; -- u32 pasid = (upd->pd_asid << 8) | UPD_SWID; - int ret; - - ret = rproc_boot(rpd_rproc); - if (ret) - return ret; - -- return qcom_mdt_load(upd->dev, fw, rproc->firmware, -- pasid, wcss->mem_region, -+ return qcom_mdt_load_pd_seg(upd->dev, fw, rproc->firmware, -+ wcss->desc->pasid, upd->pd_asid, wcss->mem_region, - wcss->mem_phys, wcss->mem_size, - NULL); - } -@@ -771,6 +795,12 @@ static void q6_wcss_remove(struct platfo - rproc_free(rproc); - } - -+static const struct wcss_data q6_ipq5018_res_init = { -+ .pasid = MPD_WCNSS_PAS_ID, -+ // .share_upd_info_to_q6 = true, /* Version 1 */ -+ // .mdt_load_sec = qcom_mdt_load_pd_seg, -+}; -+ - static const struct wcss_data q6_ipq5332_res_init = { - .pasid = MPD_WCNSS_PAS_ID, - .share_upd_info_to_q6 = true, -@@ -781,6 +811,7 @@ static const struct wcss_data q6_ipq9574 - }; - - static const struct of_device_id q6_wcss_of_match[] = { -+ { .compatible = "qcom,ipq5018-q6-mpd", .data = &q6_ipq5018_res_init }, - { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init }, - { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init }, - { }, diff --git a/target/linux/qualcommax/patches-6.12/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch b/target/linux/qualcommax/patches-6.12/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch deleted file mode 100644 index 2c4a7b12df8..00000000000 --- a/target/linux/qualcommax/patches-6.12/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: George Moussalem -Date: Mon, 09 Dec 2024 09:59:38 +0400 -Subject: [PATCH] remoteproc: qcom_q6v5_mpd: add support for passing v1 bootargs - -On multi-PD platforms such as IPQ5018, boot args are passed to the root PD -run on the Q6 processor which in turn boots the user PDs for internal -(IPQ5018) and external wifi radios (such as QCN6122). These boot args -let the user PD process know details like what PCIE index, user PD ID, and -reset GPIO is used. These are otherwise hardcoded in the firmware. - -Below is the structure expected of the version 1 boot args including the -default values hardcoded in the firmware for IPQ5018: - -+------------+------+--------------+--------------+ -| Argument | type | def val UPD2 | def val UPD3 | -+------------+------+--------------+--------------+ -| PCIE Index | u32 | 0x02 (PCIE1) | 0x01 (PCIE0) | -| Length | u32 | 0x04 | 0x04 | -| User PD ID | u32 | 0x02 | 0x03 | -| Reset GPIO | u32 | 0x12 | 0x0f | -| Reserved 1 | u32 | 0x00 | 0x00 | -| Reserved 2 | u32 | 0x00 | 0x00 | -+------------+------+--------------+--------------+ - -On IPQ5018/QCN6122 boards, the default mapping is as follows: - - +-> UPD1 ----> IPQ5018 Internal 2.4G Radio - / - / -Root PD +---> UPD2 ----> QCN6122 6G Radio on PCIE1 (if available) - \ - \ - +-> UPD3 ----> QCN6102 5G Radio on PCIE0 - -To support (future) boards with other mappings or control what UPD ID is -used, let's add support for passing boot args for more flexibility. - -Signed-off-by: George Moussalem ---- ---- a/drivers/remoteproc/qcom_q6v5_mpd.c -+++ b/drivers/remoteproc/qcom_q6v5_mpd.c -@@ -43,7 +43,11 @@ - #define UPD_BOOT_INFO_SMEM_SIZE 4096 - #define UPD_BOOT_INFO_HEADER_TYPE 0x2 - #define UPD_BOOT_INFO_SMEM_ID 507 --#define VERSION2 2 -+ -+enum q6_bootargs_version { -+ VERSION1 = 1, -+ VERSION2, -+}; - - /** - * struct userpd_boot_info_header - header of user pd bootinfo -@@ -95,6 +99,7 @@ struct userpd { - struct wcss_data { - u32 pasid; - bool share_upd_info_to_q6; -+ u8 bootargs_version; - }; - - /** -@@ -299,10 +304,13 @@ static void *q6_wcss_da_to_va(struct rpr - static int share_upd_bootinfo_to_q6(struct rproc *rproc) - { - int i, ret; -+ u32 rd_val; - size_t size; - u16 cnt = 0, version; - void *ptr; -+ u8 *bootargs_arr; - struct q6_wcss *wcss = rproc->priv; -+ struct device_node *np = wcss->dev->of_node; - struct userpd *upd; - struct userpd_boot_info upd_bootinfo = {0}; - const struct firmware *fw; -@@ -324,10 +332,47 @@ static int share_upd_bootinfo_to_q6(stru - } - - /*Version*/ -- version = VERSION2; -+ version = (wcss->desc->bootargs_version) ? wcss->desc->bootargs_version : VERSION2; - memcpy_toio(ptr, &version, sizeof(version)); - ptr += sizeof(version); - -+ cnt = ret = of_property_count_u32_elems(np, "boot-args"); -+ if (ret < 0) { -+ if (ret == -ENODATA) { -+ dev_err(wcss->dev, "failed to read boot args ret:%d\n", ret); -+ return ret; -+ } -+ cnt = 0; -+ } -+ -+ /* No of elements */ -+ memcpy_toio(ptr, &cnt, sizeof(u16)); -+ ptr += sizeof(u16); -+ -+ bootargs_arr = kzalloc(cnt, GFP_KERNEL); -+ if (!bootargs_arr) { -+ dev_err(wcss->dev, "failed to allocate memory\n"); -+ return PTR_ERR(bootargs_arr); -+ } -+ -+ for (i = 0; i < cnt; i++) { -+ ret = of_property_read_u32_index(np, "boot-args", i, &rd_val); -+ if (ret) { -+ dev_err(wcss->dev, "failed to read boot args\n"); -+ kfree(bootargs_arr); -+ return ret; -+ } -+ bootargs_arr[i] = (u8)rd_val; -+ } -+ -+ /* Copy bootargs */ -+ memcpy_toio(ptr, bootargs_arr, cnt); -+ ptr += (cnt); -+ -+ of_node_put(np); -+ kfree(bootargs_arr); -+ cnt = 0; -+ - for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) - if (wcss->upd[i]) - cnt++; -@@ -383,12 +428,14 @@ static int q6_wcss_load(struct rproc *rp - - /* Share user pd boot info to Q6 remote processor */ - if (desc->share_upd_info_to_q6) { -- ret = share_upd_bootinfo_to_q6(rproc); -- if (ret) { -- dev_err(wcss->dev, -- "user pd boot info sharing with q6 failed %d\n", -- ret); -- return ret; -+ if (of_property_present(wcss->dev->of_node, "boot-args")) { -+ ret = share_upd_bootinfo_to_q6(rproc); -+ if (ret) { -+ dev_err(wcss->dev, -+ "user pd boot info sharing with q6 failed %d\n", -+ ret); -+ return ret; -+ } - } - } - -@@ -797,13 +844,15 @@ static void q6_wcss_remove(struct platfo - - static const struct wcss_data q6_ipq5018_res_init = { - .pasid = MPD_WCNSS_PAS_ID, -- // .share_upd_info_to_q6 = true, /* Version 1 */ -+ .share_upd_info_to_q6 = true, -+ .bootargs_version = VERSION1, - // .mdt_load_sec = qcom_mdt_load_pd_seg, - }; - - static const struct wcss_data q6_ipq5332_res_init = { - .pasid = MPD_WCNSS_PAS_ID, - .share_upd_info_to_q6 = true, -+ .bootargs_version = VERSION2, - }; - - static const struct wcss_data q6_ipq9574_res_init = { diff --git a/target/linux/qualcommax/patches-6.12/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch b/target/linux/qualcommax/patches-6.12/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch deleted file mode 100644 index 8c6b2f9769e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: George Moussalem -Date: Wed, 27 Oct 2024 16:34:11 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: add wifi support - -The IPQ5018 SoC comes with an internal 2x2 2.4Ghz wifi radio. -QCN6122 is a PCIe based wifi solution specific to the IPQ5018 platform which -comes optinally packed with 1 or 2 QCN6122 chips or with an external -PCIe based wifi solution (such as QCN9074) for 5/6 Ghz support. - -As such, add wifi nodes for both IPQ5018 and QCN6122. - -Signed-off-by: George Moussalem ---- ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -739,6 +739,118 @@ - }; - }; - -+ wifi: wifi@c000000 { -+ compatible = "qcom,ipq5018-wifi"; -+ reg = <0x0c000000 0x1000000>; -+ -+ interrupts = , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ ; -+ interrupt-names = "misc-pulse1", -+ "misc-latch", -+ "sw-exception", -+ "watchdog", -+ "ce0", -+ "ce1", -+ "ce2", -+ "ce3", -+ "ce4", -+ "ce5", -+ "ce6", -+ "ce7", -+ "ce8", -+ "ce9", -+ "ce10", -+ "ce11", -+ "host2wbm-desc-feed", -+ "host2reo-re-injection", -+ "host2reo-command", -+ "host2rxdma-monitor-ring3", -+ "host2rxdma-monitor-ring2", -+ "host2rxdma-monitor-ring1", -+ "reo2ost-exception", -+ "wbm2host-rx-release", -+ "reo2host-status", -+ "reo2host-destination-ring4", -+ "reo2host-destination-ring3", -+ "reo2host-destination-ring2", -+ "reo2host-destination-ring1", -+ "rxdma2host-monitor-destination-mac3", -+ "rxdma2host-monitor-destination-mac2", -+ "rxdma2host-monitor-destination-mac1", -+ "ppdu-end-interrupts-mac3", -+ "ppdu-end-interrupts-mac2", -+ "ppdu-end-interrupts-mac1", -+ "rxdma2host-monitor-status-ring-mac3", -+ "rxdma2host-monitor-status-ring-mac2", -+ "rxdma2host-monitor-status-ring-mac1", -+ "host2rxdma-host-buf-ring-mac3", -+ "host2rxdma-host-buf-ring-mac2", -+ "host2rxdma-host-buf-ring-mac1", -+ "rxdma2host-destination-ring-mac3", -+ "rxdma2host-destination-ring-mac2", -+ "rxdma2host-destination-ring-mac1", -+ "host2tcl-input-ring4", -+ "host2tcl-input-ring3", -+ "host2tcl-input-ring2", -+ "host2tcl-input-ring1", -+ "wbm2host-tx-completions-ring3", -+ "wbm2host-tx-completions-ring2", -+ "wbm2host-tx-completions-ring1", -+ "tcl2host-status-ring"; -+ -+ status = "disabled"; -+ }; -+ - q6v5_wcss: remoteproc@cd00000 { - compatible = "qcom,ipq5018-wcss-sec-pil"; - reg = <0x0cd00000 0x10000>; diff --git a/target/linux/qualcommax/patches-6.12/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch b/target/linux/qualcommax/patches-6.12/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch deleted file mode 100644 index ce44b037550..00000000000 --- a/target/linux/qualcommax/patches-6.12/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: George Moussalem -Date: Wed, 05 Feb 2025 12:12:47 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: add tz_apps reserved memory region - -Add tz_apps memory region needed for wifi to work. - -Signed-off-by: George Moussalem ---- ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -136,6 +136,11 @@ - #size-cells = <2>; - ranges; - -+ tz_apps@4a400000 { -+ reg = <0x0 0x4a400000 0x0 0x400000>; -+ no-map; -+ }; -+ - bootloader@4a800000 { - reg = <0x0 0x4a800000 0x0 0x200000>; - no-map; diff --git a/target/linux/qualcommax/patches-6.12/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch b/target/linux/qualcommax/patches-6.12/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch deleted file mode 100644 index 2980acb7411..00000000000 --- a/target/linux/qualcommax/patches-6.12/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: George Moussalem -Date: Wed, 27 Oct 2024 16:34:11 +0400 -Subject: [PATCH] dt: bindings: net: add bindings for QCN6122 - -QCN6122 is a PCIe based solution that is attached to and enumerated -by the WPSS (Wireless Processor SubSystem) Q6 processor. - -Though it is a PCIe device, since it is not attached to APSS processor -(Application Processor SubSystem), APSS will be unaware of such a decice -and hence it is registered to the APSS processor as a platform device(AHB). -Because of this hybrid nature, it is called as a hybrid bus device. - -As such, QCN6122 is a hybrid bus type device and follows the same codepath -as for WCN6750. - -This is a reversed engineered and heavily simplified version of below -downstream patch: -https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/ \ -blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch - -Signed-off-by: George Moussalem ---- ---- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml -+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml -@@ -22,6 +22,7 @@ properties: - - qcom,ipq6018-wifi - - qcom,wcn6750-wifi - - qcom,ipq5018-wifi -+ - qcom,qcn6122-wifi - - reg: - maxItems: 1 -@@ -262,6 +263,29 @@ allOf: - - description: interrupt event for ring DP20 - - description: interrupt event for ring DP21 - - description: interrupt event for ring DP22 -+ - if: -+ properties: -+ compatible: -+ contains: -+ enum: -+ - qcom,qcn6122-wifi -+ then: -+ properties: -+ interrupts: -+ items: -+ - description: interrupt event for ring CE1 -+ - description: interrupt event for ring CE2 -+ - description: interrupt event for ring CE3 -+ - description: interrupt event for ring CE4 -+ - description: interrupt event for ring CE5 -+ - description: interrupt event for ring DP1 -+ - description: interrupt event for ring DP2 -+ - description: interrupt event for ring DP3 -+ - description: interrupt event for ring DP4 -+ - description: interrupt event for ring DP5 -+ - description: interrupt event for ring DP6 -+ - description: interrupt event for ring DP7 -+ - description: interrupt event for ring DP8 - - examples: - - | diff --git a/target/linux/qualcommax/patches-6.12/0842-arm64-dts-qcom-ipq5018-add-uniphy-node.patch b/target/linux/qualcommax/patches-6.12/0842-arm64-dts-qcom-ipq5018-add-uniphy-node.patch deleted file mode 100644 index a4432179777..00000000000 --- a/target/linux/qualcommax/patches-6.12/0842-arm64-dts-qcom-ipq5018-add-uniphy-node.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -258,6 +259,29 @@ - status = "disabled"; - }; - -+ uniphy0: ethernet-pcs@98000 { -+ compatible = "qualcomm,ipq5018-uniphy"; -+ reg = <0x00098000 0x800>; -+ #clock-cells = <1>; -+ clocks = <&gcc GCC_UNIPHY_AHB_CLK>, -+ <&gcc GCC_UNIPHY_SYS_CLK>, -+ <&gcc GCC_UNIPHY_RX_CLK>, -+ <&gcc GCC_UNIPHY_TX_CLK>, -+ <&cmn_pll IPQ5018_ETH_50MHZ_CLK>; -+ clock-names = "ahb", -+ "sys", -+ "port5_rx", -+ "port5_tx", -+ "ref"; -+ clock-output-names = "uniphy0_gcc_rx_clk", -+ "uniphy0_gcc_tx_clk", -+ "uniphy0_ref_clk"; -+ resets = <&gcc GCC_UNIPHY_SOFT_RESET>; -+ reset-names = "soft"; -+ #pcs-cells = <0>; -+ status = "disabled"; -+ }; -+ - cmn_pll: clock-controller@9b000 { - compatible = "qcom,ipq5018-cmn-pll"; - reg = <0x0009b000 0x800>; -@@ -449,8 +473,8 @@ - <0>, - <&gephy_rx_clk>, - <&gephy_tx_clk>, -- <0>, -- <0>; -+ <&uniphy0 UNIPHY_CLK_RX>, -+ <&uniphy0 UNIPHY_CLK_TX>; - #clock-cells = <1>; - #reset-cells = <1>; - }; diff --git a/target/linux/qualcommax/patches-6.12/0843-net-stmmac-add-get_interfaces-platform-method.patch b/target/linux/qualcommax/patches-6.12/0843-net-stmmac-add-get_interfaces-platform-method.patch deleted file mode 100644 index 3e415d24fbd..00000000000 --- a/target/linux/qualcommax/patches-6.12/0843-net-stmmac-add-get_interfaces-platform-method.patch +++ /dev/null @@ -1,56 +0,0 @@ -From ca732e990fc8222a2d6782ae750304719e212fe8 Mon Sep 17 00:00:00 2001 -From: "Russell King (Oracle)" -Date: Thu, 1 May 2025 12:45:11 +0100 -Subject: [PATCH] net: stmmac: add get_interfaces() platform method - -Add a get_interfaces() platform method to allow platforms to indicate -to phylink which interface modes they support - which then allows -phylink to validate on initialisation that the configured PHY interface -mode is actually supported. - -Signed-off-by: Russell King (Oracle) -Link: https://patch.msgid.link/E1uASLn-0021Qd-Mi@rmk-PC.armlinux.org.uk -Signed-off-by: Jakub Kicinski ---- - .../net/ethernet/stmicro/stmmac/stmmac_main.c | 16 +++++++++++++--- - include/linux/stmmac.h | 2 ++ - 2 files changed, 15 insertions(+), 3 deletions(-) - ---- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -1250,10 +1250,21 @@ static int stmmac_phy_setup(struct stmma - priv->phylink_config.default_an_inband = - mdio_bus_data->default_an_inband; - -- /* Set the platform/firmware specified interface mode. Note, phylink -- * deals with the PHY interface mode, not the MAC interface mode. -+ /* Get the PHY interface modes (at the PHY end of the link) that -+ * are supported by the platform. - */ -- __set_bit(mode, priv->phylink_config.supported_interfaces); -+ if (priv->plat->get_interfaces) -+ priv->plat->get_interfaces(priv, priv->plat->bsp_priv, -+ priv->phylink_config.supported_interfaces); -+ -+ /* Set the platform/firmware specified interface mode if the -+ * supported interfaces have not already been provided using -+ * phy_interface as a last resort. -+ */ -+ if (phy_interface_empty(priv->phylink_config.supported_interfaces)) -+ __set_bit(priv->plat->phy_interface, -+ priv->phylink_config.supported_interfaces); -+ - - /* If we have an xpcs, it defines which PHY interfaces are supported. */ - if (priv->hw->xpcs) ---- a/include/linux/stmmac.h -+++ b/include/linux/stmmac.h -@@ -229,6 +229,8 @@ struct plat_stmmacenet_data { - u8 tx_sched_algorithm; - struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES]; - struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES]; -+ void (*get_interfaces)(struct stmmac_priv *priv, void *bsp_priv, -+ unsigned long *interfaces); - void (*fix_mac_speed)(void *priv, unsigned int speed, unsigned int mode); - int (*fix_soc_reset)(void *priv, void __iomem *ioaddr); - int (*serdes_powerup)(struct net_device *ndev, void *priv); diff --git a/target/linux/qualcommax/patches-6.12/0900-power-Add-Qualcomm-APM.patch b/target/linux/qualcommax/patches-6.12/0900-power-Add-Qualcomm-APM.patch deleted file mode 100644 index c0744d74e25..00000000000 --- a/target/linux/qualcommax/patches-6.12/0900-power-Add-Qualcomm-APM.patch +++ /dev/null @@ -1,1039 +0,0 @@ -From 6c98adf98236b8644b8f5e1aa7af9f1a88ea2766 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 11 Apr 2022 14:38:08 +0200 -Subject: [PATCH] power: Add Qualcomm APM - -Add Qualcomm APM driver, which allows scaling cache and memory fabrics. - -Signed-off-by: Robert Marko ---- - drivers/power/Kconfig | 1 + - drivers/power/Makefile | 1 + - drivers/power/qcom/Kconfig | 7 + - drivers/power/qcom/Makefile | 1 + - drivers/power/qcom/apm.c | 944 +++++++++++++++++++++++++++++++++ - include/linux/power/qcom/apm.h | 48 ++ - 6 files changed, 1002 insertions(+) - create mode 100644 drivers/power/qcom/Kconfig - create mode 100644 drivers/power/qcom/Makefile - create mode 100644 drivers/power/qcom/apm.c - create mode 100644 include/linux/power/qcom/apm.h - ---- a/drivers/power/Kconfig -+++ b/drivers/power/Kconfig -@@ -2,3 +2,4 @@ - source "drivers/power/reset/Kconfig" - source "drivers/power/sequencing/Kconfig" - source "drivers/power/supply/Kconfig" -+source "drivers/power/qcom/Kconfig" ---- a/drivers/power/Makefile -+++ b/drivers/power/Makefile -@@ -2,3 +2,4 @@ - obj-$(CONFIG_POWER_RESET) += reset/ - obj-$(CONFIG_POWER_SEQUENCING) += sequencing/ - obj-$(CONFIG_POWER_SUPPLY) += supply/ -+obj-$(CONFIG_QCOM_APM) += qcom/ ---- /dev/null -+++ b/drivers/power/qcom/Kconfig -@@ -0,0 +1,7 @@ -+config QCOM_APM -+ bool "Qualcomm Technologies Inc platform specific APM driver" -+ help -+ Platform specific driver to manage the power source of -+ memory arrays. Interfaces with regulator drivers to ensure -+ SRAM Vmin requirements are met across different performance -+ levels. ---- /dev/null -+++ b/drivers/power/qcom/Makefile -@@ -0,0 +1 @@ -+obj-$(CONFIG_QCOM_APM) += apm.o ---- /dev/null -+++ b/drivers/power/qcom/apm.c -@@ -0,0 +1,936 @@ -+/* -+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#define pr_fmt(fmt) "%s: " fmt, __func__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * VDD_APCC -+ * ============================================================= -+ * | VDD_MX | | -+ * | ==========================|============= | -+ * ___|___ ___|___ ___|___ ___|___ ___|___ ___|___ -+ * | | | | | | | | | | | | -+ * | APCC | | MX HS | | MX HS | | APCC | | MX HS | | APCC | -+ * | HS | | | | | | HS | | | | HS | -+ * |_______| |_______| |_______| |_______| |_______| |_______| -+ * |_________| |_________| |__________| -+ * | | | -+ * ______|_____ ______|_____ _______|_____ -+ * | | | | | | -+ * | | | | | | -+ * | CPU MEM | | L2 MEM | | L3 MEM | -+ * | Arrays | | Arrays | | Arrays | -+ * | | | | | | -+ * |____________| |____________| |_____________| -+ * -+ */ -+ -+/* Register value definitions */ -+#define APCS_GFMUXA_SEL_VAL 0x13 -+#define APCS_GFMUXA_DESEL_VAL 0x03 -+#define MSM_APM_MX_MODE_VAL 0x00 -+#define MSM_APM_APCC_MODE_VAL 0x10 -+#define MSM_APM_MX_DONE_VAL 0x00 -+#define MSM_APM_APCC_DONE_VAL 0x03 -+#define MSM_APM_OVERRIDE_SEL_VAL 0xb0 -+#define MSM_APM_SEC_CLK_SEL_VAL 0x30 -+#define SPM_EVENT_SET_VAL 0x01 -+#define SPM_EVENT_CLEAR_VAL 0x00 -+ -+/* Register bit mask definitions */ -+#define MSM_APM_CTL_STS_MASK 0x0f -+ -+/* Register offset definitions */ -+#define APCC_APM_MODE 0x00000098 -+#define APCC_APM_CTL_STS 0x000000a8 -+#define APCS_SPARE 0x00000068 -+#define APCS_VERSION 0x00000fd0 -+ -+#define HMSS_VERSION_1P2 0x10020000 -+ -+#define MSM_APM_SWITCH_TIMEOUT_US 10 -+#define SPM_WAKEUP_DELAY_US 2 -+#define SPM_EVENT_NUM 6 -+ -+#define MSM_APM_DRIVER_NAME "qcom,msm-apm" -+ -+enum { -+ MSM8996_ID, -+ MSM8953_ID, -+ IPQ807x_ID, -+}; -+ -+struct msm_apm_ctrl_dev { -+ struct list_head list; -+ struct device *dev; -+ enum msm_apm_supply supply; -+ spinlock_t lock; -+ void __iomem *reg_base; -+ void __iomem *apcs_csr_base; -+ void __iomem **apcs_spm_events_addr; -+ void __iomem *apc0_pll_ctl_addr; -+ void __iomem *apc1_pll_ctl_addr; -+ u32 version; -+ struct dentry *debugfs; -+ u32 msm_id; -+}; -+ -+#if defined(CONFIG_DEBUG_FS) -+static struct dentry *apm_debugfs_base; -+#endif -+ -+static DEFINE_MUTEX(apm_ctrl_list_mutex); -+static LIST_HEAD(apm_ctrl_list); -+ -+/* -+ * Get the resources associated with the APM controller from device tree -+ * and remap all I/O addresses that are relevant to this HW revision. -+ */ -+static int msm_apm_ctrl_devm_ioremap(struct platform_device *pdev, -+ struct msm_apm_ctrl_dev *ctrl) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ static const char *res_name[SPM_EVENT_NUM] = { -+ "apc0-l2-spm", -+ "apc1-l2-spm", -+ "apc0-cpu0-spm", -+ "apc0-cpu1-spm", -+ "apc1-cpu0-spm", -+ "apc1-cpu1-spm" -+ }; -+ int i, ret = 0; -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb"); -+ if (!res) { -+ dev_err(dev, "Missing PM APCC Global register physical address"); -+ return -EINVAL; -+ } -+ ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res)); -+ if (!ctrl->reg_base) { -+ dev_err(dev, "Failed to map PM APCC Global registers\n"); -+ return -ENOMEM; -+ } -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs-csr"); -+ if (!res) { -+ dev_err(dev, "Missing APCS CSR physical base address"); -+ return -EINVAL; -+ } -+ ctrl->apcs_csr_base = devm_ioremap(dev, res->start, resource_size(res)); -+ if (!ctrl->apcs_csr_base) { -+ dev_err(dev, "Failed to map APCS CSR registers\n"); -+ return -ENOMEM; -+ } -+ -+ ctrl->version = readl_relaxed(ctrl->apcs_csr_base + APCS_VERSION); -+ -+ if (ctrl->version >= HMSS_VERSION_1P2) -+ return ret; -+ -+ ctrl->apcs_spm_events_addr = devm_kzalloc(&pdev->dev, -+ SPM_EVENT_NUM -+ * sizeof(void __iomem *), -+ GFP_KERNEL); -+ if (!ctrl->apcs_spm_events_addr) { -+ dev_err(dev, "Failed to allocate memory for APCS SPM event registers\n"); -+ return -ENOMEM; -+ } -+ -+ for (i = 0; i < SPM_EVENT_NUM; i++) { -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ res_name[i]); -+ if (!res) { -+ dev_err(dev, "Missing address for %s\n", res_name[i]); -+ ret = -EINVAL; -+ goto free_events; -+ } -+ -+ ctrl->apcs_spm_events_addr[i] = devm_ioremap(dev, res->start, -+ resource_size(res)); -+ if (!ctrl->apcs_spm_events_addr[i]) { -+ dev_err(dev, "Failed to map %s\n", res_name[i]); -+ ret = -ENOMEM; -+ goto free_events; -+ } -+ -+ dev_dbg(dev, "%s event phys: %pa virt:0x%p\n", res_name[i], -+ &res->start, ctrl->apcs_spm_events_addr[i]); -+ } -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ "apc0-pll-ctl"); -+ if (!res) { -+ dev_err(dev, "Missing APC0 PLL CTL physical address\n"); -+ ret = -EINVAL; -+ goto free_events; -+ } -+ -+ ctrl->apc0_pll_ctl_addr = devm_ioremap(dev, -+ res->start, -+ resource_size(res)); -+ if (!ctrl->apc0_pll_ctl_addr) { -+ dev_err(dev, "Failed to map APC0 PLL CTL register\n"); -+ ret = -ENOMEM; -+ goto free_events; -+ } -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ "apc1-pll-ctl"); -+ if (!res) { -+ dev_err(dev, "Missing APC1 PLL CTL physical address\n"); -+ ret = -EINVAL; -+ goto free_events; -+ } -+ -+ ctrl->apc1_pll_ctl_addr = devm_ioremap(dev, -+ res->start, -+ resource_size(res)); -+ if (!ctrl->apc1_pll_ctl_addr) { -+ dev_err(dev, "Failed to map APC1 PLL CTL register\n"); -+ ret = -ENOMEM; -+ goto free_events; -+ } -+ -+ return ret; -+ -+free_events: -+ devm_kfree(dev, ctrl->apcs_spm_events_addr); -+ return ret; -+} -+ -+/* 8953 register offset definition */ -+#define MSM8953_APM_DLY_CNTR 0x2ac -+ -+/* Register field shift definitions */ -+#define APM_CTL_SEL_SWITCH_DLY_SHIFT 0 -+#define APM_CTL_RESUME_CLK_DLY_SHIFT 8 -+#define APM_CTL_HALT_CLK_DLY_SHIFT 16 -+#define APM_CTL_POST_HALT_DLY_SHIFT 24 -+ -+/* Register field mask definitions */ -+#define APM_CTL_SEL_SWITCH_DLY_MASK GENMASK(7, 0) -+#define APM_CTL_RESUME_CLK_DLY_MASK GENMASK(15, 8) -+#define APM_CTL_HALT_CLK_DLY_MASK GENMASK(23, 16) -+#define APM_CTL_POST_HALT_DLY_MASK GENMASK(31, 24) -+ -+/* -+ * Get the resources associated with the msm8953 APM controller from -+ * device tree, remap all I/O addresses, and program the initial -+ * register configuration required for the 8953 APM controller device. -+ */ -+static int msm8953_apm_ctrl_init(struct platform_device *pdev, -+ struct msm_apm_ctrl_dev *ctrl) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ u32 delay_counter, val = 0, regval = 0; -+ int rc = 0; -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb"); -+ if (!res) { -+ dev_err(dev, "Missing PM APCC Global register physical address\n"); -+ return -ENODEV; -+ } -+ ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res)); -+ if (!ctrl->reg_base) { -+ dev_err(dev, "Failed to map PM APCC Global registers\n"); -+ return -ENOMEM; -+ } -+ -+ /* -+ * Initial APM register configuration required before starting -+ * APM HW controller. -+ */ -+ regval = readl_relaxed(ctrl->reg_base + MSM8953_APM_DLY_CNTR); -+ val = regval; -+ -+ if (of_property_present(dev->of_node, "qcom,apm-post-halt-delay")) { -+ rc = of_property_read_u32(dev->of_node, -+ "qcom,apm-post-halt-delay", &delay_counter); -+ if (rc < 0) { -+ dev_err(dev, "apm-post-halt-delay read failed, rc = %d", -+ rc); -+ return rc; -+ } -+ -+ val &= ~APM_CTL_POST_HALT_DLY_MASK; -+ val |= (delay_counter << APM_CTL_POST_HALT_DLY_SHIFT) -+ & APM_CTL_POST_HALT_DLY_MASK; -+ } -+ -+ if (of_property_present(dev->of_node, "qcom,apm-halt-clk-delay")) { -+ rc = of_property_read_u32(dev->of_node, -+ "qcom,apm-halt-clk-delay", &delay_counter); -+ if (rc < 0) { -+ dev_err(dev, "apm-halt-clk-delay read failed, rc = %d", -+ rc); -+ return rc; -+ } -+ -+ val &= ~APM_CTL_HALT_CLK_DLY_MASK; -+ val |= (delay_counter << APM_CTL_HALT_CLK_DLY_SHIFT) -+ & APM_CTL_HALT_CLK_DLY_MASK; -+ } -+ -+ if (of_property_present(dev->of_node, "qcom,apm-resume-clk-delay")) { -+ rc = of_property_read_u32(dev->of_node, -+ "qcom,apm-resume-clk-delay", &delay_counter); -+ if (rc < 0) { -+ dev_err(dev, "apm-resume-clk-delay read failed, rc = %d", -+ rc); -+ return rc; -+ } -+ -+ val &= ~APM_CTL_RESUME_CLK_DLY_MASK; -+ val |= (delay_counter << APM_CTL_RESUME_CLK_DLY_SHIFT) -+ & APM_CTL_RESUME_CLK_DLY_MASK; -+ } -+ -+ if (of_property_present(dev->of_node, "qcom,apm-sel-switch-delay")) { -+ rc = of_property_read_u32(dev->of_node, -+ "qcom,apm-sel-switch-delay", &delay_counter); -+ if (rc < 0) { -+ dev_err(dev, "apm-sel-switch-delay read failed, rc = %d", -+ rc); -+ return rc; -+ } -+ -+ val &= ~APM_CTL_SEL_SWITCH_DLY_MASK; -+ val |= (delay_counter << APM_CTL_SEL_SWITCH_DLY_SHIFT) -+ & APM_CTL_SEL_SWITCH_DLY_MASK; -+ } -+ -+ if (val != regval) { -+ writel_relaxed(val, ctrl->reg_base + MSM8953_APM_DLY_CNTR); -+ /* make sure write completes before return */ -+ mb(); -+ } -+ -+ return rc; -+} -+ -+static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ int i, timeout = MSM_APM_SWITCH_TIMEOUT_US; -+ u32 regval; -+ int ret = 0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ctrl_dev->lock, flags); -+ -+ /* Perform revision-specific programming steps */ -+ if (ctrl_dev->version < HMSS_VERSION_1P2) { -+ /* Clear SPM events */ -+ for (i = 0; i < SPM_EVENT_NUM; i++) -+ writel_relaxed(SPM_EVENT_CLEAR_VAL, -+ ctrl_dev->apcs_spm_events_addr[i]); -+ -+ udelay(SPM_WAKEUP_DELAY_US); -+ -+ /* Switch APC/CBF to GPLL0 clock */ -+ writel_relaxed(APCS_GFMUXA_SEL_VAL, -+ ctrl_dev->apcs_csr_base + APCS_SPARE); -+ ndelay(200); -+ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, -+ ctrl_dev->apc0_pll_ctl_addr); -+ ndelay(200); -+ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, -+ ctrl_dev->apc1_pll_ctl_addr); -+ -+ /* Ensure writes complete before proceeding */ -+ mb(); -+ } -+ -+ /* Switch arrays to MX supply and wait for its completion */ -+ writel_relaxed(MSM_APM_MX_MODE_VAL, ctrl_dev->reg_base + -+ APCC_APM_MODE); -+ -+ /* Ensure write above completes before delaying */ -+ mb(); -+ -+ while (timeout > 0) { -+ regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS); -+ if ((regval & MSM_APM_CTL_STS_MASK) == -+ MSM_APM_MX_DONE_VAL) -+ break; -+ -+ udelay(1); -+ timeout--; -+ } -+ -+ if (timeout == 0) { -+ ret = -ETIMEDOUT; -+ dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n", -+ regval); -+ } -+ -+ /* Perform revision-specific programming steps */ -+ if (ctrl_dev->version < HMSS_VERSION_1P2) { -+ /* Switch APC/CBF clocks to original source */ -+ writel_relaxed(APCS_GFMUXA_DESEL_VAL, -+ ctrl_dev->apcs_csr_base + APCS_SPARE); -+ ndelay(200); -+ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, -+ ctrl_dev->apc0_pll_ctl_addr); -+ ndelay(200); -+ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, -+ ctrl_dev->apc1_pll_ctl_addr); -+ -+ /* Complete clock source switch before SPM event sequence */ -+ mb(); -+ -+ /* Set SPM events */ -+ for (i = 0; i < SPM_EVENT_NUM; i++) -+ writel_relaxed(SPM_EVENT_SET_VAL, -+ ctrl_dev->apcs_spm_events_addr[i]); -+ } -+ -+ if (!ret) { -+ ctrl_dev->supply = MSM_APM_SUPPLY_MX; -+ dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n"); -+ } -+ -+ spin_unlock_irqrestore(&ctrl_dev->lock, flags); -+ -+ return ret; -+} -+ -+static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ int i, timeout = MSM_APM_SWITCH_TIMEOUT_US; -+ u32 regval; -+ int ret = 0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ctrl_dev->lock, flags); -+ -+ /* Perform revision-specific programming steps */ -+ if (ctrl_dev->version < HMSS_VERSION_1P2) { -+ /* Clear SPM events */ -+ for (i = 0; i < SPM_EVENT_NUM; i++) -+ writel_relaxed(SPM_EVENT_CLEAR_VAL, -+ ctrl_dev->apcs_spm_events_addr[i]); -+ -+ udelay(SPM_WAKEUP_DELAY_US); -+ -+ /* Switch APC/CBF to GPLL0 clock */ -+ writel_relaxed(APCS_GFMUXA_SEL_VAL, -+ ctrl_dev->apcs_csr_base + APCS_SPARE); -+ ndelay(200); -+ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, -+ ctrl_dev->apc0_pll_ctl_addr); -+ ndelay(200); -+ writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL, -+ ctrl_dev->apc1_pll_ctl_addr); -+ -+ /* Ensure previous writes complete before proceeding */ -+ mb(); -+ } -+ -+ /* Switch arrays to APCC supply and wait for its completion */ -+ writel_relaxed(MSM_APM_APCC_MODE_VAL, ctrl_dev->reg_base + -+ APCC_APM_MODE); -+ -+ /* Ensure write above completes before delaying */ -+ mb(); -+ -+ while (timeout > 0) { -+ regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS); -+ if ((regval & MSM_APM_CTL_STS_MASK) == -+ MSM_APM_APCC_DONE_VAL) -+ break; -+ -+ udelay(1); -+ timeout--; -+ } -+ -+ if (timeout == 0) { -+ ret = -ETIMEDOUT; -+ dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n", -+ regval); -+ } -+ -+ /* Perform revision-specific programming steps */ -+ if (ctrl_dev->version < HMSS_VERSION_1P2) { -+ /* Set SPM events */ -+ for (i = 0; i < SPM_EVENT_NUM; i++) -+ writel_relaxed(SPM_EVENT_SET_VAL, -+ ctrl_dev->apcs_spm_events_addr[i]); -+ -+ /* Complete SPM event sequence before clock source switch */ -+ mb(); -+ -+ /* Switch APC/CBF clocks to original source */ -+ writel_relaxed(APCS_GFMUXA_DESEL_VAL, -+ ctrl_dev->apcs_csr_base + APCS_SPARE); -+ ndelay(200); -+ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, -+ ctrl_dev->apc0_pll_ctl_addr); -+ ndelay(200); -+ writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL, -+ ctrl_dev->apc1_pll_ctl_addr); -+ } -+ -+ if (!ret) { -+ ctrl_dev->supply = MSM_APM_SUPPLY_APCC; -+ dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n"); -+ } -+ -+ spin_unlock_irqrestore(&ctrl_dev->lock, flags); -+ -+ return ret; -+} -+ -+/* 8953 register value definitions */ -+#define MSM8953_APM_MX_MODE_VAL 0x00 -+#define MSM8953_APM_APCC_MODE_VAL 0x02 -+#define MSM8953_APM_MX_DONE_VAL 0x00 -+#define MSM8953_APM_APCC_DONE_VAL 0x03 -+ -+/* 8953 register offset definitions */ -+#define MSM8953_APCC_APM_MODE 0x000002a8 -+#define MSM8953_APCC_APM_CTL_STS 0x000002b0 -+ -+/* 8953 constants */ -+#define MSM8953_APM_SWITCH_TIMEOUT_US 500 -+ -+/* Register bit mask definitions */ -+#define MSM8953_APM_CTL_STS_MASK 0x1f -+ -+static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; -+ u32 regval; -+ int ret = 0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ctrl_dev->lock, flags); -+ -+ /* Switch arrays to MX supply and wait for its completion */ -+ writel_relaxed(MSM8953_APM_MX_MODE_VAL, ctrl_dev->reg_base + -+ MSM8953_APCC_APM_MODE); -+ -+ /* Ensure write above completes before delaying */ -+ mb(); -+ -+ while (timeout > 0) { -+ regval = readl_relaxed(ctrl_dev->reg_base + -+ MSM8953_APCC_APM_CTL_STS); -+ if ((regval & MSM8953_APM_CTL_STS_MASK) == -+ MSM8953_APM_MX_DONE_VAL) -+ break; -+ -+ udelay(1); -+ timeout--; -+ } -+ -+ if (timeout == 0) { -+ ret = -ETIMEDOUT; -+ dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n", -+ regval); -+ } else { -+ ctrl_dev->supply = MSM_APM_SUPPLY_MX; -+ dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n"); -+ } -+ -+ spin_unlock_irqrestore(&ctrl_dev->lock, flags); -+ -+ return ret; -+} -+ -+static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US; -+ u32 regval; -+ int ret = 0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ctrl_dev->lock, flags); -+ -+ /* Switch arrays to APCC supply and wait for its completion */ -+ writel_relaxed(MSM8953_APM_APCC_MODE_VAL, ctrl_dev->reg_base + -+ MSM8953_APCC_APM_MODE); -+ -+ /* Ensure write above completes before delaying */ -+ mb(); -+ -+ while (timeout > 0) { -+ regval = readl_relaxed(ctrl_dev->reg_base + -+ MSM8953_APCC_APM_CTL_STS); -+ if ((regval & MSM8953_APM_CTL_STS_MASK) == -+ MSM8953_APM_APCC_DONE_VAL) -+ break; -+ -+ udelay(1); -+ timeout--; -+ } -+ -+ if (timeout == 0) { -+ ret = -ETIMEDOUT; -+ dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n", -+ regval); -+ } else { -+ ctrl_dev->supply = MSM_APM_SUPPLY_APCC; -+ dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n"); -+ } -+ -+ spin_unlock_irqrestore(&ctrl_dev->lock, flags); -+ -+ return ret; -+} -+ -+static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ int ret = 0; -+ -+ switch (ctrl_dev->msm_id) { -+ case MSM8996_ID: -+ ret = msm8996_apm_switch_to_mx(ctrl_dev); -+ break; -+ case MSM8953_ID: -+ case IPQ807x_ID: -+ ret = msm8953_apm_switch_to_mx(ctrl_dev); -+ break; -+ } -+ -+ return ret; -+} -+ -+static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ int ret = 0; -+ -+ switch (ctrl_dev->msm_id) { -+ case MSM8996_ID: -+ ret = msm8996_apm_switch_to_apcc(ctrl_dev); -+ break; -+ case MSM8953_ID: -+ case IPQ807x_ID: -+ ret = msm8953_apm_switch_to_apcc(ctrl_dev); -+ break; -+ } -+ -+ return ret; -+} -+ -+/** -+ * msm_apm_get_supply() - Returns the supply that is currently -+ * powering the memory arrays -+ * @ctrl_dev: Pointer to an MSM APM controller device -+ * -+ * Returns the supply currently selected by the APM. -+ */ -+int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ return ctrl_dev->supply; -+} -+EXPORT_SYMBOL(msm_apm_get_supply); -+ -+/** -+ * msm_apm_set_supply() - Perform the necessary steps to switch the voltage -+ * source of the memory arrays to a given supply -+ * @ctrl_dev: Pointer to an MSM APM controller device -+ * @supply: Power rail to use as supply for the memory -+ * arrays -+ * -+ * Returns 0 on success, -ETIMEDOUT on APM switch timeout, or -EPERM if -+ * the supply is not supported. -+ */ -+int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, -+ enum msm_apm_supply supply) -+{ -+ int ret; -+ -+ switch (supply) { -+ case MSM_APM_SUPPLY_APCC: -+ ret = msm_apm_switch_to_apcc(ctrl_dev); -+ break; -+ case MSM_APM_SUPPLY_MX: -+ ret = msm_apm_switch_to_mx(ctrl_dev); -+ break; -+ default: -+ ret = -EPERM; -+ break; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(msm_apm_set_supply); -+ -+/** -+ * msm_apm_ctrl_dev_get() - get a handle to the MSM APM controller linked to -+ * the device in device tree -+ * @dev: Pointer to the device -+ * -+ * The device must specify "qcom,apm-ctrl" property in its device tree -+ * node which points to an MSM APM controller device node. -+ * -+ * Returns an MSM APM controller handle if successful or ERR_PTR on any error. -+ * If the APM controller device hasn't probed yet, ERR_PTR(-EPROBE_DEFER) is -+ * returned. -+ */ -+struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev) -+{ -+ struct msm_apm_ctrl_dev *ctrl_dev = NULL; -+ struct msm_apm_ctrl_dev *dev_found = ERR_PTR(-EPROBE_DEFER); -+ struct device_node *ctrl_node; -+ -+ if (!dev || !dev->of_node) { -+ pr_err("Invalid device node\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ ctrl_node = of_parse_phandle(dev->of_node, "qcom,apm-ctrl", 0); -+ if (!ctrl_node) { -+ pr_err("Could not find qcom,apm-ctrl property in %s\n", -+ dev->of_node->full_name); -+ return ERR_PTR(-ENXIO); -+ } -+ -+ mutex_lock(&apm_ctrl_list_mutex); -+ list_for_each_entry(ctrl_dev, &apm_ctrl_list, list) { -+ if (ctrl_dev->dev && ctrl_dev->dev->of_node == ctrl_node) { -+ dev_found = ctrl_dev; -+ break; -+ } -+ } -+ mutex_unlock(&apm_ctrl_list_mutex); -+ -+ of_node_put(ctrl_node); -+ return dev_found; -+} -+EXPORT_SYMBOL(msm_apm_ctrl_dev_get); -+ -+#if defined(CONFIG_DEBUG_FS) -+ -+static int apm_supply_dbg_open(struct inode *inode, struct file *filep) -+{ -+ filep->private_data = inode->i_private; -+ -+ return 0; -+} -+ -+static ssize_t apm_supply_dbg_read(struct file *filep, char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ struct msm_apm_ctrl_dev *ctrl_dev = filep->private_data; -+ char buf[10]; -+ int len; -+ -+ if (!ctrl_dev) { -+ pr_err("invalid apm ctrl handle\n"); -+ return -ENODEV; -+ } -+ -+ if (ctrl_dev->supply == MSM_APM_SUPPLY_APCC) -+ len = snprintf(buf, sizeof(buf), "APCC\n"); -+ else if (ctrl_dev->supply == MSM_APM_SUPPLY_MX) -+ len = snprintf(buf, sizeof(buf), "MX\n"); -+ else -+ len = snprintf(buf, sizeof(buf), "ERR\n"); -+ -+ return simple_read_from_buffer(ubuf, count, ppos, buf, len); -+} -+ -+static const struct file_operations apm_supply_fops = { -+ .open = apm_supply_dbg_open, -+ .read = apm_supply_dbg_read, -+}; -+ -+static void apm_debugfs_base_init(void) -+{ -+ apm_debugfs_base = debugfs_create_dir("msm-apm", NULL); -+ -+ if (IS_ERR_OR_NULL(apm_debugfs_base)) -+ pr_err("msm-apm debugfs base directory creation failed\n"); -+} -+ -+static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ struct dentry *temp; -+ -+ if (IS_ERR_OR_NULL(apm_debugfs_base)) { -+ pr_err("Base directory missing, cannot create apm debugfs nodes\n"); -+ return; -+ } -+ -+ ctrl_dev->debugfs = debugfs_create_dir(dev_name(ctrl_dev->dev), -+ apm_debugfs_base); -+ if (IS_ERR_OR_NULL(ctrl_dev->debugfs)) { -+ pr_err("%s debugfs directory creation failed\n", -+ dev_name(ctrl_dev->dev)); -+ return; -+ } -+ -+ temp = debugfs_create_file("supply", S_IRUGO, ctrl_dev->debugfs, -+ ctrl_dev, &apm_supply_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ pr_err("supply mode creation failed\n"); -+ return; -+ } -+} -+ -+static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev) -+{ -+ if (!IS_ERR_OR_NULL(ctrl_dev->debugfs)) -+ debugfs_remove_recursive(ctrl_dev->debugfs); -+} -+ -+static void apm_debugfs_base_remove(void) -+{ -+ debugfs_remove_recursive(apm_debugfs_base); -+} -+#else -+ -+static void apm_debugfs_base_init(void) -+{} -+ -+static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev) -+{} -+ -+static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev) -+{} -+ -+static void apm_debugfs_base_remove(void) -+{} -+ -+#endif -+ -+static const struct of_device_id msm_apm_match_table[] = { -+ { -+ .compatible = "qcom,msm-apm", -+ .data = (void *)(uintptr_t)MSM8996_ID, -+ }, -+ { -+ .compatible = "qcom,msm8953-apm", -+ .data = (void *)(uintptr_t)MSM8953_ID, -+ }, -+ { -+ .compatible = "qcom,ipq807x-apm", -+ .data = (void *)(uintptr_t)IPQ807x_ID, -+ }, -+ {} -+}; -+ -+static int msm_apm_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct msm_apm_ctrl_dev *ctrl; -+ int ret = 0; -+ -+ dev_dbg(dev, "probing MSM Array Power Mux driver\n"); -+ -+ if (!dev->of_node) { -+ dev_err(dev, "Device tree node is missing\n"); -+ return -ENODEV; -+ } -+ -+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); -+ if (!ctrl) { -+ dev_err(dev, "MSM APM controller memory allocation failed\n"); -+ return -ENOMEM; -+ } -+ -+ INIT_LIST_HEAD(&ctrl->list); -+ spin_lock_init(&ctrl->lock); -+ ctrl->dev = dev; -+ ctrl->msm_id = (uintptr_t)of_device_get_match_data(dev); -+ platform_set_drvdata(pdev, ctrl); -+ -+ switch (ctrl->msm_id) { -+ case MSM8996_ID: -+ ret = msm_apm_ctrl_devm_ioremap(pdev, ctrl); -+ if (ret) { -+ dev_err(dev, "Failed to add APM controller device\n"); -+ return ret; -+ } -+ break; -+ case MSM8953_ID: -+ case IPQ807x_ID: -+ ret = msm8953_apm_ctrl_init(pdev, ctrl); -+ if (ret) { -+ dev_err(dev, "Failed to initialize APM controller device: ret=%d\n", -+ ret); -+ return ret; -+ } -+ break; -+ default: -+ dev_err(dev, "unable to add APM controller device for msm_id:%d\n", -+ ctrl->msm_id); -+ return -ENODEV; -+ } -+ -+ apm_debugfs_init(ctrl); -+ mutex_lock(&apm_ctrl_list_mutex); -+ list_add_tail(&ctrl->list, &apm_ctrl_list); -+ mutex_unlock(&apm_ctrl_list_mutex); -+ -+ dev_dbg(dev, "MSM Array Power Mux driver probe successful"); -+ -+ return ret; -+} -+ -+static void msm_apm_remove(struct platform_device *pdev) -+{ -+ struct msm_apm_ctrl_dev *ctrl_dev; -+ -+ ctrl_dev = platform_get_drvdata(pdev); -+ if (ctrl_dev) { -+ mutex_lock(&apm_ctrl_list_mutex); -+ list_del(&ctrl_dev->list); -+ mutex_unlock(&apm_ctrl_list_mutex); -+ apm_debugfs_deinit(ctrl_dev); -+ } -+} -+ -+static struct platform_driver msm_apm_driver = { -+ .driver = { -+ .name = MSM_APM_DRIVER_NAME, -+ .of_match_table = msm_apm_match_table, -+ }, -+ .probe = msm_apm_probe, -+ .remove = msm_apm_remove, -+}; -+ -+static int __init msm_apm_init(void) -+{ -+ apm_debugfs_base_init(); -+ return platform_driver_register(&msm_apm_driver); -+} -+ -+static void __exit msm_apm_exit(void) -+{ -+ platform_driver_unregister(&msm_apm_driver); -+ apm_debugfs_base_remove(); -+} -+ -+arch_initcall(msm_apm_init); -+module_exit(msm_apm_exit); -+ -+MODULE_DESCRIPTION("MSM Array Power Mux driver"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/include/linux/power/qcom/apm.h -@@ -0,0 +1,48 @@ -+/* -+ * Copyright (c) 2015, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __LINUX_POWER_QCOM_APM_H__ -+#define __LINUX_POWER_QCOM_APM_H__ -+ -+#include -+#include -+ -+/** -+ * enum msm_apm_supply - supported power rails to supply memory arrays -+ * %MSM_APM_SUPPLY_APCC: to enable selection of VDD_APCC rail as supply -+ * %MSM_APM_SUPPLY_MX: to enable selection of VDD_MX rail as supply -+ */ -+enum msm_apm_supply { -+ MSM_APM_SUPPLY_APCC, -+ MSM_APM_SUPPLY_MX, -+}; -+ -+/* Handle used to identify an APM controller device */ -+struct msm_apm_ctrl_dev; -+ -+#ifdef CONFIG_QCOM_APM -+struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev); -+int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, -+ enum msm_apm_supply supply); -+int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev); -+ -+#else -+static inline struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev) -+{ return ERR_PTR(-EPERM); } -+static inline int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev, -+ enum msm_apm_supply supply) -+{ return -EPERM; } -+static inline int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev) -+{ return -EPERM; } -+#endif -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0901-regulator-add-Qualcomm-CPR-regulators.patch b/target/linux/qualcommax/patches-6.12/0901-regulator-add-Qualcomm-CPR-regulators.patch deleted file mode 100644 index 267ed2b5eb4..00000000000 --- a/target/linux/qualcommax/patches-6.12/0901-regulator-add-Qualcomm-CPR-regulators.patch +++ /dev/null @@ -1,11674 +0,0 @@ -From c9df32c057e43e38c8113199e64f7a64f8d341df Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 11 Apr 2022 14:35:36 +0200 -Subject: [PATCH] regulator: add Qualcomm CPR regulators - -Allow building Qualcomm CPR regulators. - -Signed-off-by: Robert Marko ---- - drivers/regulator/Kconfig | 33 + - drivers/regulator/Makefile | 3 + - drivers/regulator/cpr3-npu-regulator.c | 695 +++ - drivers/regulator/cpr3-regulator.c | 5111 +++++++++++++++++++++++ - drivers/regulator/cpr3-regulator.h | 1211 ++++++ - drivers/regulator/cpr3-util.c | 2750 ++++++++++++ - drivers/regulator/cpr4-apss-regulator.c | 1819 ++++++++ - include/soc/qcom/socinfo.h | 463 ++ - 8 files changed, 12085 insertions(+) - create mode 100644 drivers/regulator/cpr3-npu-regulator.c - create mode 100644 drivers/regulator/cpr3-regulator.c - create mode 100644 drivers/regulator/cpr3-regulator.h - create mode 100644 drivers/regulator/cpr3-util.c - create mode 100644 drivers/regulator/cpr4-apss-regulator.c - create mode 100644 include/soc/qcom/socinfo.h - ---- a/drivers/regulator/Kconfig -+++ b/drivers/regulator/Kconfig -@@ -1720,4 +1720,37 @@ config REGULATOR_QCOM_LABIBB - boost regulator and IBB can be used as a negative boost regulator - for LCD display panel. - -+config REGULATOR_CPR3 -+ bool "QCOM CPR3 regulator core support" -+ help -+ This driver supports Core Power Reduction (CPR) version 3 controllers -+ which are used by some Qualcomm Technologies, Inc. SoCs to -+ manage important voltage regulators. CPR3 controllers are capable of -+ monitoring several ring oscillator sensing loops simultaneously. The -+ CPR3 controller informs software when the silicon conditions require -+ the supply voltage to be increased or decreased. On certain supply -+ rails, the CPR3 controller is able to propagate the voltage increase -+ or decrease requests all the way to the PMIC without software -+ involvement. -+ -+config REGULATOR_CPR3_NPU -+ bool "QCOM CPR3 regulator for NPU" -+ depends on OF && REGULATOR_CPR3 -+ help -+ This driver supports Qualcomm Technologies, Inc. NPU CPR3 -+ regulator Which will always operate in open loop. -+ -+config REGULATOR_CPR4_APSS -+ bool "QCOM CPR4 regulator for APSS" -+ depends on OF && REGULATOR_CPR3 -+ help -+ This driver supports Qualcomm Technologies, Inc. APSS application -+ processor specific features including memory array power mux (APM) -+ switching, one CPR4 thread which monitor the two APSS clusters that -+ are both powered by a shared supply, hardware closed-loop auto -+ voltage stepping, voltage adjustments based on online core count, -+ voltage adjustments based on temperature readings, and voltage -+ adjustments for performance boost mode. This driver reads both initial -+ voltage and CPR target quotient values out of hardware fuses. -+ - endif ---- a/drivers/regulator/Makefile -+++ b/drivers/regulator/Makefile -@@ -121,6 +121,9 @@ obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qco - obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o - obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o - obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o -+obj-$(CONFIG_REGULATOR_CPR3) += cpr3-regulator.o cpr3-util.o -+obj-$(CONFIG_REGULATOR_CPR3_NPU) += cpr3-npu-regulator.o -+obj-$(CONFIG_REGULATOR_CPR4_APSS) += cpr4-apss-regulator.o - obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o - obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o - obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o ---- /dev/null -+++ b/drivers/regulator/cpr3-npu-regulator.c -@@ -0,0 +1,689 @@ -+/* -+ * Copyright (c) 2017, The Linux Foundation. All rights reserved. -+ * -+ * Permission to use, copy, modify, and/or distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "cpr3-regulator.h" -+ -+#define IPQ807x_NPU_FUSE_CORNERS 2 -+#define IPQ817x_NPU_FUSE_CORNERS 1 -+#define IPQ807x_NPU_FUSE_STEP_VOLT 8000 -+#define IPQ807x_NPU_VOLTAGE_FUSE_SIZE 6 -+#define IPQ807x_NPU_CPR_CLOCK_RATE 19200000 -+ -+#define IPQ807x_NPU_CPR_TCSR_START 6 -+#define IPQ807x_NPU_CPR_TCSR_END 7 -+ -+#define NPU_TSENS 5 -+ -+u32 g_valid_npu_fuse_count = IPQ807x_NPU_FUSE_CORNERS; -+/** -+ * struct cpr3_ipq807x_npu_fuses - NPU specific fuse data for IPQ807x -+ * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value -+ * for each fuse corner (raw, not converted to a voltage) -+ * This struct holds the values for all of the fuses read from memory. -+ */ -+struct cpr3_ipq807x_npu_fuses { -+ u64 init_voltage[IPQ807x_NPU_FUSE_CORNERS]; -+}; -+ -+/* -+ * Constants which define the name of each fuse corner. -+ */ -+enum cpr3_ipq807x_npu_fuse_corner { -+ CPR3_IPQ807x_NPU_FUSE_CORNER_NOM = 0, -+ CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO = 1, -+}; -+ -+static const char * const cpr3_ipq807x_npu_fuse_corner_name[] = { -+ [CPR3_IPQ807x_NPU_FUSE_CORNER_NOM] = "NOM", -+ [CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO] = "TURBO", -+}; -+ -+/* -+ * IPQ807x NPU fuse parameter locations: -+ * -+ * Structs are organized with the following dimensions: -+ * Outer: 0 to 1 for fuse corners from lowest to highest corner -+ * Inner: large enough to hold the longest set of parameter segments which -+ * fully defines a fuse parameter, +1 (for NULL termination). -+ * Each segment corresponds to a contiguous group of bits from a -+ * single fuse row. These segments are concatentated together in -+ * order to form the full fuse parameter value. The segments for -+ * a given parameter may correspond to different fuse rows. -+ */ -+static struct cpr3_fuse_param -+ipq807x_npu_init_voltage_param[IPQ807x_NPU_FUSE_CORNERS][2] = { -+ {{73, 22, 27}, {} }, -+ {{73, 16, 21}, {} }, -+}; -+ -+/* -+ * Open loop voltage fuse reference voltages in microvolts for IPQ807x -+ */ -+static int -+ipq807x_npu_fuse_ref_volt [IPQ807x_NPU_FUSE_CORNERS] = { -+ 912000, -+ 992000, -+}; -+ -+/* -+ * IPQ9574 (Few parameters are changed, remaining are same as IPQ807x) -+ */ -+#define IPQ9574_NPU_FUSE_CORNERS 2 -+#define IPQ9574_NPU_FUSE_STEP_VOLT 10000 -+#define IPQ9574_NPU_CPR_CLOCK_RATE 24000000 -+ -+/* -+ * fues parameters for IPQ9574 -+ */ -+static struct cpr3_fuse_param -+ipq9574_npu_init_voltage_param[IPQ9574_NPU_FUSE_CORNERS][2] = { -+ {{105, 12, 17}, {} }, -+ {{105, 6, 11}, {} }, -+}; -+ -+/* -+ * Open loop voltage fuse reference voltages in microvolts for IPQ9574 -+ */ -+static int -+ipq9574_npu_fuse_ref_volt [IPQ9574_NPU_FUSE_CORNERS] = { -+ 862500, -+ 987500, -+}; -+ -+struct cpr3_controller *g_ctrl; -+ -+void cpr3_npu_temp_notify(int sensor, int temp, int low_notif) -+{ -+ u32 prev_sensor_state; -+ -+ if (sensor != NPU_TSENS) -+ return; -+ -+ prev_sensor_state = g_ctrl->cur_sensor_state; -+ if (low_notif) -+ g_ctrl->cur_sensor_state |= BIT(sensor); -+ else -+ g_ctrl->cur_sensor_state &= ~BIT(sensor); -+ -+ if (!prev_sensor_state && g_ctrl->cur_sensor_state) -+ cpr3_handle_temp_open_loop_adjustment(g_ctrl, true); -+ else if (prev_sensor_state && !g_ctrl->cur_sensor_state) -+ cpr3_handle_temp_open_loop_adjustment(g_ctrl, false); -+} -+ -+/** -+ * cpr3_ipq807x_npu_read_fuse_data() - load NPU specific fuse parameter values -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function allocates a cpr3_ipq807x_npu_fuses struct, fills it with -+ * values read out of hardware fuses, and finally copies common fuse values -+ * into the CPR3 regulator struct. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_ipq807x_npu_read_fuse_data(struct cpr3_regulator *vreg) -+{ -+ void __iomem *base = vreg->thread->ctrl->fuse_base; -+ struct cpr3_ipq807x_npu_fuses *fuse; -+ int i, rc; -+ -+ fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL); -+ if (!fuse) -+ return -ENOMEM; -+ -+ for (i = 0; i < g_valid_npu_fuse_count; i++) { -+ rc = cpr3_read_fuse_param(base, -+ vreg->cpr3_regulator_data->init_voltage_param[i], -+ &fuse->init_voltage[i]); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", -+ i, rc); -+ return rc; -+ } -+ } -+ -+ vreg->fuse_corner_count = g_valid_npu_fuse_count; -+ vreg->platform_fuses = fuse; -+ -+ return 0; -+} -+ -+/** -+ * cpr3_npu_parse_corner_data() - parse NPU corner data from device tree -+ * properties of the CPR3 regulator's device node -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_npu_parse_corner_data(struct cpr3_regulator *vreg) -+{ -+ int rc; -+ -+ rc = cpr3_parse_common_corner_data(vreg); -+ if (rc) { -+ cpr3_err(vreg, "error reading corner data, rc=%d\n", rc); -+ return rc; -+ } -+ -+ return rc; -+} -+ -+/** -+ * cpr3_ipq807x_npu_calculate_open_loop_voltages() - calculate the open-loop -+ * voltage for each corner of a CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * @temp_correction: Temperature based correction -+ * -+ * If open-loop voltage interpolation is allowed in device tree, then -+ * this function calculates the open-loop voltage for a given corner using -+ * linear interpolation. This interpolation is performed using the processor -+ * frequencies of the lower and higher Fmax corners along with their fused -+ * open-loop voltages. -+ * -+ * If open-loop voltage interpolation is not allowed, then this function uses -+ * the Fmax fused open-loop voltage for all of the corners associated with a -+ * given fuse corner. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_ipq807x_npu_calculate_open_loop_voltages( -+ struct cpr3_regulator *vreg, bool temp_correction) -+{ -+ struct cpr3_ipq807x_npu_fuses *fuse = vreg->platform_fuses; -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ int i, j, rc = 0; -+ u64 freq_low, volt_low, freq_high, volt_high; -+ int *fuse_volt; -+ int *fmax_corner; -+ -+ fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt), -+ GFP_KERNEL); -+ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), -+ GFP_KERNEL); -+ if (!fuse_volt || !fmax_corner) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->fuse_corner_count; i++) { -+ if (ctrl->cpr_global_setting == CPR_DISABLED) -+ fuse_volt[i] = vreg->cpr3_regulator_data->fuse_ref_volt[i]; -+ else -+ fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse( -+ vreg->cpr3_regulator_data->fuse_ref_volt[i], -+ vreg->cpr3_regulator_data->fuse_step_volt, -+ fuse->init_voltage[i], -+ IPQ807x_NPU_VOLTAGE_FUSE_SIZE); -+ -+ /* Log fused open-loop voltage values for debugging purposes. */ -+ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", -+ cpr3_ipq807x_npu_fuse_corner_name[i], -+ fuse_volt[i]); -+ } -+ -+ rc = cpr3_determine_part_type(vreg, -+ fuse_volt[CPR3_IPQ807x_NPU_FUSE_CORNER_TURBO]); -+ if (rc) { -+ cpr3_err(vreg, -+ "fused part type detection failed failed, rc=%d\n", rc); -+ goto done; -+ } -+ -+ rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt); -+ if (rc) { -+ cpr3_err(vreg, -+ "fused open-loop voltage adjustment failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ if (temp_correction) { -+ rc = cpr3_determine_temp_base_open_loop_correction(vreg, -+ fuse_volt); -+ if (rc) { -+ cpr3_err(vreg, -+ "temp open-loop voltage adj. failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ for (i = 1; i < vreg->fuse_corner_count; i++) { -+ if (fuse_volt[i] < fuse_volt[i - 1]) { -+ cpr3_info(vreg, -+ "fuse corner %d voltage=%d uV < fuse corner %d \ -+ voltage=%d uV; overriding: fuse corner %d \ -+ voltage=%d\n", -+ i, fuse_volt[i], i - 1, fuse_volt[i - 1], -+ i, fuse_volt[i - 1]); -+ fuse_volt[i] = fuse_volt[i - 1]; -+ } -+ } -+ -+ /* Determine highest corner mapped to each fuse corner */ -+ j = vreg->fuse_corner_count - 1; -+ for (i = vreg->corner_count - 1; i >= 0; i--) { -+ if (vreg->corner[i].cpr_fuse_corner == j) { -+ fmax_corner[j] = i; -+ j--; -+ } -+ } -+ -+ if (j >= 0) { -+ cpr3_err(vreg, "invalid fuse corner mapping\n"); -+ rc = -EINVAL; -+ goto done; -+ } -+ -+ /* -+ * Interpolation is not possible for corners mapped to the lowest fuse -+ * corner so use the fuse corner value directly. -+ */ -+ for (i = 0; i <= fmax_corner[0]; i++) -+ vreg->corner[i].open_loop_volt = fuse_volt[0]; -+ -+ /* Interpolate voltages for the higher fuse corners. */ -+ for (i = 1; i < vreg->fuse_corner_count; i++) { -+ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; -+ volt_low = fuse_volt[i - 1]; -+ freq_high = vreg->corner[fmax_corner[i]].proc_freq; -+ volt_high = fuse_volt[i]; -+ -+ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) -+ vreg->corner[j].open_loop_volt = cpr3_interpolate( -+ freq_low, volt_low, freq_high, volt_high, -+ vreg->corner[j].proc_freq); -+ } -+ -+done: -+ if (rc == 0) { -+ cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n"); -+ for (i = 0; i < vreg->corner_count; i++) -+ cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i, -+ vreg->corner[i].open_loop_volt); -+ -+ rc = cpr3_adjust_open_loop_voltages(vreg); -+ if (rc) -+ cpr3_err(vreg, -+ "open-loop voltage adjustment failed, rc=%d\n", -+ rc); -+ } -+ -+ kfree(fuse_volt); -+ kfree(fmax_corner); -+ return rc; -+} -+ -+/** -+ * cpr3_npu_print_settings() - print out NPU CPR configuration settings into -+ * the kernel log for debugging purposes -+ * @vreg: Pointer to the CPR3 regulator -+ */ -+static void cpr3_npu_print_settings(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_corner *corner; -+ int i; -+ -+ cpr3_debug(vreg, -+ "Corner: Frequency (Hz), Fuse Corner, Floor (uV), \ -+ Open-Loop (uV), Ceiling (uV)\n"); -+ for (i = 0; i < vreg->corner_count; i++) { -+ corner = &vreg->corner[i]; -+ cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n", -+ i, corner->proc_freq, corner->cpr_fuse_corner, -+ corner->floor_volt, corner->open_loop_volt, -+ corner->ceiling_volt); -+ } -+ -+ if (vreg->thread->ctrl->apm) -+ cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n", -+ vreg->thread->ctrl->apm_threshold_volt, -+ vreg->thread->ctrl->apm_adj_volt); -+} -+ -+/** -+ * cpr3_ipq807x_npu_calc_temp_based_ol_voltages() - Calculate the open loop -+ * voltages based on temperature based correction margins -+ * @vreg: Pointer to the CPR3 regulator -+ */ -+ -+static int -+cpr3_ipq807x_npu_calc_temp_based_ol_voltages(struct cpr3_regulator *vreg, -+ bool temp_correction) -+{ -+ int rc, i; -+ -+ rc = cpr3_ipq807x_npu_calculate_open_loop_voltages(vreg, -+ temp_correction); -+ if (rc) { -+ cpr3_err(vreg, -+ "unable to calculate open-loop voltages, rc=%d\n", rc); -+ return rc; -+ } -+ -+ rc = cpr3_limit_open_loop_voltages(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ cpr3_open_loop_voltage_as_ceiling(vreg); -+ -+ rc = cpr3_limit_floor_voltages(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc); -+ return rc; -+ } -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ if (temp_correction) -+ vreg->corner[i].cold_temp_open_loop_volt = -+ vreg->corner[i].open_loop_volt; -+ else -+ vreg->corner[i].normal_temp_open_loop_volt = -+ vreg->corner[i].open_loop_volt; -+ } -+ -+ cpr3_npu_print_settings(vreg); -+ -+ return rc; -+} -+ -+/** -+ * cpr3_npu_init_thread() - perform steps necessary to initialize the -+ * configuration data for a CPR3 thread -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_npu_init_thread(struct cpr3_thread *thread) -+{ -+ int rc; -+ -+ rc = cpr3_parse_common_thread_data(thread); -+ if (rc) { -+ cpr3_err(thread->ctrl, -+ "thread %u CPR thread data from DT- failed, rc=%d\n", -+ thread->thread_id, rc); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_npu_init_regulator() - perform all steps necessary to initialize the -+ * configuration data for a CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_npu_init_regulator(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_ipq807x_npu_fuses *fuse; -+ int rc, cold_temp = 0; -+ bool can_adj_cold_temp = cpr3_can_adjust_cold_temp(vreg); -+ -+ rc = cpr3_ipq807x_npu_read_fuse_data(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc); -+ return rc; -+ } -+ -+ fuse = vreg->platform_fuses; -+ -+ rc = cpr3_npu_parse_corner_data(vreg); -+ if (rc) { -+ cpr3_err(vreg, -+ "Cannot read CPR corner data from DT, rc=%d\n", rc); -+ return rc; -+ } -+ -+ rc = cpr3_mem_acc_init(vreg); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(vreg, -+ "Cannot initialize mem-acc regulator settings, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (can_adj_cold_temp) { -+ rc = cpr3_ipq807x_npu_calc_temp_based_ol_voltages(vreg, true); -+ if (rc) { -+ cpr3_err(vreg, -+ "unable to calculate open-loop voltages, rc=%d\n", rc); -+ return rc; -+ } -+ } -+ -+ rc = cpr3_ipq807x_npu_calc_temp_based_ol_voltages(vreg, false); -+ if (rc) { -+ cpr3_err(vreg, -+ "unable to calculate open-loop voltages, rc=%d\n", rc); -+ return rc; -+ } -+ -+ if (can_adj_cold_temp) { -+ cpr3_info(vreg, -+ "Normal and Cold condition init done. Default to normal.\n"); -+ -+ rc = cpr3_get_cold_temp_threshold(vreg, &cold_temp); -+ if (rc) { -+ cpr3_err(vreg, -+ "Get cold temperature threshold failed, rc=%d\n", rc); -+ return rc; -+ } -+ register_low_temp_notif(NPU_TSENS, cold_temp, -+ cpr3_npu_temp_notify); -+ } -+ -+ return rc; -+} -+ -+/** -+ * cpr3_npu_init_controller() - perform NPU CPR3 controller specific -+ * initializations -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_npu_init_controller(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ rc = cpr3_parse_open_loop_common_ctrl_data(ctrl); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ ctrl->ctrl_type = CPR_CTRL_TYPE_CPR3; -+ ctrl->supports_hw_closed_loop = false; -+ -+ return 0; -+} -+ -+static const struct cpr3_reg_data ipq807x_cpr_npu = { -+ .cpr_valid_fuse_count = IPQ807x_NPU_FUSE_CORNERS, -+ .init_voltage_param = ipq807x_npu_init_voltage_param, -+ .fuse_ref_volt = ipq807x_npu_fuse_ref_volt, -+ .fuse_step_volt = IPQ807x_NPU_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ807x_NPU_CPR_CLOCK_RATE, -+}; -+ -+static const struct cpr3_reg_data ipq817x_cpr_npu = { -+ .cpr_valid_fuse_count = IPQ817x_NPU_FUSE_CORNERS, -+ .init_voltage_param = ipq807x_npu_init_voltage_param, -+ .fuse_ref_volt = ipq807x_npu_fuse_ref_volt, -+ .fuse_step_volt = IPQ807x_NPU_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ807x_NPU_CPR_CLOCK_RATE, -+}; -+ -+static const struct cpr3_reg_data ipq9574_cpr_npu = { -+ .cpr_valid_fuse_count = IPQ9574_NPU_FUSE_CORNERS, -+ .init_voltage_param = ipq9574_npu_init_voltage_param, -+ .fuse_ref_volt = ipq9574_npu_fuse_ref_volt, -+ .fuse_step_volt = IPQ9574_NPU_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ9574_NPU_CPR_CLOCK_RATE, -+}; -+ -+static const struct of_device_id cpr3_regulator_match_table[] = { -+ { -+ .compatible = "qcom,cpr3-ipq807x-npu-regulator", -+ .data = &ipq807x_cpr_npu -+ }, -+ { -+ .compatible = "qcom,cpr3-ipq817x-npu-regulator", -+ .data = &ipq817x_cpr_npu -+ }, -+ { -+ .compatible = "qcom,cpr3-ipq9574-npu-regulator", -+ .data = &ipq9574_cpr_npu -+ }, -+ {} -+}; -+ -+static int cpr3_npu_regulator_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct cpr3_controller *ctrl; -+ int i, rc; -+ const struct cpr3_reg_data *cpr_data; -+ -+ if (!dev->of_node) { -+ dev_err(dev, "Device tree node is missing\n"); -+ return -EINVAL; -+ } -+ -+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); -+ if (!ctrl) -+ return -ENOMEM; -+ g_ctrl = ctrl; -+ -+ cpr_data = of_device_get_match_data(&pdev->dev); -+ g_valid_npu_fuse_count = cpr_data->cpr_valid_fuse_count; -+ dev_info(dev, "NPU CPR valid fuse count: %d\n", g_valid_npu_fuse_count); -+ ctrl->cpr_clock_rate = cpr_data->cpr_clk_rate; -+ -+ ctrl->dev = dev; -+ /* Set to false later if anything precludes CPR operation. */ -+ ctrl->cpr_allowed_hw = true; -+ -+ rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name", -+ &ctrl->name); -+ if (rc) { -+ cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr3_map_fuse_base(ctrl, pdev); -+ if (rc) { -+ cpr3_err(ctrl, "could not map fuse base address\n"); -+ return rc; -+ } -+ -+ rc = cpr3_read_tcsr_setting(ctrl, pdev, IPQ807x_NPU_CPR_TCSR_START, -+ IPQ807x_NPU_CPR_TCSR_END); -+ if (rc) { -+ cpr3_err(ctrl, "could not read CPR tcsr rsetting\n"); -+ return rc; -+ } -+ -+ rc = cpr3_allocate_threads(ctrl, 0, 0); -+ if (rc) { -+ cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (ctrl->thread_count != 1) { -+ cpr3_err(ctrl, "expected 1 thread but found %d\n", -+ ctrl->thread_count); -+ return -EINVAL; -+ } -+ -+ rc = cpr3_npu_init_controller(ctrl); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr3_npu_init_thread(&ctrl->thread[0]); -+ if (rc) { -+ cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ for (i = 0; i < ctrl->thread[0].vreg_count; i++) { -+ ctrl->thread[0].vreg[i].cpr3_regulator_data = cpr_data; -+ rc = cpr3_npu_init_regulator(&ctrl->thread[0].vreg[i]); -+ if (rc) { -+ cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ platform_set_drvdata(pdev, ctrl); -+ -+ return cpr3_open_loop_regulator_register(pdev, ctrl); -+} -+ -+static void cpr3_npu_regulator_remove(struct platform_device *pdev) -+{ -+ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); -+ -+ cpr3_open_loop_regulator_unregister(ctrl); -+} -+ -+static struct platform_driver cpr3_npu_regulator_driver = { -+ .driver = { -+ .name = "qcom,cpr3-npu-regulator", -+ .of_match_table = cpr3_regulator_match_table, -+ }, -+ .probe = cpr3_npu_regulator_probe, -+ .remove = cpr3_npu_regulator_remove, -+}; -+ -+static int cpr3_regulator_init(void) -+{ -+ return platform_driver_register(&cpr3_npu_regulator_driver); -+} -+arch_initcall(cpr3_regulator_init); -+ -+static void cpr3_regulator_exit(void) -+{ -+ platform_driver_unregister(&cpr3_npu_regulator_driver); -+} -+module_exit(cpr3_regulator_exit); -+ -+MODULE_DESCRIPTION("QCOM CPR3 NPU regulator driver"); -+MODULE_LICENSE("Dual BSD/GPLv2"); -+MODULE_ALIAS("platform:npu-ipq807x"); ---- /dev/null -+++ b/drivers/regulator/cpr3-regulator.c -@@ -0,0 +1,5111 @@ -+/* -+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#define pr_fmt(fmt) "%s: " fmt, __func__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "cpr3-regulator.h" -+ -+#define CPR3_REGULATOR_CORNER_INVALID (-1) -+#define CPR3_RO_MASK GENMASK(CPR3_RO_COUNT - 1, 0) -+ -+/* CPR3 registers */ -+#define CPR3_REG_CPR_CTL 0x4 -+#define CPR3_CPR_CTL_LOOP_EN_MASK BIT(0) -+#define CPR3_CPR_CTL_LOOP_ENABLE BIT(0) -+#define CPR3_CPR_CTL_LOOP_DISABLE 0 -+#define CPR3_CPR_CTL_IDLE_CLOCKS_MASK GENMASK(5, 1) -+#define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT 1 -+#define CPR3_CPR_CTL_COUNT_MODE_MASK GENMASK(7, 6) -+#define CPR3_CPR_CTL_COUNT_MODE_SHIFT 6 -+#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN 0 -+#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX 1 -+#define CPR3_CPR_CTL_COUNT_MODE_STAGGERED 2 -+#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE 3 -+#define CPR3_CPR_CTL_COUNT_REPEAT_MASK GENMASK(31, 9) -+#define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT 9 -+ -+#define CPR3_REG_CPR_STATUS 0x8 -+#define CPR3_CPR_STATUS_BUSY_MASK BIT(0) -+#define CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK BIT(1) -+ -+/* -+ * This register is not present on controllers that support HW closed-loop -+ * except CPR4 APSS controller. -+ */ -+#define CPR3_REG_CPR_TIMER_AUTO_CONT 0xC -+ -+#define CPR3_REG_CPR_STEP_QUOT 0x14 -+#define CPR3_CPR_STEP_QUOT_MIN_MASK GENMASK(5, 0) -+#define CPR3_CPR_STEP_QUOT_MIN_SHIFT 0 -+#define CPR3_CPR_STEP_QUOT_MAX_MASK GENMASK(11, 6) -+#define CPR3_CPR_STEP_QUOT_MAX_SHIFT 6 -+ -+#define CPR3_REG_GCNT(ro) (0xA0 + 0x4 * (ro)) -+ -+#define CPR3_REG_SENSOR_BYPASS_WRITE(sensor) (0xE0 + 0x4 * ((sensor) / 32)) -+#define CPR3_REG_SENSOR_BYPASS_WRITE_BANK(bank) (0xE0 + 0x4 * (bank)) -+ -+#define CPR3_REG_SENSOR_MASK_WRITE(sensor) (0x120 + 0x4 * ((sensor) / 32)) -+#define CPR3_REG_SENSOR_MASK_WRITE_BANK(bank) (0x120 + 0x4 * (bank)) -+#define CPR3_REG_SENSOR_MASK_READ(sensor) (0x140 + 0x4 * ((sensor) / 32)) -+ -+#define CPR3_REG_SENSOR_OWNER(sensor) (0x200 + 0x4 * (sensor)) -+ -+#define CPR3_REG_CONT_CMD 0x800 -+#define CPR3_CONT_CMD_ACK 0x1 -+#define CPR3_CONT_CMD_NACK 0x0 -+ -+#define CPR3_REG_THRESH(thread) (0x808 + 0x440 * (thread)) -+#define CPR3_THRESH_CONS_DOWN_MASK GENMASK(3, 0) -+#define CPR3_THRESH_CONS_DOWN_SHIFT 0 -+#define CPR3_THRESH_CONS_UP_MASK GENMASK(7, 4) -+#define CPR3_THRESH_CONS_UP_SHIFT 4 -+#define CPR3_THRESH_DOWN_THRESH_MASK GENMASK(12, 8) -+#define CPR3_THRESH_DOWN_THRESH_SHIFT 8 -+#define CPR3_THRESH_UP_THRESH_MASK GENMASK(17, 13) -+#define CPR3_THRESH_UP_THRESH_SHIFT 13 -+ -+#define CPR3_REG_RO_MASK(thread) (0x80C + 0x440 * (thread)) -+ -+#define CPR3_REG_RESULT0(thread) (0x810 + 0x440 * (thread)) -+#define CPR3_RESULT0_BUSY_MASK BIT(0) -+#define CPR3_RESULT0_STEP_DN_MASK BIT(1) -+#define CPR3_RESULT0_STEP_UP_MASK BIT(2) -+#define CPR3_RESULT0_ERROR_STEPS_MASK GENMASK(7, 3) -+#define CPR3_RESULT0_ERROR_STEPS_SHIFT 3 -+#define CPR3_RESULT0_ERROR_MASK GENMASK(19, 8) -+#define CPR3_RESULT0_ERROR_SHIFT 8 -+#define CPR3_RESULT0_NEGATIVE_MASK BIT(20) -+ -+#define CPR3_REG_RESULT1(thread) (0x814 + 0x440 * (thread)) -+#define CPR3_RESULT1_QUOT_MIN_MASK GENMASK(11, 0) -+#define CPR3_RESULT1_QUOT_MIN_SHIFT 0 -+#define CPR3_RESULT1_QUOT_MAX_MASK GENMASK(23, 12) -+#define CPR3_RESULT1_QUOT_MAX_SHIFT 12 -+#define CPR3_RESULT1_RO_MIN_MASK GENMASK(27, 24) -+#define CPR3_RESULT1_RO_MIN_SHIFT 24 -+#define CPR3_RESULT1_RO_MAX_MASK GENMASK(31, 28) -+#define CPR3_RESULT1_RO_MAX_SHIFT 28 -+ -+#define CPR3_REG_RESULT2(thread) (0x818 + 0x440 * (thread)) -+#define CPR3_RESULT2_STEP_QUOT_MIN_MASK GENMASK(5, 0) -+#define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT 0 -+#define CPR3_RESULT2_STEP_QUOT_MAX_MASK GENMASK(11, 6) -+#define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT 6 -+#define CPR3_RESULT2_SENSOR_MIN_MASK GENMASK(23, 16) -+#define CPR3_RESULT2_SENSOR_MIN_SHIFT 16 -+#define CPR3_RESULT2_SENSOR_MAX_MASK GENMASK(31, 24) -+#define CPR3_RESULT2_SENSOR_MAX_SHIFT 24 -+ -+#define CPR3_REG_IRQ_EN 0x81C -+#define CPR3_REG_IRQ_CLEAR 0x820 -+#define CPR3_REG_IRQ_STATUS 0x824 -+#define CPR3_IRQ_UP BIT(3) -+#define CPR3_IRQ_MID BIT(2) -+#define CPR3_IRQ_DOWN BIT(1) -+ -+#define CPR3_REG_TARGET_QUOT(thread, ro) \ -+ (0x840 + 0x440 * (thread) + 0x4 * (ro)) -+ -+/* Registers found only on controllers that support HW closed-loop. */ -+#define CPR3_REG_PD_THROTTLE 0xE8 -+#define CPR3_PD_THROTTLE_DISABLE 0x0 -+ -+#define CPR3_REG_HW_CLOSED_LOOP 0x3000 -+#define CPR3_HW_CLOSED_LOOP_ENABLE 0x0 -+#define CPR3_HW_CLOSED_LOOP_DISABLE 0x1 -+ -+#define CPR3_REG_CPR_TIMER_MID_CONT 0x3004 -+#define CPR3_REG_CPR_TIMER_UP_DN_CONT 0x3008 -+ -+#define CPR3_REG_LAST_MEASUREMENT 0x7F8 -+#define CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT 0 -+#define CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT 4 -+#define CPR3_LAST_MEASUREMENT_THREAD_DN(thread) \ -+ (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT) -+#define CPR3_LAST_MEASUREMENT_THREAD_UP(thread) \ -+ (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT) -+#define CPR3_LAST_MEASUREMENT_AGGR_DN BIT(8) -+#define CPR3_LAST_MEASUREMENT_AGGR_MID BIT(9) -+#define CPR3_LAST_MEASUREMENT_AGGR_UP BIT(10) -+#define CPR3_LAST_MEASUREMENT_VALID BIT(11) -+#define CPR3_LAST_MEASUREMENT_SAW_ERROR BIT(12) -+#define CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK GENMASK(23, 16) -+#define CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT 16 -+ -+/* CPR4 controller specific registers and bit definitions */ -+#define CPR4_REG_CPR_TIMER_CLAMP 0x10 -+#define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27) -+ -+#define CPR4_REG_MISC 0x700 -+#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK GENMASK(23, 20) -+#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT 20 -+#define CPR4_MISC_TEMP_SENSOR_ID_START_MASK GENMASK(27, 24) -+#define CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT 24 -+#define CPR4_MISC_TEMP_SENSOR_ID_END_MASK GENMASK(31, 28) -+#define CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT 28 -+ -+#define CPR4_REG_SAW_ERROR_STEP_LIMIT 0x7A4 -+#define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK GENMASK(4, 0) -+#define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT 0 -+#define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK GENMASK(9, 5) -+#define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT 5 -+ -+#define CPR4_REG_MARGIN_TEMP_CORE_TIMERS 0x7A8 -+#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK GENMASK(28, 18) -+#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT 18 -+ -+#define CPR4_REG_MARGIN_TEMP_CORE(core) (0x7AC + 0x4 * (core)) -+#define CPR4_MARGIN_TEMP_CORE_ADJ_MASK GENMASK(7, 0) -+#define CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT 8 -+ -+#define CPR4_REG_MARGIN_TEMP_POINT0N1 0x7F0 -+#define CPR4_MARGIN_TEMP_POINT0_MASK GENMASK(11, 0) -+#define CPR4_MARGIN_TEMP_POINT0_SHIFT 0 -+#define CPR4_MARGIN_TEMP_POINT1_MASK GENMASK(23, 12) -+#define CPR4_MARGIN_TEMP_POINT1_SHIFT 12 -+#define CPR4_REG_MARGIN_TEMP_POINT2 0x7F4 -+#define CPR4_MARGIN_TEMP_POINT2_MASK GENMASK(11, 0) -+#define CPR4_MARGIN_TEMP_POINT2_SHIFT 0 -+ -+#define CPR4_REG_MARGIN_ADJ_CTL 0x7F8 -+#define CPR4_MARGIN_ADJ_CTL_BOOST_EN BIT(0) -+#define CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN BIT(1) -+#define CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN BIT(2) -+#define CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN BIT(3) -+#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK BIT(4) -+#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE BIT(4) -+#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE 0 -+#define CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN BIT(7) -+#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN BIT(8) -+#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK GENMASK(16, 12) -+#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT 12 -+#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK GENMASK(21, 19) -+#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT 19 -+#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK GENMASK(25, 22) -+#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT 22 -+#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK GENMASK(31, 26) -+#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT 26 -+ -+#define CPR4_REG_CPR_MASK_THREAD(thread) (0x80C + 0x440 * (thread)) -+#define CPR4_CPR_MASK_THREAD_DISABLE_THREAD BIT(31) -+#define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK GENMASK(15, 0) -+ -+/* -+ * The amount of time to wait for the CPR controller to become idle when -+ * performing an aging measurement. -+ */ -+#define CPR3_AGING_MEASUREMENT_TIMEOUT_NS 5000000 -+ -+/* -+ * The number of individual aging measurements to perform which are then -+ * averaged together in order to determine the final aging adjustment value. -+ */ -+#define CPR3_AGING_MEASUREMENT_ITERATIONS 16 -+ -+/* -+ * Aging measurements for the aged and unaged ring oscillators take place a few -+ * microseconds apart. If the vdd-supply voltage fluctuates between the two -+ * measurements, then the difference between them will be incorrect. The -+ * difference could end up too high or too low. This constant defines the -+ * number of lowest and highest measurements to ignore when averaging. -+ */ -+#define CPR3_AGING_MEASUREMENT_FILTER 3 -+ -+/* -+ * The number of times to attempt the full aging measurement sequence before -+ * declaring a measurement failure. -+ */ -+#define CPR3_AGING_RETRY_COUNT 5 -+ -+/* -+ * The maximum time to wait in microseconds for a CPR register write to -+ * complete. -+ */ -+#define CPR3_REGISTER_WRITE_DELAY_US 200 -+ -+static DEFINE_MUTEX(cpr3_controller_list_mutex); -+static LIST_HEAD(cpr3_controller_list); -+static struct dentry *cpr3_debugfs_base; -+ -+/** -+ * cpr3_read() - read four bytes from the memory address specified -+ * @ctrl: Pointer to the CPR3 controller -+ * @offset: Offset in bytes from the CPR3 controller's base address -+ * -+ * Return: memory address value -+ */ -+static inline u32 cpr3_read(struct cpr3_controller *ctrl, u32 offset) -+{ -+ if (!ctrl->cpr_enabled) { -+ cpr3_err(ctrl, "CPR register reads are not possible when CPR clocks are disabled\n"); -+ return 0; -+ } -+ -+ return readl_relaxed(ctrl->cpr_ctrl_base + offset); -+} -+ -+/** -+ * cpr3_write() - write four bytes to the memory address specified -+ * @ctrl: Pointer to the CPR3 controller -+ * @offset: Offset in bytes from the CPR3 controller's base address -+ * @value: Value to write to the memory address -+ * -+ * Return: none -+ */ -+static inline void cpr3_write(struct cpr3_controller *ctrl, u32 offset, -+ u32 value) -+{ -+ if (!ctrl->cpr_enabled) { -+ cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n"); -+ return; -+ } -+ -+ writel_relaxed(value, ctrl->cpr_ctrl_base + offset); -+} -+ -+/** -+ * cpr3_masked_write() - perform a read-modify-write sequence so that only -+ * masked bits are modified -+ * @ctrl: Pointer to the CPR3 controller -+ * @offset: Offset in bytes from the CPR3 controller's base address -+ * @mask: Mask identifying the bits that should be modified -+ * @value: Value to write to the memory address -+ * -+ * Return: none -+ */ -+static inline void cpr3_masked_write(struct cpr3_controller *ctrl, u32 offset, -+ u32 mask, u32 value) -+{ -+ u32 reg_val, orig_val; -+ -+ if (!ctrl->cpr_enabled) { -+ cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n"); -+ return; -+ } -+ -+ reg_val = orig_val = readl_relaxed(ctrl->cpr_ctrl_base + offset); -+ reg_val &= ~mask; -+ reg_val |= value & mask; -+ -+ if (reg_val != orig_val) -+ writel_relaxed(reg_val, ctrl->cpr_ctrl_base + offset); -+} -+ -+/** -+ * cpr3_ctrl_loop_enable() - enable the CPR sensing loop for a given controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: none -+ */ -+static inline void cpr3_ctrl_loop_enable(struct cpr3_controller *ctrl) -+{ -+ if (ctrl->cpr_enabled && !(ctrl->aggr_corner.sdelta -+ && ctrl->aggr_corner.sdelta->allow_boost)) -+ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, -+ CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_ENABLE); -+} -+ -+/** -+ * cpr3_ctrl_loop_disable() - disable the CPR sensing loop for a given -+ * controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: none -+ */ -+static inline void cpr3_ctrl_loop_disable(struct cpr3_controller *ctrl) -+{ -+ if (ctrl->cpr_enabled) -+ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, -+ CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_DISABLE); -+} -+ -+/** -+ * cpr3_clock_enable() - prepare and enable all clocks used by this CPR3 -+ * controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_clock_enable(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ rc = clk_prepare_enable(ctrl->bus_clk); -+ if (rc) { -+ cpr3_err(ctrl, "failed to enable bus clock, rc=%d\n", rc); -+ return rc; -+ } -+ -+ rc = clk_prepare_enable(ctrl->iface_clk); -+ if (rc) { -+ cpr3_err(ctrl, "failed to enable interface clock, rc=%d\n", rc); -+ clk_disable_unprepare(ctrl->bus_clk); -+ return rc; -+ } -+ -+ rc = clk_prepare_enable(ctrl->core_clk); -+ if (rc) { -+ cpr3_err(ctrl, "failed to enable core clock, rc=%d\n", rc); -+ clk_disable_unprepare(ctrl->iface_clk); -+ clk_disable_unprepare(ctrl->bus_clk); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_clock_disable() - disable and unprepare all clocks used by this CPR3 -+ * controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: none -+ */ -+static void cpr3_clock_disable(struct cpr3_controller *ctrl) -+{ -+ clk_disable_unprepare(ctrl->core_clk); -+ clk_disable_unprepare(ctrl->iface_clk); -+ clk_disable_unprepare(ctrl->bus_clk); -+} -+ -+/** -+ * cpr3_ctrl_clear_cpr4_config() - clear the CPR4 register configuration -+ * programmed for current aggregated corner of a given controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl) -+{ -+ struct cpr4_sdelta *aggr_sdelta = ctrl->aggr_corner.sdelta; -+ bool cpr_enabled = ctrl->cpr_enabled; -+ int i, rc = 0; -+ -+ if (!aggr_sdelta || !(aggr_sdelta->allow_core_count_adj -+ || aggr_sdelta->allow_temp_adj || aggr_sdelta->allow_boost)) -+ /* cpr4 features are not enabled */ -+ return 0; -+ -+ /* Ensure that CPR clocks are enabled before writing to registers. */ -+ if (!cpr_enabled) { -+ rc = cpr3_clock_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); -+ return rc; -+ } -+ ctrl->cpr_enabled = true; -+ } -+ -+ /* -+ * Clear feature enable configuration made for current -+ * aggregated corner. -+ */ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK -+ | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN -+ | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN -+ | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN -+ | CPR4_MARGIN_ADJ_CTL_BOOST_EN -+ | CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, 0); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MISC, -+ CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK, -+ 0 << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT); -+ -+ for (i = 0; i <= aggr_sdelta->max_core_count; i++) { -+ /* Clear voltage margin adjustments programmed in TEMP_COREi */ -+ cpr3_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE(i), 0); -+ } -+ -+ /* Turn off CPR clocks if they were off before this function call. */ -+ if (!cpr_enabled) { -+ cpr3_clock_disable(ctrl); -+ ctrl->cpr_enabled = false; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_closed_loop_enable() - enable logical CPR closed-loop operation -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_closed_loop_enable(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ if (!ctrl->cpr_allowed_hw || !ctrl->cpr_allowed_sw) { -+ cpr3_err(ctrl, "cannot enable closed-loop CPR operation because it is disallowed\n"); -+ return -EPERM; -+ } else if (ctrl->cpr_enabled) { -+ /* Already enabled */ -+ return 0; -+ } else if (ctrl->cpr_suspended) { -+ /* -+ * CPR must remain disabled as the system is entering suspend. -+ */ -+ return 0; -+ } -+ -+ rc = cpr3_clock_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "unable to enable CPR clocks, rc=%d\n", rc); -+ return rc; -+ } -+ -+ ctrl->cpr_enabled = true; -+ cpr3_debug(ctrl, "CPR closed-loop operation enabled\n"); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_closed_loop_disable() - disable logical CPR closed-loop operation -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static inline int cpr3_closed_loop_disable(struct cpr3_controller *ctrl) -+{ -+ if (!ctrl->cpr_enabled) { -+ /* Already disabled */ -+ return 0; -+ } -+ -+ cpr3_clock_disable(ctrl); -+ ctrl->cpr_enabled = false; -+ cpr3_debug(ctrl, "CPR closed-loop operation disabled\n"); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_get_gcnt() - returns the GCNT register value corresponding -+ * to the clock rate and sensor time of the CPR3 controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: GCNT value -+ */ -+static u32 cpr3_regulator_get_gcnt(struct cpr3_controller *ctrl) -+{ -+ u64 temp; -+ unsigned int remainder; -+ u32 gcnt; -+ -+ temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->sensor_time; -+ remainder = do_div(temp, 1000000000); -+ if (remainder) -+ temp++; -+ /* -+ * GCNT == 0 corresponds to a single ref clock measurement interval so -+ * offset GCNT values by 1. -+ */ -+ gcnt = temp - 1; -+ -+ return gcnt; -+} -+ -+/** -+ * cpr3_regulator_init_thread() - performs hardware initialization of CPR -+ * thread registers -+ * @thread: Pointer to the CPR3 thread -+ * -+ * CPR interface/bus clocks must be enabled before calling this function. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_init_thread(struct cpr3_thread *thread) -+{ -+ u32 reg; -+ -+ reg = (thread->consecutive_up << CPR3_THRESH_CONS_UP_SHIFT) -+ & CPR3_THRESH_CONS_UP_MASK; -+ reg |= (thread->consecutive_down << CPR3_THRESH_CONS_DOWN_SHIFT) -+ & CPR3_THRESH_CONS_DOWN_MASK; -+ reg |= (thread->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT) -+ & CPR3_THRESH_UP_THRESH_MASK; -+ reg |= (thread->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT) -+ & CPR3_THRESH_DOWN_THRESH_MASK; -+ -+ cpr3_write(thread->ctrl, CPR3_REG_THRESH(thread->thread_id), reg); -+ -+ /* -+ * Mask all RO's initially so that unused thread doesn't contribute -+ * to closed-loop voltage. -+ */ -+ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), -+ CPR3_RO_MASK); -+ -+ return 0; -+} -+ -+/** -+ * cpr4_regulator_init_temp_points() - performs hardware initialization of CPR4 -+ * registers to track tsen temperature data and also specify the -+ * temperature band range values to apply different voltage margins -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * CPR interface/bus clocks must be enabled before calling this function. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_regulator_init_temp_points(struct cpr3_controller *ctrl) -+{ -+ if (!ctrl->allow_temp_adj) -+ return 0; -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MISC, -+ CPR4_MISC_TEMP_SENSOR_ID_START_MASK, -+ ctrl->temp_sensor_id_start -+ << CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MISC, -+ CPR4_MISC_TEMP_SENSOR_ID_END_MASK, -+ ctrl->temp_sensor_id_end -+ << CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT2, -+ CPR4_MARGIN_TEMP_POINT2_MASK, -+ (ctrl->temp_band_count == 4 ? ctrl->temp_points[2] : 0x7FF) -+ << CPR4_MARGIN_TEMP_POINT2_SHIFT); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1, -+ CPR4_MARGIN_TEMP_POINT1_MASK, -+ (ctrl->temp_band_count >= 3 ? ctrl->temp_points[1] : 0x7FF) -+ << CPR4_MARGIN_TEMP_POINT1_SHIFT); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1, -+ CPR4_MARGIN_TEMP_POINT0_MASK, -+ (ctrl->temp_band_count >= 2 ? ctrl->temp_points[0] : 0x7FF) -+ << CPR4_MARGIN_TEMP_POINT0_SHIFT); -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_init_cpr4() - performs hardware initialization at the -+ * controller and thread level required for CPR4 operation. -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * CPR interface/bus clocks must be enabled before calling this function. -+ * This function allocates sdelta structures and sdelta tables for aggregated -+ * corners of the controller and its threads. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) -+{ -+ struct cpr3_thread *thread; -+ struct cpr3_regulator *vreg; -+ struct cpr4_sdelta *sdelta; -+ int i, j, ctrl_max_core_count, thread_max_core_count, rc = 0; -+ bool ctrl_valid_sdelta, thread_valid_sdelta; -+ u32 pmic_step_size = 1; -+ int thread_id = 0; -+ u64 temp; -+ -+ if (ctrl->supports_hw_closed_loop) { -+ if (ctrl->saw_use_unit_mV) -+ pmic_step_size = ctrl->step_volt / 1000; -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK, -+ (pmic_step_size -+ << CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT)); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT, -+ CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK, -+ (ctrl->down_error_step_limit -+ << CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT)); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT, -+ CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK, -+ (ctrl->up_error_step_limit -+ << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT)); -+ -+ /* -+ * Enable thread aggregation regardless of which threads are -+ * enabled or disabled. -+ */ -+ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP, -+ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, -+ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN); -+ -+ switch (ctrl->thread_count) { -+ case 0: -+ /* Disable both threads */ -+ cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(0), -+ CPR4_CPR_MASK_THREAD_DISABLE_THREAD -+ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, -+ CPR4_CPR_MASK_THREAD_DISABLE_THREAD -+ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(1), -+ CPR4_CPR_MASK_THREAD_DISABLE_THREAD -+ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, -+ CPR4_CPR_MASK_THREAD_DISABLE_THREAD -+ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); -+ break; -+ case 1: -+ /* Disable unused thread */ -+ thread_id = ctrl->thread[0].thread_id ? 0 : 1; -+ cpr3_masked_write(ctrl, -+ CPR4_REG_CPR_MASK_THREAD(thread_id), -+ CPR4_CPR_MASK_THREAD_DISABLE_THREAD -+ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, -+ CPR4_CPR_MASK_THREAD_DISABLE_THREAD -+ | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); -+ break; -+ } -+ } -+ -+ if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj -+ && !ctrl->allow_boost) { -+ /* -+ * Skip below configuration as none of the features -+ * are enabled. -+ */ -+ return rc; -+ } -+ -+ if (ctrl->supports_hw_closed_loop) -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN, -+ CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK, -+ ctrl->step_quot_fixed -+ << CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN, -+ (ctrl->use_dynamic_step_quot -+ ? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0)); -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK, -+ ctrl->initial_temp_band -+ << CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT); -+ -+ rc = cpr4_regulator_init_temp_points(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "initialize temp points failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ if (ctrl->voltage_settling_time) { -+ /* -+ * Configure the settling timer used to account for -+ * one VDD supply step. -+ */ -+ temp = (u64)ctrl->cpr_clock_rate -+ * (u64)ctrl->voltage_settling_time; -+ do_div(temp, 1000000000); -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS, -+ CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK, -+ temp -+ << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT); -+ } -+ -+ /* -+ * Allocate memory for cpr4_sdelta structure and sdelta table for -+ * controller aggregated corner by finding the maximum core count -+ * used by any cpr3 regulators. -+ */ -+ ctrl_max_core_count = 1; -+ ctrl_valid_sdelta = false; -+ for (i = 0; i < ctrl->thread_count; i++) { -+ thread = &ctrl->thread[i]; -+ -+ /* -+ * Allocate memory for cpr4_sdelta structure and sdelta table -+ * for thread aggregated corner by finding the maximum core -+ * count used by any cpr3 regulators of the thread. -+ */ -+ thread_max_core_count = 1; -+ thread_valid_sdelta = false; -+ for (j = 0; j < thread->vreg_count; j++) { -+ vreg = &thread->vreg[j]; -+ thread_max_core_count = max(thread_max_core_count, -+ vreg->max_core_count); -+ thread_valid_sdelta |= (vreg->allow_core_count_adj -+ | vreg->allow_temp_adj -+ | vreg->allow_boost); -+ } -+ if (thread_valid_sdelta) { -+ sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), -+ GFP_KERNEL); -+ if (!sdelta) -+ return -ENOMEM; -+ -+ sdelta->table = devm_kcalloc(ctrl->dev, -+ thread_max_core_count -+ * ctrl->temp_band_count, -+ sizeof(*sdelta->table), -+ GFP_KERNEL); -+ if (!sdelta->table) -+ return -ENOMEM; -+ -+ sdelta->boost_table = devm_kcalloc(ctrl->dev, -+ ctrl->temp_band_count, -+ sizeof(*sdelta->boost_table), -+ GFP_KERNEL); -+ if (!sdelta->boost_table) -+ return -ENOMEM; -+ -+ thread->aggr_corner.sdelta = sdelta; -+ } -+ -+ ctrl_valid_sdelta |= thread_valid_sdelta; -+ ctrl_max_core_count = max(ctrl_max_core_count, -+ thread_max_core_count); -+ } -+ -+ if (ctrl_valid_sdelta) { -+ sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), GFP_KERNEL); -+ if (!sdelta) -+ return -ENOMEM; -+ -+ sdelta->table = devm_kcalloc(ctrl->dev, ctrl_max_core_count -+ * ctrl->temp_band_count, -+ sizeof(*sdelta->table), GFP_KERNEL); -+ if (!sdelta->table) -+ return -ENOMEM; -+ -+ sdelta->boost_table = devm_kcalloc(ctrl->dev, -+ ctrl->temp_band_count, -+ sizeof(*sdelta->boost_table), -+ GFP_KERNEL); -+ if (!sdelta->boost_table) -+ return -ENOMEM; -+ -+ ctrl->aggr_corner.sdelta = sdelta; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_write_temp_core_margin() - programs hardware SDELTA registers with -+ * the voltage margin adjustments that need to be applied for -+ * different online core-count and temperature bands. -+ * @ctrl: Pointer to the CPR3 controller -+ * @addr: SDELTA register address -+ * @temp_core_adj: Array of voltage margin values for different temperature -+ * bands. -+ * -+ * CPR interface/bus clocks must be enabled before calling this function. -+ * -+ * Return: none -+ */ -+static void cpr3_write_temp_core_margin(struct cpr3_controller *ctrl, -+ int addr, int *temp_core_adj) -+{ -+ int i, margin_steps; -+ u32 reg = 0; -+ -+ for (i = 0; i < ctrl->temp_band_count; i++) { -+ margin_steps = max(min(temp_core_adj[i], 127), -128); -+ reg |= (margin_steps & CPR4_MARGIN_TEMP_CORE_ADJ_MASK) << -+ (i * CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT); -+ } -+ -+ cpr3_write(ctrl, addr, reg); -+ cpr3_debug(ctrl, "sdelta offset=0x%08x, val=0x%08x\n", addr, reg); -+} -+ -+/** -+ * cpr3_controller_program_sdelta() - programs hardware SDELTA registers with -+ * the voltage margin adjustments that need to be applied at -+ * different online core-count and temperature bands. Also, -+ * programs hardware register configuration for per-online-core -+ * and per-temperature based adjustments. -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * CPR interface/bus clocks must be enabled before calling this function. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl) -+{ -+ struct cpr3_corner *corner = &ctrl->aggr_corner; -+ struct cpr4_sdelta *sdelta = corner->sdelta; -+ int i, index, max_core_count, rc = 0; -+ bool cpr_enabled = ctrl->cpr_enabled; -+ -+ if (!sdelta) -+ /* cpr4_sdelta not defined for current aggregated corner */ -+ return 0; -+ -+ if (ctrl->supports_hw_closed_loop && ctrl->cpr_enabled) { -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, -+ (ctrl->use_hw_closed_loop && !sdelta->allow_boost) -+ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0); -+ } -+ -+ if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj -+ && !sdelta->allow_boost) { -+ /* -+ * Per-online-core, per-temperature and voltage boost -+ * adjustments are disabled for this aggregation corner. -+ */ -+ return 0; -+ } -+ -+ /* Ensure that CPR clocks are enabled before writing to registers. */ -+ if (!cpr_enabled) { -+ rc = cpr3_clock_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); -+ return rc; -+ } -+ ctrl->cpr_enabled = true; -+ } -+ -+ max_core_count = sdelta->max_core_count; -+ -+ if (sdelta->allow_core_count_adj || sdelta->allow_temp_adj) { -+ if (sdelta->allow_core_count_adj) { -+ /* Program TEMP_CORE0 to same margins as TEMP_CORE1 */ -+ cpr3_write_temp_core_margin(ctrl, -+ CPR4_REG_MARGIN_TEMP_CORE(0), -+ &sdelta->table[0]); -+ } -+ -+ for (i = 0; i < max_core_count; i++) { -+ index = i * sdelta->temp_band_count; -+ /* -+ * Program TEMP_COREi with voltage margin adjustments -+ * that need to be applied when the number of cores -+ * becomes i. -+ */ -+ cpr3_write_temp_core_margin(ctrl, -+ CPR4_REG_MARGIN_TEMP_CORE( -+ sdelta->allow_core_count_adj -+ ? i + 1 : max_core_count), -+ &sdelta->table[index]); -+ } -+ } -+ -+ if (sdelta->allow_boost) { -+ /* Program only boost_num_cores row of SDELTA */ -+ cpr3_write_temp_core_margin(ctrl, -+ CPR4_REG_MARGIN_TEMP_CORE(sdelta->boost_num_cores), -+ &sdelta->boost_table[0]); -+ } -+ -+ if (!sdelta->allow_core_count_adj && !sdelta->allow_boost) { -+ cpr3_masked_write(ctrl, CPR4_REG_MISC, -+ CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK, -+ max_core_count -+ << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT); -+ } -+ -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK -+ | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN -+ | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN -+ | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN -+ | CPR4_MARGIN_ADJ_CTL_BOOST_EN, -+ max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT -+ | ((sdelta->allow_core_count_adj || sdelta->allow_boost) -+ ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0) -+ | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop) -+ ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0) -+ | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost) -+ || !ctrl->supports_hw_closed_loop) -+ ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0) -+ | (sdelta->allow_boost -+ ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0)); -+ -+ /* -+ * Ensure that all previous CPR register writes have completed before -+ * continuing. -+ */ -+ mb(); -+ -+ /* Turn off CPR clocks if they were off before this function call. */ -+ if (!cpr_enabled) { -+ cpr3_clock_disable(ctrl); -+ ctrl->cpr_enabled = false; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_init_ctrl() - performs hardware initialization of CPR -+ * controller registers -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl) -+{ -+ int i, j, k, m, rc; -+ u32 ro_used = 0; -+ u32 gcnt, cont_dly, up_down_dly, val; -+ u64 temp; -+ char *mode; -+ -+ if (ctrl->core_clk) { -+ rc = clk_set_rate(ctrl->core_clk, ctrl->cpr_clock_rate); -+ if (rc) { -+ cpr3_err(ctrl, "clk_set_rate(core_clk, %u) failed, rc=%d\n", -+ ctrl->cpr_clock_rate, rc); -+ return rc; -+ } -+ } -+ -+ rc = cpr3_clock_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); -+ return rc; -+ } -+ ctrl->cpr_enabled = true; -+ -+ /* Find all RO's used by any corner of any regulator. */ -+ for (i = 0; i < ctrl->thread_count; i++) -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) -+ for (k = 0; k < ctrl->thread[i].vreg[j].corner_count; -+ k++) -+ for (m = 0; m < CPR3_RO_COUNT; m++) -+ if (ctrl->thread[i].vreg[j].corner[k]. -+ target_quot[m]) -+ ro_used |= BIT(m); -+ -+ /* Configure the GCNT of the RO's that will be used */ -+ gcnt = cpr3_regulator_get_gcnt(ctrl); -+ for (i = 0; i < CPR3_RO_COUNT; i++) -+ if (ro_used & BIT(i)) -+ cpr3_write(ctrl, CPR3_REG_GCNT(i), gcnt); -+ -+ /* Configure the loop delay time */ -+ temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->loop_time; -+ do_div(temp, 1000000000); -+ cont_dly = temp; -+ if (ctrl->supports_hw_closed_loop -+ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly); -+ else -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, cont_dly); -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ temp = (u64)ctrl->cpr_clock_rate * -+ (u64)ctrl->up_down_delay_time; -+ do_div(temp, 1000000000); -+ up_down_dly = temp; -+ if (ctrl->supports_hw_closed_loop) -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, -+ up_down_dly); -+ cpr3_debug(ctrl, "up_down_dly=%u, up_down_delay_time=%u ns\n", -+ up_down_dly, ctrl->up_down_delay_time); -+ } -+ -+ cpr3_debug(ctrl, "cpr_clock_rate=%u HZ, sensor_time=%u ns, loop_time=%u ns, gcnt=%u, cont_dly=%u\n", -+ ctrl->cpr_clock_rate, ctrl->sensor_time, ctrl->loop_time, -+ gcnt, cont_dly); -+ -+ /* Configure CPR sensor operation */ -+ val = (ctrl->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT) -+ & CPR3_CPR_CTL_IDLE_CLOCKS_MASK; -+ val |= (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT) -+ & CPR3_CPR_CTL_COUNT_MODE_MASK; -+ val |= (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT) -+ & CPR3_CPR_CTL_COUNT_REPEAT_MASK; -+ cpr3_write(ctrl, CPR3_REG_CPR_CTL, val); -+ -+ cpr3_debug(ctrl, "idle_clocks=%u, count_mode=%u, count_repeat=%u; CPR_CTL=0x%08X\n", -+ ctrl->idle_clocks, ctrl->count_mode, ctrl->count_repeat, val); -+ -+ /* Configure CPR default step quotients */ -+ val = (ctrl->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT) -+ & CPR3_CPR_STEP_QUOT_MIN_MASK; -+ val |= (ctrl->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT) -+ & CPR3_CPR_STEP_QUOT_MAX_MASK; -+ cpr3_write(ctrl, CPR3_REG_CPR_STEP_QUOT, val); -+ -+ cpr3_debug(ctrl, "step_quot_min=%u, step_quot_max=%u; STEP_QUOT=0x%08X\n", -+ ctrl->step_quot_init_min, ctrl->step_quot_init_max, val); -+ -+ /* Configure the CPR sensor ownership */ -+ for (i = 0; i < ctrl->sensor_count; i++) -+ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(i), -+ ctrl->sensor_owner[i]); -+ -+ /* Configure per-thread registers */ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ rc = cpr3_regulator_init_thread(&ctrl->thread[i]); -+ if (rc) { -+ cpr3_err(ctrl, "CPR thread register initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ if (ctrl->supports_hw_closed_loop) { -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, -+ ctrl->use_hw_closed_loop -+ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE -+ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); -+ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, -+ ctrl->use_hw_closed_loop -+ ? CPR3_HW_CLOSED_LOOP_ENABLE -+ : CPR3_HW_CLOSED_LOOP_DISABLE); -+ -+ cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n", -+ ctrl->proc_clock_throttle); -+ } -+ -+ if ((ctrl->use_hw_closed_loop || -+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) && -+ ctrl->vdd_limit_regulator) { -+ rc = regulator_enable(ctrl->vdd_limit_regulator); -+ if (rc) { -+ cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ } -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_regulator_init_cpr4(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "CPR4-specific controller initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ /* Ensure that all register writes complete before disabling clocks. */ -+ wmb(); -+ -+ cpr3_clock_disable(ctrl); -+ ctrl->cpr_enabled = false; -+ -+ if (!ctrl->cpr_allowed_sw || !ctrl->cpr_allowed_hw) -+ mode = "open-loop"; -+ else if (ctrl->supports_hw_closed_loop) -+ mode = ctrl->use_hw_closed_loop -+ ? "HW closed-loop" : "SW closed-loop"; -+ else -+ mode = "closed-loop"; -+ -+ cpr3_info(ctrl, "Default CPR mode = %s", mode); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_set_target_quot() - configure the target quotient for each -+ * RO of the CPR3 thread and set the RO mask -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_set_target_quot(struct cpr3_thread *thread) -+{ -+ u32 new_quot, last_quot; -+ int i; -+ -+ if (thread->aggr_corner.ro_mask == CPR3_RO_MASK -+ && thread->last_closed_loop_aggr_corner.ro_mask == CPR3_RO_MASK) { -+ /* Avoid writing target quotients since all RO's are masked. */ -+ return; -+ } else if (thread->aggr_corner.ro_mask == CPR3_RO_MASK) { -+ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), -+ CPR3_RO_MASK); -+ thread->last_closed_loop_aggr_corner.ro_mask = CPR3_RO_MASK; -+ /* -+ * Only the RO_MASK register needs to be written since all -+ * RO's are masked. -+ */ -+ return; -+ } else if (thread->aggr_corner.ro_mask -+ != thread->last_closed_loop_aggr_corner.ro_mask) { -+ cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id), -+ thread->aggr_corner.ro_mask); -+ } -+ -+ for (i = 0; i < CPR3_RO_COUNT; i++) { -+ new_quot = thread->aggr_corner.target_quot[i]; -+ last_quot = thread->last_closed_loop_aggr_corner.target_quot[i]; -+ if (new_quot != last_quot) -+ cpr3_write(thread->ctrl, -+ CPR3_REG_TARGET_QUOT(thread->thread_id, i), -+ new_quot); -+ } -+ -+ thread->last_closed_loop_aggr_corner = thread->aggr_corner; -+ -+ return; -+} -+ -+/** -+ * cpr3_update_vreg_closed_loop_volt() - update the last known settled -+ * closed loop voltage for a CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * @vdd_volt: Last known settled voltage in microvolts for the -+ * VDD supply -+ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register -+ * -+ * Return: none -+ */ -+static void cpr3_update_vreg_closed_loop_volt(struct cpr3_regulator *vreg, -+ int vdd_volt, u32 reg_last_measurement) -+{ -+ bool step_dn, step_up, aggr_step_up, aggr_step_dn, aggr_step_mid; -+ bool valid, pd_valid, saw_error; -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ struct cpr3_corner *corner; -+ u32 id; -+ -+ if (vreg->last_closed_loop_corner == CPR3_REGULATOR_CORNER_INVALID) -+ return; -+ else -+ corner = &vreg->corner[vreg->last_closed_loop_corner]; -+ -+ if (vreg->thread->last_closed_loop_aggr_corner.ro_mask -+ == CPR3_RO_MASK || !vreg->aggregated) { -+ return; -+ } else if (!ctrl->cpr_enabled || !ctrl->last_corner_was_closed_loop) { -+ return; -+ } else if (ctrl->thread_count == 1 -+ && vdd_volt >= corner->floor_volt -+ && vdd_volt <= corner->ceiling_volt) { -+ corner->last_volt = vdd_volt; -+ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n", -+ vreg->last_closed_loop_corner, corner->last_volt, -+ vreg->last_closed_loop_corner, -+ corner->ceiling_volt, -+ vreg->last_closed_loop_corner, -+ corner->floor_volt); -+ return; -+ } else if (!ctrl->supports_hw_closed_loop) { -+ return; -+ } else if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPR3) { -+ corner->last_volt = vdd_volt; -+ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n", -+ vreg->last_closed_loop_corner, corner->last_volt, -+ vreg->last_closed_loop_corner, -+ corner->ceiling_volt, -+ vreg->last_closed_loop_corner, -+ corner->floor_volt); -+ return; -+ } -+ -+ /* CPR clocks are on and HW closed loop is supported */ -+ valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID); -+ if (!valid) { -+ cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X valid bit not set\n", -+ reg_last_measurement); -+ return; -+ } -+ -+ id = vreg->thread->thread_id; -+ -+ step_dn -+ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_DN(id)); -+ step_up -+ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_UP(id)); -+ aggr_step_dn = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_DN); -+ aggr_step_mid -+ = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_MID); -+ aggr_step_up = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_UP); -+ saw_error = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_SAW_ERROR); -+ pd_valid -+ = !((((reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK) -+ >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT) -+ & vreg->pd_bypass_mask) == vreg->pd_bypass_mask); -+ -+ if (!pd_valid) { -+ cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X, all power domains bypassed\n", -+ reg_last_measurement); -+ return; -+ } else if (step_dn && step_up) { -+ cpr3_err(vreg, "both up and down status bits set, CPR_LAST_VALID_MEASUREMENT=0x%X\n", -+ reg_last_measurement); -+ return; -+ } else if (aggr_step_dn && step_dn && vdd_volt < corner->last_volt -+ && vdd_volt >= corner->floor_volt) { -+ corner->last_volt = vdd_volt; -+ } else if (aggr_step_up && step_up && vdd_volt > corner->last_volt -+ && vdd_volt <= corner->ceiling_volt) { -+ corner->last_volt = vdd_volt; -+ } else if (aggr_step_mid -+ && vdd_volt >= corner->floor_volt -+ && vdd_volt <= corner->ceiling_volt) { -+ corner->last_volt = vdd_volt; -+ } else if (saw_error && (vdd_volt == corner->ceiling_volt -+ || vdd_volt == corner->floor_volt)) { -+ corner->last_volt = vdd_volt; -+ } else { -+ cpr3_debug(vreg, "last_volt not updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, vdd_volt=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n", -+ vreg->last_closed_loop_corner, corner->last_volt, -+ vreg->last_closed_loop_corner, -+ corner->ceiling_volt, -+ vreg->last_closed_loop_corner, corner->floor_volt, -+ vdd_volt, reg_last_measurement); -+ return; -+ } -+ -+ cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n", -+ vreg->last_closed_loop_corner, corner->last_volt, -+ vreg->last_closed_loop_corner, corner->ceiling_volt, -+ vreg->last_closed_loop_corner, corner->floor_volt, -+ reg_last_measurement); -+} -+ -+/** -+ * cpr3_regulator_mem_acc_bhs_used() - determines if mem-acc regulators powered -+ * through a BHS are associated with the CPR3 controller or any of -+ * the CPR3 regulators it controls. -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * This function determines if the CPR3 controller or any of its CPR3 regulators -+ * need to manage mem-acc regulators that are currently powered through a BHS -+ * and whose corner selection is based upon a particular voltage threshold. -+ * -+ * Return: true or false -+ */ -+static bool cpr3_regulator_mem_acc_bhs_used(struct cpr3_controller *ctrl) -+{ -+ struct cpr3_regulator *vreg; -+ int i, j; -+ -+ if (!ctrl->mem_acc_threshold_volt) -+ return false; -+ -+ if (ctrl->mem_acc_regulator) -+ return true; -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ -+ if (vreg->mem_acc_regulator) -+ return true; -+ } -+ } -+ -+ return false; -+} -+ -+/** -+ * cpr3_regulator_config_bhs_mem_acc() - configure the mem-acc regulator -+ * settings for hardware blocks currently powered through the BHS. -+ * @ctrl: Pointer to the CPR3 controller -+ * @new_volt: New voltage in microvolts that VDD supply needs to -+ * end up at -+ * @last_volt: Pointer to the last known voltage in microvolts for the -+ * VDD supply -+ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max -+ * corner aggregated from all CPR3 threads managed by the -+ * CPR3 controller -+ * -+ * This function programs the mem-acc regulator corners for CPR3 regulators -+ * whose LDO regulators are in bypassed state. The function also handles -+ * CPR3 controllers which utilize mem-acc regulators that operate independently -+ * from the LDO hardware and that must be programmed when the VDD supply -+ * crosses a particular voltage threshold. -+ * -+ * Return: 0 on success, errno on failure. If the VDD supply voltage is -+ * modified, last_volt is updated to reflect the new voltage setpoint. -+ */ -+static int cpr3_regulator_config_bhs_mem_acc(struct cpr3_controller *ctrl, -+ int new_volt, int *last_volt, -+ struct cpr3_corner *aggr_corner) -+{ -+ struct cpr3_regulator *vreg; -+ int i, j, rc, mem_acc_corn, safe_volt; -+ int mem_acc_volt = ctrl->mem_acc_threshold_volt; -+ int ref_volt; -+ -+ if (!cpr3_regulator_mem_acc_bhs_used(ctrl)) -+ return 0; -+ -+ ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt : -+ new_volt; -+ -+ if (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) || -+ (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))) { -+ if (ref_volt < *last_volt) -+ safe_volt = max(mem_acc_volt, aggr_corner->last_volt); -+ else -+ safe_volt = max(mem_acc_volt, *last_volt); -+ -+ rc = regulator_set_voltage(ctrl->vdd_regulator, safe_volt, -+ new_volt < *last_volt ? -+ ctrl->aggr_corner.ceiling_volt : -+ new_volt); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", -+ safe_volt, rc); -+ return rc; -+ } -+ -+ *last_volt = safe_volt; -+ -+ mem_acc_corn = ref_volt < mem_acc_volt ? -+ ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] : -+ ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER]; -+ -+ if (ctrl->mem_acc_regulator) { -+ rc = regulator_set_voltage(ctrl->mem_acc_regulator, -+ mem_acc_corn, mem_acc_corn); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", -+ mem_acc_corn, rc); -+ return rc; -+ } -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ -+ if (!vreg->mem_acc_regulator) -+ continue; -+ -+ rc = regulator_set_voltage( -+ vreg->mem_acc_regulator, mem_acc_corn, -+ mem_acc_corn); -+ if (rc) { -+ cpr3_err(vreg, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", -+ mem_acc_corn, rc); -+ return rc; -+ } -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_switch_apm_mode() - switch the mode of the APM controller -+ * associated with a given CPR3 controller -+ * @ctrl: Pointer to the CPR3 controller -+ * @new_volt: New voltage in microvolts that VDD supply needs to -+ * end up at -+ * @last_volt: Pointer to the last known voltage in microvolts for the -+ * VDD supply -+ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max -+ * corner aggregated from all CPR3 threads managed by the -+ * CPR3 controller -+ * -+ * This function requests a switch of the APM mode while guaranteeing -+ * any LDO regulator hardware requirements are satisfied. The function must -+ * be called once it is known a new VDD supply setpoint crosses the APM -+ * voltage threshold. -+ * -+ * Return: 0 on success, errno on failure. If the VDD supply voltage is -+ * modified, last_volt is updated to reflect the new voltage setpoint. -+ */ -+static int cpr3_regulator_switch_apm_mode(struct cpr3_controller *ctrl, -+ int new_volt, int *last_volt, -+ struct cpr3_corner *aggr_corner) -+{ -+ struct regulator *vdd = ctrl->vdd_regulator; -+ int apm_volt = ctrl->apm_threshold_volt; -+ int orig_last_volt = *last_volt; -+ int rc; -+ -+ rc = regulator_set_voltage(vdd, apm_volt, apm_volt); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", -+ apm_volt, rc); -+ return rc; -+ } -+ -+ *last_volt = apm_volt; -+ -+ rc = msm_apm_set_supply(ctrl->apm, new_volt >= apm_volt -+ ? ctrl->apm_high_supply : ctrl->apm_low_supply); -+ if (rc) { -+ cpr3_err(ctrl, "APM switch failed, rc=%d\n", rc); -+ /* Roll back the voltage. */ -+ regulator_set_voltage(vdd, orig_last_volt, INT_MAX); -+ *last_volt = orig_last_volt; -+ return rc; -+ } -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_config_voltage_crossings() - configure APM and mem-acc -+ * settings depending upon a new VDD supply setpoint -+ * -+ * @ctrl: Pointer to the CPR3 controller -+ * @new_volt: New voltage in microvolts that VDD supply needs to -+ * end up at -+ * @last_volt: Pointer to the last known voltage in microvolts for the -+ * VDD supply -+ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max -+ * corner aggregated from all CPR3 threads managed by the -+ * CPR3 controller -+ * -+ * This function handles the APM and mem-acc regulator reconfiguration if -+ * the new VDD supply voltage will result in crossing their respective voltage -+ * thresholds. -+ * -+ * Return: 0 on success, errno on failure. If the VDD supply voltage is -+ * modified, last_volt is updated to reflect the new voltage setpoint. -+ */ -+static int cpr3_regulator_config_voltage_crossings(struct cpr3_controller *ctrl, -+ int new_volt, int *last_volt, -+ struct cpr3_corner *aggr_corner) -+{ -+ bool apm_crossing = false, mem_acc_crossing = false; -+ bool mem_acc_bhs_used; -+ int apm_volt = ctrl->apm_threshold_volt; -+ int mem_acc_volt = ctrl->mem_acc_threshold_volt; -+ int ref_volt, rc; -+ -+ if (ctrl->apm && apm_volt > 0 -+ && ((*last_volt < apm_volt && apm_volt <= new_volt) -+ || (*last_volt >= apm_volt && apm_volt > new_volt))) -+ apm_crossing = true; -+ -+ mem_acc_bhs_used = cpr3_regulator_mem_acc_bhs_used(ctrl); -+ -+ ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt : -+ new_volt; -+ -+ if (mem_acc_bhs_used && -+ (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) || -+ (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt)))) -+ mem_acc_crossing = true; -+ -+ if (apm_crossing && mem_acc_crossing) { -+ if ((new_volt < *last_volt && apm_volt >= mem_acc_volt) || -+ (new_volt >= *last_volt && apm_volt < mem_acc_volt)) { -+ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, -+ last_volt, -+ aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to switch APM mode\n"); -+ return rc; -+ } -+ -+ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, -+ last_volt, aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); -+ return rc; -+ } -+ } else { -+ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, -+ last_volt, aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); -+ return rc; -+ } -+ -+ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, -+ last_volt, -+ aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to switch APM mode\n"); -+ return rc; -+ } -+ } -+ } else if (apm_crossing) { -+ rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, last_volt, -+ aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to switch APM mode\n"); -+ return rc; -+ } -+ } else if (mem_acc_crossing) { -+ rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt, -+ last_volt, aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n"); -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_config_mem_acc() - configure the corner of the mem-acc -+ * regulator associated with the CPR3 controller -+ * @ctrl: Pointer to the CPR3 controller -+ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max -+ * corner aggregated from all CPR3 threads managed by the -+ * CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_config_mem_acc(struct cpr3_controller *ctrl, -+ struct cpr3_corner *aggr_corner) -+{ -+ int rc; -+ -+ if (ctrl->mem_acc_regulator && aggr_corner->mem_acc_volt) { -+ rc = regulator_set_voltage(ctrl->mem_acc_regulator, -+ aggr_corner->mem_acc_volt, -+ aggr_corner->mem_acc_volt); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n", -+ aggr_corner->mem_acc_volt, rc); -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_scale_vdd_voltage() - scale the CPR controlled VDD supply -+ * voltage to the new level while satisfying any other hardware -+ * requirements -+ * @ctrl: Pointer to the CPR3 controller -+ * @new_volt: New voltage in microvolts that VDD supply needs to end -+ * up at -+ * @last_volt: Last known voltage in microvolts for the VDD supply -+ * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max -+ * corner aggregated from all CPR3 threads managed by the -+ * CPR3 controller -+ * -+ * This function scales the CPR controlled VDD supply voltage from its -+ * current level to the new voltage that is specified. If the supply is -+ * configured to use the APM and the APM threshold is crossed as a result of -+ * the voltage scaling, then this function also stops at the APM threshold, -+ * switches the APM source, and finally sets the final new voltage. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl, -+ int new_volt, int last_volt, -+ struct cpr3_corner *aggr_corner) -+{ -+ struct regulator *vdd = ctrl->vdd_regulator; -+ int rc; -+ -+ if (new_volt < last_volt) { -+ rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); -+ if (rc) -+ return rc; -+ } else { -+ /* Increasing VDD voltage */ -+ if (ctrl->system_regulator) { -+ rc = regulator_set_voltage(ctrl->system_regulator, -+ aggr_corner->system_volt, INT_MAX); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n", -+ aggr_corner->system_volt, rc); -+ return rc; -+ } -+ } -+ } -+ -+ rc = cpr3_regulator_config_voltage_crossings(ctrl, new_volt, &last_volt, -+ aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "unable to handle voltage threshold crossing configurations, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ /* -+ * Subtract a small amount from the min_uV parameter so that the -+ * set voltage request is not dropped by the framework due to being -+ * duplicate. This is needed in order to switch from hardware -+ * closed-loop to open-loop successfully. -+ */ -+ rc = regulator_set_voltage(vdd, new_volt - (ctrl->cpr_enabled ? 0 : 1), -+ aggr_corner->ceiling_volt); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n", -+ new_volt, rc); -+ return rc; -+ } -+ -+ if (new_volt == last_volt && ctrl->supports_hw_closed_loop -+ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ /* -+ * CPR4 features enforce voltage reprogramming when the last -+ * set voltage and new set voltage are same. This way, we can -+ * ensure that SAW PMIC STATUS register is updated with newly -+ * programmed voltage. -+ */ -+ rc = regulator_sync_voltage(vdd); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_sync_voltage(vdd) == %d failed, rc=%d\n", -+ new_volt, rc); -+ return rc; -+ } -+ } -+ -+ if (new_volt >= last_volt) { -+ rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner); -+ if (rc) -+ return rc; -+ } else { -+ /* Decreasing VDD voltage */ -+ if (ctrl->system_regulator) { -+ rc = regulator_set_voltage(ctrl->system_regulator, -+ aggr_corner->system_volt, INT_MAX); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n", -+ aggr_corner->system_volt, rc); -+ return rc; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_get_dynamic_floor_volt() - returns the current dynamic floor -+ * voltage based upon static configurations and the state of all -+ * power domains during the last CPR measurement -+ * @ctrl: Pointer to the CPR3 controller -+ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register -+ * -+ * When using HW closed-loop, the dynamic floor voltage is always returned -+ * regardless of the current state of the power domains. -+ * -+ * Return: dynamic floor voltage in microvolts or 0 if dynamic floor is not -+ * currently required -+ */ -+static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl, -+ u32 reg_last_measurement) -+{ -+ int dynamic_floor_volt = 0; -+ struct cpr3_regulator *vreg; -+ bool valid, pd_valid; -+ u32 bypass_bits; -+ int i, j; -+ -+ if (!ctrl->supports_hw_closed_loop) -+ return 0; -+ -+ if (likely(!ctrl->use_hw_closed_loop)) { -+ valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID); -+ bypass_bits -+ = (reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK) -+ >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT; -+ } else { -+ /* -+ * Ensure that the dynamic floor voltage is always used for -+ * HW closed-loop since the conditions below cannot be evaluated -+ * after each CPR measurement. -+ */ -+ valid = false; -+ bypass_bits = 0; -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ -+ if (!vreg->uses_dynamic_floor) -+ continue; -+ -+ pd_valid = !((bypass_bits & vreg->pd_bypass_mask) -+ == vreg->pd_bypass_mask); -+ -+ if (!valid || !pd_valid) -+ dynamic_floor_volt = max(dynamic_floor_volt, -+ vreg->corner[ -+ vreg->dynamic_floor_corner].last_volt); -+ } -+ } -+ -+ return dynamic_floor_volt; -+} -+ -+/** -+ * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in -+ * microvolts that can result from different operating conditions -+ * for the specified sdelta struct -+ * @sdelta: Pointer to the sdelta structure -+ * @step_volt: Step size in microvolts between available set -+ * points of the VDD supply. -+ * -+ * Return: voltage difference between the highest and lowest adjustments if -+ * sdelta and sdelta->table are valid, else 0. -+ */ -+static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta, -+ int step_volt) -+{ -+ int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN; -+ -+ if (!sdelta || !sdelta->table) -+ return 0; -+ -+ for (i = 0; i < sdelta->max_core_count; i++) { -+ for (j = 0; j < sdelta->temp_band_count; j++) { -+ index = i * sdelta->temp_band_count + j; -+ sdelta_min = min(sdelta_min, sdelta->table[index]); -+ sdelta_max = max(sdelta_max, sdelta->table[index]); -+ } -+ } -+ -+ return (sdelta_max - sdelta_min) * step_volt; -+} -+ -+/** -+ * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current -+ * aggregated corner and current corner of a given regulator -+ * and adjust the sdelta strucuture data of aggregate corner. -+ * @aggr_corner: Pointer to accumulated aggregated corner which -+ * is both an input and an output -+ * @corner: Pointer to the corner to be aggregated with -+ * aggr_corner -+ * @step_volt: Step size in microvolts between available set -+ * points of the VDD supply. -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_aggregate_sdelta( -+ struct cpr3_corner *aggr_corner, -+ const struct cpr3_corner *corner, int step_volt) -+{ -+ struct cpr4_sdelta *aggr_sdelta, *sdelta; -+ int aggr_core_count, core_count, temp_band_count; -+ u32 aggr_index, index; -+ int i, j, sdelta_size, cap_steps, adjust_sdelta; -+ -+ aggr_sdelta = aggr_corner->sdelta; -+ sdelta = corner->sdelta; -+ -+ if (aggr_corner->open_loop_volt < corner->open_loop_volt) { -+ /* -+ * Found the new dominant regulator as its open-loop requirement -+ * is higher than previous dominant regulator. Calculate cap -+ * voltage to limit the SDELTA values to make sure the runtime -+ * (Core-count/temp) adjustments do not violate other -+ * regulators' voltage requirements. Use cpr4_sdelta values of -+ * new dominant regulator. -+ */ -+ aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt, -+ (corner->open_loop_volt - -+ aggr_corner->open_loop_volt)); -+ -+ /* Clear old data in the sdelta table */ -+ sdelta_size = aggr_sdelta->max_core_count -+ * aggr_sdelta->temp_band_count; -+ -+ if (aggr_sdelta->allow_core_count_adj -+ || aggr_sdelta->allow_temp_adj) -+ memset(aggr_sdelta->table, 0, sdelta_size -+ * sizeof(*aggr_sdelta->table)); -+ -+ if (sdelta->allow_temp_adj || sdelta->allow_core_count_adj) { -+ /* Copy new data in sdelta table */ -+ sdelta_size = sdelta->max_core_count -+ * sdelta->temp_band_count; -+ if (sdelta->table) -+ memcpy(aggr_sdelta->table, sdelta->table, -+ sdelta_size * sizeof(*sdelta->table)); -+ } -+ -+ if (sdelta->allow_boost) { -+ memcpy(aggr_sdelta->boost_table, sdelta->boost_table, -+ sdelta->temp_band_count -+ * sizeof(*sdelta->boost_table)); -+ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; -+ } else if (aggr_sdelta->allow_boost) { -+ for (i = 0; i < aggr_sdelta->temp_band_count; i++) { -+ adjust_sdelta = (corner->open_loop_volt -+ - aggr_corner->open_loop_volt) -+ / step_volt; -+ aggr_sdelta->boost_table[i] += adjust_sdelta; -+ aggr_sdelta->boost_table[i] -+ = min(aggr_sdelta->boost_table[i], 0); -+ } -+ } -+ -+ aggr_corner->open_loop_volt = corner->open_loop_volt; -+ aggr_sdelta->allow_temp_adj = sdelta->allow_temp_adj; -+ aggr_sdelta->allow_core_count_adj -+ = sdelta->allow_core_count_adj; -+ aggr_sdelta->max_core_count = sdelta->max_core_count; -+ aggr_sdelta->temp_band_count = sdelta->temp_band_count; -+ } else if (aggr_corner->open_loop_volt > corner->open_loop_volt) { -+ /* -+ * Adjust the cap voltage if the open-loop requirement of new -+ * regulator is the next highest. -+ */ -+ aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt, -+ (aggr_corner->open_loop_volt -+ - corner->open_loop_volt)); -+ -+ if (sdelta->allow_boost) { -+ for (i = 0; i < aggr_sdelta->temp_band_count; i++) { -+ adjust_sdelta = (aggr_corner->open_loop_volt -+ - corner->open_loop_volt) -+ / step_volt; -+ aggr_sdelta->boost_table[i] = -+ sdelta->boost_table[i] + adjust_sdelta; -+ aggr_sdelta->boost_table[i] -+ = min(aggr_sdelta->boost_table[i], 0); -+ } -+ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; -+ } -+ } else { -+ /* -+ * Found another dominant regulator with same open-loop -+ * requirement. Make cap voltage to '0'. Disable core-count -+ * adjustments as we couldn't support for both regulators. -+ * Keep enable temp based adjustments if enabled for both -+ * regulators and choose mininum margin adjustment values -+ * between them. -+ */ -+ aggr_sdelta->cap_volt = 0; -+ aggr_sdelta->allow_core_count_adj = false; -+ -+ if (aggr_sdelta->allow_temp_adj -+ && sdelta->allow_temp_adj) { -+ aggr_core_count = aggr_sdelta->max_core_count - 1; -+ core_count = sdelta->max_core_count - 1; -+ temp_band_count = sdelta->temp_band_count; -+ for (j = 0; j < temp_band_count; j++) { -+ aggr_index = aggr_core_count * temp_band_count -+ + j; -+ index = core_count * temp_band_count + j; -+ aggr_sdelta->table[aggr_index] = -+ min(aggr_sdelta->table[aggr_index], -+ sdelta->table[index]); -+ } -+ } else { -+ aggr_sdelta->allow_temp_adj = false; -+ } -+ -+ if (sdelta->allow_boost) { -+ memcpy(aggr_sdelta->boost_table, sdelta->boost_table, -+ sdelta->temp_band_count -+ * sizeof(*sdelta->boost_table)); -+ aggr_sdelta->boost_num_cores = sdelta->boost_num_cores; -+ } -+ } -+ -+ /* Keep non-dominant clients boost enable state */ -+ aggr_sdelta->allow_boost |= sdelta->allow_boost; -+ if (aggr_sdelta->allow_boost) -+ aggr_sdelta->allow_core_count_adj = false; -+ -+ if (aggr_sdelta->cap_volt && !(aggr_sdelta->cap_volt == INT_MAX)) { -+ core_count = aggr_sdelta->max_core_count; -+ temp_band_count = aggr_sdelta->temp_band_count; -+ /* -+ * Convert cap voltage from uV to PMIC steps and use to limit -+ * sdelta margin adjustments. -+ */ -+ cap_steps = aggr_sdelta->cap_volt / step_volt; -+ for (i = 0; i < core_count; i++) -+ for (j = 0; j < temp_band_count; j++) { -+ index = i * temp_band_count + j; -+ aggr_sdelta->table[index] = -+ min(aggr_sdelta->table[index], -+ cap_steps); -+ } -+ } -+} -+ -+/** -+ * cpr3_regulator_aggregate_corners() - aggregate two corners together -+ * @aggr_corner: Pointer to accumulated aggregated corner which -+ * is both an input and an output -+ * @corner: Pointer to the corner to be aggregated with -+ * aggr_corner -+ * @aggr_quot: Flag indicating that target quotients should be -+ * aggregated as well. -+ * @step_volt: Step size in microvolts between available set -+ * points of the VDD supply. -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner, -+ const struct cpr3_corner *corner, bool aggr_quot, -+ int step_volt) -+{ -+ int i; -+ -+ aggr_corner->ceiling_volt -+ = max(aggr_corner->ceiling_volt, corner->ceiling_volt); -+ aggr_corner->floor_volt -+ = max(aggr_corner->floor_volt, corner->floor_volt); -+ aggr_corner->last_volt -+ = max(aggr_corner->last_volt, corner->last_volt); -+ aggr_corner->system_volt -+ = max(aggr_corner->system_volt, corner->system_volt); -+ aggr_corner->mem_acc_volt -+ = max(aggr_corner->mem_acc_volt, corner->mem_acc_volt); -+ aggr_corner->irq_en |= corner->irq_en; -+ aggr_corner->use_open_loop |= corner->use_open_loop; -+ -+ if (aggr_quot) { -+ aggr_corner->ro_mask &= corner->ro_mask; -+ -+ for (i = 0; i < CPR3_RO_COUNT; i++) -+ aggr_corner->target_quot[i] -+ = max(aggr_corner->target_quot[i], -+ corner->target_quot[i]); -+ } -+ -+ if (aggr_corner->sdelta && corner->sdelta -+ && (aggr_corner->sdelta->table -+ || aggr_corner->sdelta->boost_table)) { -+ cpr3_regulator_aggregate_sdelta(aggr_corner, corner, step_volt); -+ } else { -+ aggr_corner->open_loop_volt -+ = max(aggr_corner->open_loop_volt, -+ corner->open_loop_volt); -+ } -+} -+ -+/** -+ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller -+ * to reflect the corners used by all CPR3 regulators as well as -+ * the CPR operating mode -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * This function aggregates the CPR parameters for all CPR3 regulators -+ * associated with the VDD supply. Upon success, it sets the aggregated last -+ * known good voltage. -+ * -+ * The VDD supply voltage will not be physically configured unless this -+ * condition is met by at least one of the regulators of the controller: -+ * regulator->vreg_enabled == true && -+ * regulator->current_corner != CPR3_REGULATOR_CORNER_INVALID -+ * -+ * CPR registers for the controller and each thread are updated as long as -+ * ctrl->cpr_enabled == true. -+ * -+ * Note, CPR3 controller lock must be held by the caller. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) -+{ -+ struct cpr3_corner aggr_corner = {}; -+ struct cpr3_thread *thread; -+ struct cpr3_regulator *vreg; -+ struct cpr4_sdelta *sdelta; -+ bool valid = false; -+ bool thread_valid; -+ int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt; -+ u32 reg_last_measurement = 0, sdelta_size; -+ int *sdelta_table, *boost_table; -+ -+ last_corner_volt = 0; -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ vdd_volt = regulator_get_voltage(ctrl->vdd_regulator); -+ if (vdd_volt < 0) { -+ cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n", -+ vdd_volt); -+ return vdd_volt; -+ } -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ /* -+ * Save aggregated corner open-loop voltage which was programmed -+ * during last corner switch which is used when programming new -+ * aggregated corner open-loop voltage. -+ */ -+ last_corner_volt = ctrl->aggr_corner.open_loop_volt; -+ } -+ -+ if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop && -+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) -+ reg_last_measurement -+ = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT); -+ -+ aggr_corner.sdelta = ctrl->aggr_corner.sdelta; -+ if (aggr_corner.sdelta) { -+ sdelta = aggr_corner.sdelta; -+ sdelta_table = sdelta->table; -+ if (sdelta_table) { -+ sdelta_size = sdelta->max_core_count * -+ sdelta->temp_band_count; -+ memset(sdelta_table, 0, sdelta_size -+ * sizeof(*sdelta_table)); -+ } -+ -+ boost_table = sdelta->boost_table; -+ if (boost_table) -+ memset(boost_table, 0, sdelta->temp_band_count -+ * sizeof(*boost_table)); -+ -+ memset(sdelta, 0, sizeof(*sdelta)); -+ sdelta->table = sdelta_table; -+ sdelta->cap_volt = INT_MAX; -+ sdelta->boost_table = boost_table; -+ } -+ -+ /* Aggregate the requests of all threads */ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ thread = &ctrl->thread[i]; -+ thread_valid = false; -+ -+ sdelta = thread->aggr_corner.sdelta; -+ if (sdelta) { -+ sdelta_table = sdelta->table; -+ if (sdelta_table) { -+ sdelta_size = sdelta->max_core_count * -+ sdelta->temp_band_count; -+ memset(sdelta_table, 0, sdelta_size -+ * sizeof(*sdelta_table)); -+ } -+ -+ boost_table = sdelta->boost_table; -+ if (boost_table) -+ memset(boost_table, 0, sdelta->temp_band_count -+ * sizeof(*boost_table)); -+ -+ memset(sdelta, 0, sizeof(*sdelta)); -+ sdelta->table = sdelta_table; -+ sdelta->cap_volt = INT_MAX; -+ sdelta->boost_table = boost_table; -+ } -+ -+ memset(&thread->aggr_corner, 0, sizeof(thread->aggr_corner)); -+ thread->aggr_corner.sdelta = sdelta; -+ thread->aggr_corner.ro_mask = CPR3_RO_MASK; -+ -+ for (j = 0; j < thread->vreg_count; j++) { -+ vreg = &thread->vreg[j]; -+ -+ if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop) -+ cpr3_update_vreg_closed_loop_volt(vreg, -+ vdd_volt, reg_last_measurement); -+ -+ if (!vreg->vreg_enabled -+ || vreg->current_corner -+ == CPR3_REGULATOR_CORNER_INVALID) { -+ /* Cannot participate in aggregation. */ -+ vreg->aggregated = false; -+ continue; -+ } else { -+ vreg->aggregated = true; -+ thread_valid = true; -+ } -+ -+ cpr3_regulator_aggregate_corners(&thread->aggr_corner, -+ &vreg->corner[vreg->current_corner], -+ true, ctrl->step_volt); -+ } -+ -+ valid |= thread_valid; -+ -+ if (thread_valid) -+ cpr3_regulator_aggregate_corners(&aggr_corner, -+ &thread->aggr_corner, -+ false, ctrl->step_volt); -+ } -+ -+ if (valid && ctrl->cpr_allowed_hw && ctrl->cpr_allowed_sw) { -+ rc = cpr3_closed_loop_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc); -+ return rc; -+ } -+ } else { -+ rc = cpr3_closed_loop_disable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc); -+ return rc; -+ } -+ } -+ -+ /* No threads are enabled with a valid corner so exit. */ -+ if (!valid) -+ return 0; -+ -+ /* -+ * When using CPR hardware closed-loop, the voltage may vary anywhere -+ * between the floor and ceiling voltage without software notification. -+ * Therefore, it is required that the floor to ceiling range for the -+ * aggregated corner not intersect the APM threshold voltage. Adjust -+ * the floor to ceiling range if this requirement is violated. -+ * -+ * The following algorithm is applied in the case that -+ * floor < threshold <= ceiling: -+ * if open_loop >= threshold - adj, then floor = threshold -+ * else ceiling = threshold - step -+ * where adj = an adjustment factor to ensure sufficient voltage margin -+ * and step = VDD output step size -+ * -+ * The open-loop and last known voltages are also bounded by the new -+ * floor or ceiling value as needed. -+ */ -+ if (ctrl->use_hw_closed_loop -+ && aggr_corner.ceiling_volt >= ctrl->apm_threshold_volt -+ && aggr_corner.floor_volt < ctrl->apm_threshold_volt) { -+ -+ if (aggr_corner.open_loop_volt -+ >= ctrl->apm_threshold_volt - ctrl->apm_adj_volt) -+ aggr_corner.floor_volt = ctrl->apm_threshold_volt; -+ else -+ aggr_corner.ceiling_volt -+ = ctrl->apm_threshold_volt - ctrl->step_volt; -+ -+ aggr_corner.last_volt -+ = max(aggr_corner.last_volt, aggr_corner.floor_volt); -+ aggr_corner.last_volt -+ = min(aggr_corner.last_volt, aggr_corner.ceiling_volt); -+ aggr_corner.open_loop_volt -+ = max(aggr_corner.open_loop_volt, aggr_corner.floor_volt); -+ aggr_corner.open_loop_volt -+ = min(aggr_corner.open_loop_volt, aggr_corner.ceiling_volt); -+ } -+ -+ if (ctrl->use_hw_closed_loop -+ && aggr_corner.ceiling_volt >= ctrl->mem_acc_threshold_volt -+ && aggr_corner.floor_volt < ctrl->mem_acc_threshold_volt) { -+ aggr_corner.floor_volt = ctrl->mem_acc_threshold_volt; -+ aggr_corner.last_volt = max(aggr_corner.last_volt, -+ aggr_corner.floor_volt); -+ aggr_corner.open_loop_volt = max(aggr_corner.open_loop_volt, -+ aggr_corner.floor_volt); -+ } -+ -+ if (ctrl->use_hw_closed_loop) { -+ dynamic_floor_volt -+ = cpr3_regulator_get_dynamic_floor_volt(ctrl, -+ reg_last_measurement); -+ if (aggr_corner.floor_volt < dynamic_floor_volt) { -+ aggr_corner.floor_volt = dynamic_floor_volt; -+ aggr_corner.last_volt = max(aggr_corner.last_volt, -+ aggr_corner.floor_volt); -+ aggr_corner.open_loop_volt -+ = max(aggr_corner.open_loop_volt, -+ aggr_corner.floor_volt); -+ aggr_corner.ceiling_volt = max(aggr_corner.ceiling_volt, -+ aggr_corner.floor_volt); -+ } -+ } -+ -+ if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) { -+ /* -+ * Always program open-loop voltage for CPR4 controllers which -+ * support hardware closed-loop. Storing the last closed loop -+ * voltage in corner structure can still help with debugging. -+ */ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) -+ new_volt = aggr_corner.last_volt; -+ else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 -+ && ctrl->supports_hw_closed_loop) -+ new_volt = aggr_corner.open_loop_volt; -+ else -+ new_volt = min(aggr_corner.last_volt + -+ cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta, -+ ctrl->step_volt), -+ aggr_corner.ceiling_volt); -+ -+ aggr_corner.last_volt = new_volt; -+ } else { -+ new_volt = aggr_corner.open_loop_volt; -+ aggr_corner.last_volt = aggr_corner.open_loop_volt; -+ } -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 -+ && ctrl->supports_hw_closed_loop) { -+ /* -+ * Store last aggregated corner open-loop voltage in vdd_volt -+ * which is used when programming current aggregated corner -+ * required voltage. -+ */ -+ vdd_volt = last_corner_volt; -+ } -+ -+ cpr3_debug(ctrl, "setting new voltage=%d uV\n", new_volt); -+ rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt, -+ vdd_volt, &aggr_corner); -+ if (rc) { -+ cpr3_err(ctrl, "vdd voltage scaling failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ /* Only update registers if CPR is enabled. */ -+ if (ctrl->cpr_enabled) { -+ if (ctrl->use_hw_closed_loop) { -+ /* Hardware closed-loop */ -+ -+ /* Set ceiling and floor limits in hardware */ -+ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, -+ aggr_corner.floor_volt, -+ aggr_corner.ceiling_volt); -+ if (rc) { -+ cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } else { -+ /* Software closed-loop */ -+ -+ /* -+ * Disable UP or DOWN interrupts when at ceiling or -+ * floor respectively. -+ */ -+ if (new_volt == aggr_corner.floor_volt) -+ aggr_corner.irq_en &= ~CPR3_IRQ_DOWN; -+ if (new_volt == aggr_corner.ceiling_volt) -+ aggr_corner.irq_en &= ~CPR3_IRQ_UP; -+ -+ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, -+ CPR3_IRQ_UP | CPR3_IRQ_DOWN); -+ cpr3_write(ctrl, CPR3_REG_IRQ_EN, aggr_corner.irq_en); -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ cpr3_regulator_set_target_quot(&ctrl->thread[i]); -+ -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ -+ if (vreg->vreg_enabled) -+ vreg->last_closed_loop_corner -+ = vreg->current_corner; -+ } -+ } -+ -+ if (ctrl->proc_clock_throttle) { -+ if (aggr_corner.ceiling_volt > aggr_corner.floor_volt -+ && (ctrl->use_hw_closed_loop -+ || new_volt < aggr_corner.ceiling_volt)) -+ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, -+ ctrl->proc_clock_throttle); -+ else -+ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, -+ CPR3_PD_THROTTLE_DISABLE); -+ } -+ -+ /* -+ * Ensure that all CPR register writes complete before -+ * re-enabling CPR loop operation. -+ */ -+ wmb(); -+ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 -+ && ctrl->vdd_limit_regulator) { -+ /* Set ceiling and floor limits in hardware */ -+ rc = regulator_set_voltage(ctrl->vdd_limit_regulator, -+ aggr_corner.floor_volt, -+ aggr_corner.ceiling_volt); -+ if (rc) { -+ cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ ctrl->aggr_corner = aggr_corner; -+ -+ if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj -+ || ctrl->allow_boost) { -+ rc = cpr3_controller_program_sdelta(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "failed to program sdelta, rc=%d\n", rc); -+ return rc; -+ } -+ } -+ -+ /* -+ * Only enable the CPR controller if it is possible to set more than -+ * one vdd-supply voltage. -+ */ -+ if (aggr_corner.ceiling_volt > aggr_corner.floor_volt && -+ !aggr_corner.use_open_loop) -+ cpr3_ctrl_loop_enable(ctrl); -+ -+ ctrl->last_corner_was_closed_loop = ctrl->cpr_enabled; -+ cpr3_debug(ctrl, "CPR configuration updated\n"); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_wait_for_idle() - wait for the CPR controller to no longer be -+ * busy -+ * @ctrl: Pointer to the CPR3 controller -+ * @max_wait_ns: Max wait time in nanoseconds -+ * -+ * Return: 0 on success or -ETIMEDOUT if the controller was still busy after -+ * the maximum delay time -+ */ -+static int cpr3_regulator_wait_for_idle(struct cpr3_controller *ctrl, -+ s64 max_wait_ns) -+{ -+ ktime_t start, end; -+ s64 time_ns; -+ u32 reg; -+ -+ /* -+ * Ensure that all previous CPR register writes have completed before -+ * checking the status register. -+ */ -+ mb(); -+ -+ start = ktime_get(); -+ do { -+ end = ktime_get(); -+ time_ns = ktime_to_ns(ktime_sub(end, start)); -+ if (time_ns > max_wait_ns) { -+ cpr3_err(ctrl, "CPR controller still busy after %lld us\n", -+ div_s64(time_ns, 1000)); -+ return -ETIMEDOUT; -+ } -+ usleep_range(50, 100); -+ reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); -+ } while (reg & CPR3_CPR_STATUS_BUSY_MASK); -+ -+ return 0; -+} -+ -+/** -+ * cmp_int() - int comparison function to be passed into the sort() function -+ * which leads to ascending sorting -+ * @a: First int value -+ * @b: Second int value -+ * -+ * Return: >0 if a > b, 0 if a == b, <0 if a < b -+ */ -+static int cmp_int(const void *a, const void *b) -+{ -+ return *(int *)a - *(int *)b; -+} -+ -+/** -+ * cpr3_regulator_measure_aging() - measure the quotient difference for the -+ * specified CPR aging sensor -+ * @ctrl: Pointer to the CPR3 controller -+ * @aging_sensor: Aging sensor to measure -+ * -+ * Note that vdd-supply must be configured to the aging reference voltage before -+ * calling this function. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl, -+ struct cpr3_aging_sensor_info *aging_sensor) -+{ -+ u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max; -+ u32 quot_min_scaled, quot_max_scaled; -+ u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore; -+ u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0; -+ int quot_delta, quot_delta_scaled, quot_delta_scaled_sum; -+ int *quot_delta_results; -+ int rc, rc2, i, aging_measurement_count, filtered_count; -+ bool is_aging_measurement; -+ -+ quot_delta_results = kcalloc(CPR3_AGING_MEASUREMENT_ITERATIONS, -+ sizeof(*quot_delta_results), GFP_KERNEL); -+ if (!quot_delta_results) -+ return -ENOMEM; -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc); -+ kfree(quot_delta_results); -+ return rc; -+ } -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ /* Enable up, down, and mid CPR interrupts */ -+ irq_restore = cpr3_read(ctrl, CPR3_REG_IRQ_EN); -+ cpr3_write(ctrl, CPR3_REG_IRQ_EN, -+ CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID); -+ -+ /* Ensure that the aging sensor is assigned to CPR thread 0 */ -+ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), 0); -+ -+ /* Switch from HW to SW closed-loop if necessary */ -+ if (ctrl->supports_hw_closed_loop) { -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, -+ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); -+ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, -+ CPR3_HW_CLOSED_LOOP_DISABLE); -+ } -+ } -+ -+ /* Configure the GCNT for RO0 and RO1 that are used for aging */ -+ gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0)); -+ gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1)); -+ gcnt_ref = cpr3_regulator_get_gcnt(ctrl); -+ gcnt = gcnt_ref * 3 / 2; -+ cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt); -+ cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt); -+ -+ /* Unmask all RO's */ -+ ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0)); -+ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0); -+ -+ /* -+ * Mask all sensors except for the one to measure and bypass all -+ * sensors in collapsible domains. -+ */ -+ for (i = 0; i <= ctrl->sensor_count / 32; i++) { -+ mask = GENMASK(min(31, ctrl->sensor_count - i * 32), 0); -+ if (aging_sensor->sensor_id / 32 >= i -+ && aging_sensor->sensor_id / 32 < (i + 1)) -+ mask &= ~BIT(aging_sensor->sensor_id % 32); -+ cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), mask); -+ cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), -+ aging_sensor->bypass_mask[i]); -+ } -+ -+ /* Set CPR loop delays to 0 us */ -+ if (ctrl->supports_hw_closed_loop -+ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ cont_dly_restore = cpr3_read(ctrl, CPR3_REG_CPR_TIMER_MID_CONT); -+ up_down_dly_restore = cpr3_read(ctrl, -+ CPR3_REG_CPR_TIMER_UP_DN_CONT); -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, 0); -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, 0); -+ } else { -+ cont_dly_restore = cpr3_read(ctrl, -+ CPR3_REG_CPR_TIMER_AUTO_CONT); -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, 0); -+ } -+ -+ /* Set count mode to all-at-once min with no repeat */ -+ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, -+ CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK, -+ CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN -+ << CPR3_CPR_CTL_COUNT_MODE_SHIFT); -+ -+ cpr3_ctrl_loop_enable(ctrl); -+ -+ rc = cpr3_regulator_wait_for_idle(ctrl, -+ CPR3_AGING_MEASUREMENT_TIMEOUT_NS); -+ if (rc) -+ goto cleanup; -+ -+ /* Set count mode to all-at-once aging */ -+ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, CPR3_CPR_CTL_COUNT_MODE_MASK, -+ CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE -+ << CPR3_CPR_CTL_COUNT_MODE_SHIFT); -+ -+ aging_measurement_count = 0; -+ for (i = 0; i < CPR3_AGING_MEASUREMENT_ITERATIONS; i++) { -+ /* Send CONT_NACK */ -+ cpr3_write(ctrl, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK); -+ -+ rc = cpr3_regulator_wait_for_idle(ctrl, -+ CPR3_AGING_MEASUREMENT_TIMEOUT_NS); -+ if (rc) -+ goto cleanup; -+ -+ /* Check for PAGE_IS_AGE flag in status register */ -+ reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); -+ is_aging_measurement -+ = reg & CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK; -+ -+ /* Read CPR measurement results */ -+ result = cpr3_read(ctrl, CPR3_REG_RESULT1(0)); -+ quot_min = (result & CPR3_RESULT1_QUOT_MIN_MASK) -+ >> CPR3_RESULT1_QUOT_MIN_SHIFT; -+ quot_max = (result & CPR3_RESULT1_QUOT_MAX_MASK) -+ >> CPR3_RESULT1_QUOT_MAX_SHIFT; -+ sel_min = (result & CPR3_RESULT1_RO_MIN_MASK) -+ >> CPR3_RESULT1_RO_MIN_SHIFT; -+ sel_max = (result & CPR3_RESULT1_RO_MAX_MASK) -+ >> CPR3_RESULT1_RO_MAX_SHIFT; -+ -+ /* -+ * Scale the quotients so that they are equivalent to the fused -+ * values. This accounts for the difference in measurement -+ * interval times. -+ */ -+ quot_min_scaled = quot_min * (gcnt_ref + 1) / (gcnt + 1); -+ quot_max_scaled = quot_max * (gcnt_ref + 1) / (gcnt + 1); -+ -+ if (sel_max == 1) { -+ quot_delta = quot_max - quot_min; -+ quot_delta_scaled = quot_max_scaled - quot_min_scaled; -+ } else { -+ quot_delta = quot_min - quot_max; -+ quot_delta_scaled = quot_min_scaled - quot_max_scaled; -+ } -+ -+ if (is_aging_measurement) -+ quot_delta_results[aging_measurement_count++] -+ = quot_delta_scaled; -+ -+ cpr3_debug(ctrl, "aging results: page_is_age=%u, sel_min=%u, sel_max=%u, quot_min=%u, quot_max=%u, quot_delta=%d, quot_min_scaled=%u, quot_max_scaled=%u, quot_delta_scaled=%d\n", -+ is_aging_measurement, sel_min, sel_max, quot_min, -+ quot_max, quot_delta, quot_min_scaled, quot_max_scaled, -+ quot_delta_scaled); -+ } -+ -+ filtered_count -+ = aging_measurement_count - CPR3_AGING_MEASUREMENT_FILTER * 2; -+ if (filtered_count > 0) { -+ sort(quot_delta_results, aging_measurement_count, -+ sizeof(*quot_delta_results), cmp_int, NULL); -+ -+ quot_delta_scaled_sum = 0; -+ for (i = 0; i < filtered_count; i++) -+ quot_delta_scaled_sum -+ += quot_delta_results[i -+ + CPR3_AGING_MEASUREMENT_FILTER]; -+ -+ aging_sensor->measured_quot_diff -+ = quot_delta_scaled_sum / filtered_count; -+ cpr3_info(ctrl, "average quotient delta=%d (count=%d)\n", -+ aging_sensor->measured_quot_diff, -+ filtered_count); -+ } else { -+ cpr3_err(ctrl, "%d aging measurements completed after %d iterations\n", -+ aging_measurement_count, -+ CPR3_AGING_MEASUREMENT_ITERATIONS); -+ rc = -EBUSY; -+ } -+ -+cleanup: -+ kfree(quot_delta_results); -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc2 = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc2) { -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc2); -+ rc = rc2; -+ } -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore); -+ -+ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore); -+ -+ cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore); -+ cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore); -+ -+ if (ctrl->supports_hw_closed_loop -+ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly_restore); -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, -+ up_down_dly_restore); -+ } else { -+ cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, -+ cont_dly_restore); -+ } -+ -+ for (i = 0; i <= ctrl->sensor_count / 32; i++) { -+ cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), 0); -+ cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), 0); -+ } -+ -+ cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, -+ CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK, -+ (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT) -+ | (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT)); -+ -+ cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), -+ ctrl->sensor_owner[aging_sensor->sensor_id]); -+ -+ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, -+ CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID); -+ -+ if (ctrl->supports_hw_closed_loop) { -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, -+ ctrl->use_hw_closed_loop -+ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE -+ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); -+ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, -+ ctrl->use_hw_closed_loop -+ ? CPR3_HW_CLOSED_LOOP_ENABLE -+ : CPR3_HW_CLOSED_LOOP_DISABLE); -+ } -+ } -+ -+ return rc; -+} -+ -+/** -+ * cpr3_regulator_readjust_volt_and_quot() - readjust the target quotients as -+ * well as the floor, ceiling, and open-loop voltages for the -+ * regulator by removing the old adjustment and adding the new one -+ * @vreg: Pointer to the CPR3 regulator -+ * @old_adjust_volt: Old aging adjustment voltage in microvolts -+ * @new_adjust_volt: New aging adjustment voltage in microvolts -+ * -+ * Also reset the cached closed loop voltage (last_volt) to equal the open-loop -+ * voltage for each corner. -+ * -+ * Return: None -+ */ -+static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg, -+ int old_adjust_volt, int new_adjust_volt) -+{ -+ unsigned long long temp; -+ int i, j, old_volt, new_volt, rounded_volt; -+ -+ if (!vreg->aging_allowed) -+ return; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ temp = (unsigned long long)old_adjust_volt -+ * (unsigned long long)vreg->corner[i].aging_derate; -+ do_div(temp, 1000); -+ old_volt = temp; -+ -+ temp = (unsigned long long)new_adjust_volt -+ * (unsigned long long)vreg->corner[i].aging_derate; -+ do_div(temp, 1000); -+ new_volt = temp; -+ -+ old_volt = min(vreg->aging_max_adjust_volt, old_volt); -+ new_volt = min(vreg->aging_max_adjust_volt, new_volt); -+ -+ for (j = 0; j < CPR3_RO_COUNT; j++) { -+ if (vreg->corner[i].target_quot[j] != 0) { -+ vreg->corner[i].target_quot[j] -+ += cpr3_quot_adjustment( -+ vreg->corner[i].ro_scale[j], -+ new_volt) -+ - cpr3_quot_adjustment( -+ vreg->corner[i].ro_scale[j], -+ old_volt); -+ } -+ } -+ -+ rounded_volt = CPR3_ROUND(new_volt, -+ vreg->thread->ctrl->step_volt); -+ -+ if (!vreg->aging_allow_open_loop_adj) -+ rounded_volt = 0; -+ -+ vreg->corner[i].ceiling_volt -+ = vreg->corner[i].unaged_ceiling_volt + rounded_volt; -+ vreg->corner[i].ceiling_volt = min(vreg->corner[i].ceiling_volt, -+ vreg->corner[i].abs_ceiling_volt); -+ vreg->corner[i].floor_volt -+ = vreg->corner[i].unaged_floor_volt + rounded_volt; -+ vreg->corner[i].floor_volt = min(vreg->corner[i].floor_volt, -+ vreg->corner[i].ceiling_volt); -+ vreg->corner[i].open_loop_volt -+ = vreg->corner[i].unaged_open_loop_volt + rounded_volt; -+ vreg->corner[i].open_loop_volt -+ = min(vreg->corner[i].open_loop_volt, -+ vreg->corner[i].ceiling_volt); -+ -+ vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; -+ -+ cpr3_debug(vreg, "corner %d: applying %d uV closed-loop and %d uV open-loop voltage margin adjustment\n", -+ i, new_volt, rounded_volt); -+ } -+} -+ -+/** -+ * cpr3_regulator_set_aging_ref_adjustment() - adjust target quotients for the -+ * regulators managed by this CPR controller to account for aging -+ * @ctrl: Pointer to the CPR3 controller -+ * @ref_adjust_volt: New aging reference adjustment voltage in microvolts to -+ * apply to all regulators managed by this CPR controller -+ * -+ * The existing aging adjustment as defined by ctrl->aging_ref_adjust_volt is -+ * first removed and then the adjustment is applied. Lastly, the value of -+ * ctrl->aging_ref_adjust_volt is updated to ref_adjust_volt. -+ */ -+static void cpr3_regulator_set_aging_ref_adjustment( -+ struct cpr3_controller *ctrl, int ref_adjust_volt) -+{ -+ struct cpr3_regulator *vreg; -+ int i, j; -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ cpr3_regulator_readjust_volt_and_quot(vreg, -+ ctrl->aging_ref_adjust_volt, ref_adjust_volt); -+ } -+ } -+ -+ ctrl->aging_ref_adjust_volt = ref_adjust_volt; -+} -+ -+/** -+ * cpr3_regulator_aging_adjust() - adjust the target quotients for regulators -+ * based on the output of CPR aging sensors -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl) -+{ -+ struct cpr3_regulator *vreg; -+ struct cpr3_corner restore_aging_corner; -+ struct cpr3_corner *corner; -+ int *restore_current_corner; -+ bool *restore_vreg_enabled; -+ int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt = 0; -+ u32 reg; -+ -+ if (!ctrl->aging_required || !ctrl->cpr_enabled -+ || ctrl->aggr_corner.ceiling_volt == 0 -+ || ctrl->aggr_corner.ceiling_volt > ctrl->aging_ref_volt) -+ return 0; -+ -+ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ vreg_count++; -+ -+ if (vreg->aging_allowed && vreg->vreg_enabled -+ && vreg->current_corner > vreg->aging_corner) -+ return 0; -+ } -+ } -+ -+ /* Verify that none of the aging sensors are currently masked. */ -+ for (i = 0; i < ctrl->aging_sensor_count; i++) { -+ id = ctrl->aging_sensor[i].sensor_id; -+ reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id)); -+ if (reg & BIT(id % 32)) -+ return 0; -+ } -+ -+ /* -+ * Verify that the aging possible register (if specified) has an -+ * acceptable value. -+ */ -+ if (ctrl->aging_possible_reg) { -+ reg = readl_relaxed(ctrl->aging_possible_reg); -+ reg &= ctrl->aging_possible_mask; -+ if (reg != ctrl->aging_possible_val) -+ return 0; -+ } -+ -+ restore_current_corner = kcalloc(vreg_count, -+ sizeof(*restore_current_corner), GFP_KERNEL); -+ restore_vreg_enabled = kcalloc(vreg_count, -+ sizeof(*restore_vreg_enabled), GFP_KERNEL); -+ if (!restore_current_corner || !restore_vreg_enabled) { -+ kfree(restore_current_corner); -+ kfree(restore_vreg_enabled); -+ return -ENOMEM; -+ } -+ -+ /* Force all regulators to the aging corner */ -+ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ -+ restore_current_corner[vreg_count] -+ = vreg->current_corner; -+ restore_vreg_enabled[vreg_count] -+ = vreg->vreg_enabled; -+ -+ vreg->current_corner = vreg->aging_corner; -+ vreg->vreg_enabled = true; -+ } -+ } -+ -+ /* Force one of the regulators to require the aging reference voltage */ -+ vreg = &ctrl->thread[0].vreg[0]; -+ corner = &vreg->corner[vreg->current_corner]; -+ restore_aging_corner = *corner; -+ corner->ceiling_volt = ctrl->aging_ref_volt; -+ corner->floor_volt = ctrl->aging_ref_volt; -+ corner->open_loop_volt = ctrl->aging_ref_volt; -+ corner->last_volt = ctrl->aging_ref_volt; -+ -+ /* Skip last_volt caching */ -+ ctrl->last_corner_was_closed_loop = false; -+ -+ /* Set the vdd supply voltage to the aging reference voltage */ -+ rc = _cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "unable to force vdd-supply to the aging reference voltage=%d uV, rc=%d\n", -+ ctrl->aging_ref_volt, rc); -+ goto cleanup; -+ } -+ -+ if (ctrl->aging_vdd_mode) { -+ rc = regulator_set_mode(ctrl->vdd_regulator, -+ ctrl->aging_vdd_mode); -+ if (rc) { -+ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n", -+ ctrl->aging_vdd_mode, rc); -+ goto cleanup; -+ } -+ } -+ -+ /* Perform aging measurement on all aging sensors */ -+ for (i = 0; i < ctrl->aging_sensor_count; i++) { -+ for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) { -+ rc = cpr3_regulator_measure_aging(ctrl, -+ &ctrl->aging_sensor[i]); -+ if (!rc) -+ break; -+ } -+ -+ if (!rc) { -+ aging_volt = -+ cpr3_voltage_adjustment( -+ ctrl->aging_sensor[i].ro_scale, -+ ctrl->aging_sensor[i].measured_quot_diff -+ - ctrl->aging_sensor[i].init_quot_diff); -+ max_aging_volt = max(max_aging_volt, aging_volt); -+ } else { -+ cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n", -+ j, rc); -+ ctrl->aging_failed = true; -+ ctrl->aging_required = false; -+ goto cleanup; -+ } -+ } -+ -+cleanup: -+ vreg = &ctrl->thread[0].vreg[0]; -+ vreg->corner[vreg->current_corner] = restore_aging_corner; -+ -+ for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ vreg->current_corner -+ = restore_current_corner[vreg_count]; -+ vreg->vreg_enabled = restore_vreg_enabled[vreg_count]; -+ } -+ } -+ -+ kfree(restore_current_corner); -+ kfree(restore_vreg_enabled); -+ -+ /* Adjust the CPR target quotients according to the aging measurement */ -+ if (!rc) { -+ cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt); -+ -+ cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n", -+ ctrl->aging_ref_adjust_volt); -+ ctrl->aging_succeeded = true; -+ ctrl->aging_required = false; -+ } -+ -+ if (ctrl->aging_complete_vdd_mode) { -+ rc = regulator_set_mode(ctrl->vdd_regulator, -+ ctrl->aging_complete_vdd_mode); -+ if (rc) -+ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n", -+ ctrl->aging_complete_vdd_mode, rc); -+ } -+ -+ /* Skip last_volt caching */ -+ ctrl->last_corner_was_closed_loop = false; -+ -+ /* -+ * Restore vdd-supply to the voltage before the aging measurement and -+ * restore the CPR3 controller hardware state. -+ */ -+ rc2 = _cpr3_regulator_update_ctrl_state(ctrl); -+ -+ /* Stop last_volt caching on for the next request */ -+ ctrl->last_corner_was_closed_loop = false; -+ -+ return rc ? rc : rc2; -+} -+ -+/** -+ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller -+ * to reflect the corners used by all CPR3 regulators as well as -+ * the CPR operating mode and perform aging adjustments if needed -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Note, CPR3 controller lock must be held by the caller. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ rc = _cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) -+ return rc; -+ -+ return cpr3_regulator_aging_adjust(ctrl); -+} -+ -+/** -+ * cpr3_regulator_set_voltage() - set the voltage corner for the CPR3 regulator -+ * associated with the regulator device -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * @corner: New voltage corner to set (offset by CPR3_CORNER_OFFSET) -+ * @corner_max: Maximum voltage corner allowed (offset by -+ * CPR3_CORNER_OFFSET) -+ * @selector: Pointer which is filled with the selector value for the -+ * corner -+ * -+ * This function is passed as a callback function into the regulator ops that -+ * are registered for each cpr3-regulator device. The VDD voltage will not be -+ * physically configured until both this function and cpr3_regulator_enable() -+ * are called. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_set_voltage(struct regulator_dev *rdev, -+ int corner, int corner_max, unsigned *selector) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ int rc = 0; -+ int last_corner; -+ -+ corner -= CPR3_CORNER_OFFSET; -+ corner_max -= CPR3_CORNER_OFFSET; -+ *selector = corner; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (!vreg->vreg_enabled) { -+ vreg->current_corner = corner; -+ cpr3_debug(vreg, "stored corner=%d\n", corner); -+ goto done; -+ } else if (vreg->current_corner == corner) { -+ goto done; -+ } -+ -+ last_corner = vreg->current_corner; -+ vreg->current_corner = corner; -+ -+ if (vreg->cpr4_regulator_data != NULL) -+ if (vreg->cpr4_regulator_data->mem_acc_funcs != NULL) -+ vreg->cpr4_regulator_data->mem_acc_funcs->set_mem_acc(rdev); -+ -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); -+ vreg->current_corner = last_corner; -+ } -+ -+ if (vreg->cpr4_regulator_data != NULL) -+ if (vreg->cpr4_regulator_data->mem_acc_funcs != NULL) -+ vreg->cpr4_regulator_data->mem_acc_funcs->clear_mem_acc(rdev); -+ -+ cpr3_debug(vreg, "set corner=%d\n", corner); -+done: -+ mutex_unlock(&ctrl->lock); -+ -+ return rc; -+} -+ -+/** -+ * cpr3_handle_temp_open_loop_adjustment() - voltage based cold temperature -+ * -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * @is_cold: Flag to denote enter/exit cold condition -+ * -+ * This function is adjusts voltage margin based on cold condition -+ * -+ * Return: 0 = success -+ */ -+ -+int cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, -+ bool is_cold) -+{ -+ int i ,j, k, rc; -+ struct cpr3_regulator *vreg; -+ -+ mutex_lock(&ctrl->lock); -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ for (k = 0; k < vreg->corner_count; k++) { -+ vreg->corner[k].open_loop_volt = is_cold ? -+ vreg->corner[k].cold_temp_open_loop_volt : -+ vreg->corner[k].normal_temp_open_loop_volt; -+ } -+ } -+ } -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ mutex_unlock(&ctrl->lock); -+ -+ return rc; -+} -+ -+/** -+ * cpr3_regulator_get_voltage() - get the voltage corner for the CPR3 regulator -+ * associated with the regulator device -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * -+ * This function is passed as a callback function into the regulator ops that -+ * are registered for each cpr3-regulator device. -+ * -+ * Return: voltage corner value offset by CPR3_CORNER_OFFSET -+ */ -+static int cpr3_regulator_get_voltage(struct regulator_dev *rdev) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ -+ if (vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID) -+ return CPR3_CORNER_OFFSET; -+ else -+ return vreg->current_corner + CPR3_CORNER_OFFSET; -+} -+ -+/** -+ * cpr3_regulator_list_voltage() - return the voltage corner mapped to the -+ * specified selector -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * @selector: Regulator selector -+ * -+ * This function is passed as a callback function into the regulator ops that -+ * are registered for each cpr3-regulator device. -+ * -+ * Return: voltage corner value offset by CPR3_CORNER_OFFSET -+ */ -+static int cpr3_regulator_list_voltage(struct regulator_dev *rdev, -+ unsigned selector) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ -+ if (selector < vreg->corner_count) -+ return selector + CPR3_CORNER_OFFSET; -+ else -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_is_enabled() - return the enable state of the CPR3 regulator -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * -+ * This function is passed as a callback function into the regulator ops that -+ * are registered for each cpr3-regulator device. -+ * -+ * Return: true if regulator is enabled, false if regulator is disabled -+ */ -+static int cpr3_regulator_is_enabled(struct regulator_dev *rdev) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ -+ return vreg->vreg_enabled; -+} -+ -+/** -+ * cpr3_regulator_enable() - enable the CPR3 regulator -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * -+ * This function is passed as a callback function into the regulator ops that -+ * are registered for each cpr3-regulator device. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_enable(struct regulator_dev *rdev) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ int rc = 0; -+ -+ if (vreg->vreg_enabled == true) -+ return 0; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (ctrl->system_regulator) { -+ rc = regulator_enable(ctrl->system_regulator); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_enable(system) failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ rc = regulator_enable(ctrl->vdd_regulator); -+ if (rc) { -+ cpr3_err(vreg, "regulator_enable(vdd) failed, rc=%d\n", rc); -+ goto done; -+ } -+ -+ vreg->vreg_enabled = true; -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); -+ regulator_disable(ctrl->vdd_regulator); -+ vreg->vreg_enabled = false; -+ goto done; -+ } -+ -+ cpr3_debug(vreg, "Enabled\n"); -+done: -+ mutex_unlock(&ctrl->lock); -+ -+ return rc; -+} -+ -+/** -+ * cpr3_regulator_disable() - disable the CPR3 regulator -+ * @rdev: Regulator device pointer for the cpr3-regulator -+ * -+ * This function is passed as a callback function into the regulator ops that -+ * are registered for each cpr3-regulator device. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_disable(struct regulator_dev *rdev) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ int rc, rc2; -+ -+ if (vreg->vreg_enabled == false) -+ return 0; -+ -+ mutex_lock(&ctrl->lock); -+ rc = regulator_disable(ctrl->vdd_regulator); -+ if (rc) { -+ cpr3_err(vreg, "regulator_disable(vdd) failed, rc=%d\n", rc); -+ goto done; -+ } -+ -+ vreg->vreg_enabled = false; -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc); -+ rc2 = regulator_enable(ctrl->vdd_regulator); -+ vreg->vreg_enabled = true; -+ goto done; -+ } -+ -+ if (ctrl->system_regulator) { -+ rc = regulator_disable(ctrl->system_regulator); -+ if (rc) { -+ cpr3_err(ctrl, "regulator_disable(system) failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ cpr3_debug(vreg, "Disabled\n"); -+done: -+ mutex_unlock(&ctrl->lock); -+ -+ return rc; -+} -+ -+static struct regulator_ops cpr3_regulator_ops = { -+ .enable = cpr3_regulator_enable, -+ .disable = cpr3_regulator_disable, -+ .is_enabled = cpr3_regulator_is_enabled, -+ .set_voltage = cpr3_regulator_set_voltage, -+ .get_voltage = cpr3_regulator_get_voltage, -+ .list_voltage = cpr3_regulator_list_voltage, -+}; -+ -+/** -+ * cpr3_print_result() - print CPR measurement results to the kernel log for -+ * debugging purposes -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: None -+ */ -+static void cpr3_print_result(struct cpr3_thread *thread) -+{ -+ struct cpr3_controller *ctrl = thread->ctrl; -+ u32 result[3], busy, step_dn, step_up, error_steps, error, negative; -+ u32 quot_min, quot_max, ro_min, ro_max, step_quot_min, step_quot_max; -+ u32 sensor_min, sensor_max; -+ char *sign; -+ -+ result[0] = cpr3_read(ctrl, CPR3_REG_RESULT0(thread->thread_id)); -+ result[1] = cpr3_read(ctrl, CPR3_REG_RESULT1(thread->thread_id)); -+ result[2] = cpr3_read(ctrl, CPR3_REG_RESULT2(thread->thread_id)); -+ -+ busy = !!(result[0] & CPR3_RESULT0_BUSY_MASK); -+ step_dn = !!(result[0] & CPR3_RESULT0_STEP_DN_MASK); -+ step_up = !!(result[0] & CPR3_RESULT0_STEP_UP_MASK); -+ error_steps = (result[0] & CPR3_RESULT0_ERROR_STEPS_MASK) -+ >> CPR3_RESULT0_ERROR_STEPS_SHIFT; -+ error = (result[0] & CPR3_RESULT0_ERROR_MASK) -+ >> CPR3_RESULT0_ERROR_SHIFT; -+ negative = !!(result[0] & CPR3_RESULT0_NEGATIVE_MASK); -+ -+ quot_min = (result[1] & CPR3_RESULT1_QUOT_MIN_MASK) -+ >> CPR3_RESULT1_QUOT_MIN_SHIFT; -+ quot_max = (result[1] & CPR3_RESULT1_QUOT_MAX_MASK) -+ >> CPR3_RESULT1_QUOT_MAX_SHIFT; -+ ro_min = (result[1] & CPR3_RESULT1_RO_MIN_MASK) -+ >> CPR3_RESULT1_RO_MIN_SHIFT; -+ ro_max = (result[1] & CPR3_RESULT1_RO_MAX_MASK) -+ >> CPR3_RESULT1_RO_MAX_SHIFT; -+ -+ step_quot_min = (result[2] & CPR3_RESULT2_STEP_QUOT_MIN_MASK) -+ >> CPR3_RESULT2_STEP_QUOT_MIN_SHIFT; -+ step_quot_max = (result[2] & CPR3_RESULT2_STEP_QUOT_MAX_MASK) -+ >> CPR3_RESULT2_STEP_QUOT_MAX_SHIFT; -+ sensor_min = (result[2] & CPR3_RESULT2_SENSOR_MIN_MASK) -+ >> CPR3_RESULT2_SENSOR_MIN_SHIFT; -+ sensor_max = (result[2] & CPR3_RESULT2_SENSOR_MAX_MASK) -+ >> CPR3_RESULT2_SENSOR_MAX_SHIFT; -+ -+ sign = negative ? "-" : ""; -+ cpr3_debug(ctrl, "thread %u: busy=%u, step_dn=%u, step_up=%u, error_steps=%s%u, error=%s%u\n", -+ thread->thread_id, busy, step_dn, step_up, sign, error_steps, -+ sign, error); -+ cpr3_debug(ctrl, "thread %u: quot_min=%u, quot_max=%u, ro_min=%u, ro_max=%u\n", -+ thread->thread_id, quot_min, quot_max, ro_min, ro_max); -+ cpr3_debug(ctrl, "thread %u: step_quot_min=%u, step_quot_max=%u, sensor_min=%u, sensor_max=%u\n", -+ thread->thread_id, step_quot_min, step_quot_max, sensor_min, -+ sensor_max); -+} -+ -+/** -+ * cpr3_thread_busy() - returns if the specified CPR3 thread is busy taking -+ * a measurement -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: CPR3 busy status -+ */ -+static bool cpr3_thread_busy(struct cpr3_thread *thread) -+{ -+ u32 result; -+ -+ result = cpr3_read(thread->ctrl, CPR3_REG_RESULT0(thread->thread_id)); -+ -+ return !!(result & CPR3_RESULT0_BUSY_MASK); -+} -+ -+/** -+ * cpr3_irq_handler() - CPR interrupt handler callback function used for -+ * software closed-loop operation -+ * @irq: CPR interrupt number -+ * @data: Private data corresponding to the CPR3 controller -+ * pointer -+ * -+ * This function increases or decreases the vdd supply voltage based upon the -+ * CPR controller recommendation. -+ * -+ * Return: IRQ_HANDLED -+ */ -+static irqreturn_t cpr3_irq_handler(int irq, void *data) -+{ -+ struct cpr3_controller *ctrl = data; -+ struct cpr3_corner *aggr = &ctrl->aggr_corner; -+ u32 cont = CPR3_CONT_CMD_NACK; -+ u32 reg_last_measurement = 0; -+ struct cpr3_regulator *vreg; -+ struct cpr3_corner *corner; -+ unsigned long flags; -+ int i, j, new_volt, last_volt, dynamic_floor_volt, rc; -+ u32 irq_en, status, cpr_status, ctl; -+ bool up, down; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (!ctrl->cpr_enabled) { -+ cpr3_debug(ctrl, "CPR interrupt received but CPR is disabled\n"); -+ mutex_unlock(&ctrl->lock); -+ return IRQ_HANDLED; -+ } else if (ctrl->use_hw_closed_loop) { -+ cpr3_debug(ctrl, "CPR interrupt received but CPR is using HW closed-loop\n"); -+ goto done; -+ } -+ -+ /* -+ * CPR IRQ status checking and CPR controller disabling must happen -+ * atomically and without invening delay in order to avoid an interrupt -+ * storm caused by the handler racing with the CPR controller. -+ */ -+ local_irq_save(flags); -+ preempt_disable(); -+ -+ status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS); -+ up = status & CPR3_IRQ_UP; -+ down = status & CPR3_IRQ_DOWN; -+ -+ if (!up && !down) { -+ /* -+ * Toggle the CPR controller off and then back on since the -+ * hardware and software states are out of sync. This condition -+ * occurs after an aging measurement completes as the CPR IRQ -+ * physically triggers during the aging measurement but the -+ * handler is stuck waiting on the mutex lock. -+ */ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ local_irq_restore(flags); -+ preempt_enable(); -+ -+ /* Wait for the loop disable write to complete */ -+ mb(); -+ -+ /* Wait for BUSY=1 and LOOP_EN=0 in CPR controller registers. */ -+ for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) { -+ cpr_status = cpr3_read(ctrl, CPR3_REG_CPR_STATUS); -+ ctl = cpr3_read(ctrl, CPR3_REG_CPR_CTL); -+ if (cpr_status & CPR3_CPR_STATUS_BUSY_MASK -+ && (ctl & CPR3_CPR_CTL_LOOP_EN_MASK) -+ == CPR3_CPR_CTL_LOOP_DISABLE) -+ break; -+ udelay(10); -+ } -+ if (i == CPR3_REGISTER_WRITE_DELAY_US / 10) -+ cpr3_debug(ctrl, "CPR controller not disabled after %d us\n", -+ CPR3_REGISTER_WRITE_DELAY_US); -+ -+ /* Clear interrupt status */ -+ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, -+ CPR3_IRQ_UP | CPR3_IRQ_DOWN); -+ -+ /* Wait for the interrupt clearing write to complete */ -+ mb(); -+ -+ /* Wait for IRQ_STATUS register to be cleared. */ -+ for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) { -+ status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS); -+ if (!(status & (CPR3_IRQ_UP | CPR3_IRQ_DOWN))) -+ break; -+ udelay(10); -+ } -+ if (i == CPR3_REGISTER_WRITE_DELAY_US / 10) -+ cpr3_debug(ctrl, "CPR interrupts not cleared after %d us\n", -+ CPR3_REGISTER_WRITE_DELAY_US); -+ -+ cpr3_ctrl_loop_enable(ctrl); -+ -+ cpr3_debug(ctrl, "CPR interrupt received but no up or down status bit is set\n"); -+ -+ mutex_unlock(&ctrl->lock); -+ return IRQ_HANDLED; -+ } else if (up && down) { -+ cpr3_debug(ctrl, "both up and down status bits set\n"); -+ /* The up flag takes precedence over the down flag. */ -+ down = false; -+ } -+ -+ if (ctrl->supports_hw_closed_loop) -+ reg_last_measurement -+ = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT); -+ dynamic_floor_volt = cpr3_regulator_get_dynamic_floor_volt(ctrl, -+ reg_last_measurement); -+ -+ local_irq_restore(flags); -+ preempt_enable(); -+ -+ irq_en = aggr->irq_en; -+ last_volt = aggr->last_volt; -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ if (cpr3_thread_busy(&ctrl->thread[i])) { -+ cpr3_debug(ctrl, "CPR thread %u busy when it should be waiting for SW cont\n", -+ ctrl->thread[i].thread_id); -+ goto done; -+ } -+ } -+ -+ new_volt = up ? last_volt + ctrl->step_volt -+ : last_volt - ctrl->step_volt; -+ -+ /* Re-enable UP/DOWN interrupt when its opposite is received. */ -+ irq_en |= up ? CPR3_IRQ_DOWN : CPR3_IRQ_UP; -+ -+ if (new_volt > aggr->ceiling_volt) { -+ new_volt = aggr->ceiling_volt; -+ irq_en &= ~CPR3_IRQ_UP; -+ cpr3_debug(ctrl, "limiting to ceiling=%d uV\n", -+ aggr->ceiling_volt); -+ } else if (new_volt < aggr->floor_volt) { -+ new_volt = aggr->floor_volt; -+ irq_en &= ~CPR3_IRQ_DOWN; -+ cpr3_debug(ctrl, "limiting to floor=%d uV\n", aggr->floor_volt); -+ } -+ -+ if (down && new_volt < dynamic_floor_volt) { -+ /* -+ * The vdd-supply voltage should not be decreased below the -+ * dynamic floor voltage. However, it is not necessary (and -+ * counter productive) to force the voltage up to this level -+ * if it happened to be below it since the closed-loop voltage -+ * must have gotten there in a safe manner while the power -+ * domains for the CPR3 regulator imposing the dynamic floor -+ * were not bypassed. -+ */ -+ new_volt = last_volt; -+ irq_en &= ~CPR3_IRQ_DOWN; -+ cpr3_debug(ctrl, "limiting to dynamic floor=%d uV\n", -+ dynamic_floor_volt); -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) -+ cpr3_print_result(&ctrl->thread[i]); -+ -+ cpr3_debug(ctrl, "%s: new_volt=%d uV, last_volt=%d uV\n", -+ up ? "UP" : "DN", new_volt, last_volt); -+ -+ if (ctrl->proc_clock_throttle && last_volt == aggr->ceiling_volt -+ && new_volt < last_volt) -+ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, -+ ctrl->proc_clock_throttle); -+ -+ if (new_volt != last_volt) { -+ rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt, -+ last_volt, -+ aggr); -+ if (rc) { -+ cpr3_err(ctrl, "scale_vdd() failed to set vdd=%d uV, rc=%d\n", -+ new_volt, rc); -+ goto done; -+ } -+ cont = CPR3_CONT_CMD_ACK; -+ -+ /* -+ * Update the closed-loop voltage for all regulators managed -+ * by this CPR controller. -+ */ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ cpr3_update_vreg_closed_loop_volt(vreg, -+ new_volt, reg_last_measurement); -+ } -+ } -+ } -+ -+ if (ctrl->proc_clock_throttle && new_volt == aggr->ceiling_volt) -+ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, -+ CPR3_PD_THROTTLE_DISABLE); -+ -+ corner = &ctrl->thread[0].vreg[0].corner[ -+ ctrl->thread[0].vreg[0].current_corner]; -+ -+ if (irq_en != aggr->irq_en) { -+ aggr->irq_en = irq_en; -+ cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_en); -+ } -+ -+ aggr->last_volt = new_volt; -+ -+done: -+ /* Clear interrupt status */ -+ cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_UP | CPR3_IRQ_DOWN); -+ -+ /* ACK or NACK the CPR controller */ -+ cpr3_write(ctrl, CPR3_REG_CONT_CMD, cont); -+ -+ mutex_unlock(&ctrl->lock); -+ return IRQ_HANDLED; -+} -+ -+/** -+ * cpr3_ceiling_irq_handler() - CPR ceiling reached interrupt handler callback -+ * function used for hardware closed-loop operation -+ * @irq: CPR ceiling interrupt number -+ * @data: Private data corresponding to the CPR3 controller -+ * pointer -+ * -+ * This function disables processor clock throttling and closed-loop operation -+ * when the ceiling voltage is reached. -+ * -+ * Return: IRQ_HANDLED -+ */ -+static irqreturn_t cpr3_ceiling_irq_handler(int irq, void *data) -+{ -+ struct cpr3_controller *ctrl = data; -+ int volt; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (!ctrl->cpr_enabled) { -+ cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is disabled\n"); -+ goto done; -+ } else if (!ctrl->use_hw_closed_loop) { -+ cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is using SW closed-loop\n"); -+ goto done; -+ } -+ -+ volt = regulator_get_voltage(ctrl->vdd_regulator); -+ if (volt < 0) { -+ cpr3_err(ctrl, "could not get vdd voltage, rc=%d\n", volt); -+ goto done; -+ } else if (volt != ctrl->aggr_corner.ceiling_volt) { -+ cpr3_debug(ctrl, "CPR ceiling interrupt received but vdd voltage: %d uV != ceiling voltage: %d uV\n", -+ volt, ctrl->aggr_corner.ceiling_volt); -+ goto done; -+ } -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ /* -+ * Since the ceiling voltage has been reached, disable processor -+ * clock throttling as well as CPR closed-loop operation. -+ */ -+ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, -+ CPR3_PD_THROTTLE_DISABLE); -+ cpr3_ctrl_loop_disable(ctrl); -+ cpr3_debug(ctrl, "CPR closed-loop and throttling disabled\n"); -+ } -+ -+done: -+ mutex_unlock(&ctrl->lock); -+ return IRQ_HANDLED; -+} -+ -+/** -+ * cpr3_regulator_vreg_register() - register a regulator device for a CPR3 -+ * regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function initializes all regulator framework related structures and then -+ * calls regulator_register() for the CPR3 regulator. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_vreg_register(struct cpr3_regulator *vreg) -+{ -+ struct regulator_config config = {}; -+ struct regulator_desc *rdesc; -+ struct regulator_init_data *init_data; -+ int rc; -+ -+ init_data = of_get_regulator_init_data(vreg->thread->ctrl->dev, -+ vreg->of_node, &vreg->rdesc); -+ if (!init_data) { -+ cpr3_err(vreg, "regulator init data is missing\n"); -+ return -EINVAL; -+ } -+ -+ init_data->constraints.input_uV = init_data->constraints.max_uV; -+ rdesc = &vreg->rdesc; -+ init_data->constraints.valid_ops_mask |= -+ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS; -+ rdesc->ops = &cpr3_regulator_ops; -+ -+ rdesc->n_voltages = vreg->corner_count; -+ rdesc->name = init_data->constraints.name; -+ rdesc->owner = THIS_MODULE; -+ rdesc->type = REGULATOR_VOLTAGE; -+ -+ config.dev = vreg->thread->ctrl->dev; -+ config.driver_data = vreg; -+ config.init_data = init_data; -+ config.of_node = vreg->of_node; -+ -+ vreg->rdev = regulator_register(vreg->thread->ctrl->dev, rdesc, &config); -+ if (IS_ERR(vreg->rdev)) { -+ rc = PTR_ERR(vreg->rdev); -+ cpr3_err(vreg, "regulator_register failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+static int debugfs_int_set(void *data, u64 val) -+{ -+ *(int *)data = val; -+ return 0; -+} -+ -+static int debugfs_int_get(void *data, u64 *val) -+{ -+ *val = *(int *)data; -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(fops_int, debugfs_int_get, debugfs_int_set, "%lld\n"); -+DEFINE_SIMPLE_ATTRIBUTE(fops_int_ro, debugfs_int_get, NULL, "%lld\n"); -+DEFINE_SIMPLE_ATTRIBUTE(fops_int_wo, NULL, debugfs_int_set, "%lld\n"); -+ -+/** -+ * debugfs_create_int - create a debugfs file that is used to read and write a -+ * signed int value -+ * @name: Pointer to a string containing the name of the file to -+ * create -+ * @mode: The permissions that the file should have -+ * @parent: Pointer to the parent dentry for this file. This should -+ * be a directory dentry if set. If this parameter is -+ * %NULL, then the file will be created in the root of the -+ * debugfs filesystem. -+ * @value: Pointer to the variable that the file should read to and -+ * write from -+ * -+ * This function creates a file in debugfs with the given name that -+ * contains the value of the variable @value. If the @mode variable is so -+ * set, it can be read from, and written to. -+ * -+ * This function will return a pointer to a dentry if it succeeds. This -+ * pointer must be passed to the debugfs_remove() function when the file is -+ * to be removed. If an error occurs, %NULL will be returned. -+ */ -+static struct dentry *debugfs_create_int(const char *name, umode_t mode, -+ struct dentry *parent, int *value) -+{ -+ /* if there are no write bits set, make read only */ -+ if (!(mode & S_IWUGO)) -+ return debugfs_create_file(name, mode, parent, value, -+ &fops_int_ro); -+ /* if there are no read bits set, make write only */ -+ if (!(mode & S_IRUGO)) -+ return debugfs_create_file(name, mode, parent, value, -+ &fops_int_wo); -+ -+ return debugfs_create_file(name, mode, parent, value, &fops_int); -+} -+ -+static int debugfs_bool_get(void *data, u64 *val) -+{ -+ *val = *(bool *)data; -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%lld\n"); -+ -+/** -+ * struct cpr3_debug_corner_info - data structure used by the -+ * cpr3_debugfs_create_corner_int function -+ * @vreg: Pointer to the CPR3 regulator -+ * @index: Pointer to the corner array index -+ * @member_offset: Offset in bytes from the beginning of struct cpr3_corner -+ * to the beginning of the value to be read from -+ * @corner: Pointer to the CPR3 corner array -+ */ -+struct cpr3_debug_corner_info { -+ struct cpr3_regulator *vreg; -+ int *index; -+ size_t member_offset; -+ struct cpr3_corner *corner; -+}; -+ -+static int cpr3_debug_corner_int_get(void *data, u64 *val) -+{ -+ struct cpr3_debug_corner_info *info = data; -+ struct cpr3_controller *ctrl = info->vreg->thread->ctrl; -+ int i; -+ -+ mutex_lock(&ctrl->lock); -+ -+ i = *info->index; -+ if (i < 0) -+ i = 0; -+ -+ *val = *(int *)((char *)&info->vreg->corner[i] + info->member_offset); -+ -+ mutex_unlock(&ctrl->lock); -+ -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_int_fops, cpr3_debug_corner_int_get, -+ NULL, "%lld\n"); -+ -+/** -+ * cpr3_debugfs_create_corner_int - create a debugfs file that is used to read -+ * a signed int value out of a CPR3 regulator's corner array -+ * @vreg: Pointer to the CPR3 regulator -+ * @name: Pointer to a string containing the name of the file to -+ * create -+ * @mode: The permissions that the file should have -+ * @parent: Pointer to the parent dentry for this file. This should -+ * be a directory dentry if set. If this parameter is -+ * %NULL, then the file will be created in the root of the -+ * debugfs filesystem. -+ * @index: Pointer to the corner array index -+ * @member_offset: Offset in bytes from the beginning of struct cpr3_corner -+ * to the beginning of the value to be read from -+ * -+ * This function creates a file in debugfs with the given name that -+ * contains the value of the int type variable vreg->corner[index].member -+ * where member_offset == offsetof(struct cpr3_corner, member). -+ */ -+static struct dentry *cpr3_debugfs_create_corner_int( -+ struct cpr3_regulator *vreg, const char *name, umode_t mode, -+ struct dentry *parent, int *index, size_t member_offset) -+{ -+ struct cpr3_debug_corner_info *info; -+ -+ info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ -+ info->vreg = vreg; -+ info->index = index; -+ info->member_offset = member_offset; -+ -+ return debugfs_create_file(name, mode, parent, info, -+ &cpr3_debug_corner_int_fops); -+} -+ -+static int cpr3_debug_quot_open(struct inode *inode, struct file *file) -+{ -+ struct cpr3_debug_corner_info *info = inode->i_private; -+ struct cpr3_thread *thread = info->vreg->thread; -+ int size, i, pos; -+ u32 *quot; -+ char *buf; -+ -+ /* -+ * Max size: -+ * - 10 digits + ' ' or '\n' = 11 bytes per number -+ * - terminating '\0' -+ */ -+ size = CPR3_RO_COUNT * 11; -+ buf = kzalloc(size + 1, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ file->private_data = buf; -+ -+ mutex_lock(&thread->ctrl->lock); -+ -+ quot = info->corner[*info->index].target_quot; -+ -+ for (i = 0, pos = 0; i < CPR3_RO_COUNT; i++) -+ pos += scnprintf(buf + pos, size - pos, "%u%c", -+ quot[i], i < CPR3_RO_COUNT - 1 ? ' ' : '\n'); -+ -+ mutex_unlock(&thread->ctrl->lock); -+ -+ return nonseekable_open(inode, file); -+} -+ -+static ssize_t cpr3_debug_quot_read(struct file *file, char __user *buf, -+ size_t len, loff_t *ppos) -+{ -+ return simple_read_from_buffer(buf, len, ppos, file->private_data, -+ strlen(file->private_data)); -+} -+ -+static int cpr3_debug_quot_release(struct inode *inode, struct file *file) -+{ -+ kfree(file->private_data); -+ -+ return 0; -+} -+ -+static const struct file_operations cpr3_debug_quot_fops = { -+ .owner = THIS_MODULE, -+ .open = cpr3_debug_quot_open, -+ .release = cpr3_debug_quot_release, -+ .read = cpr3_debug_quot_read, -+}; -+ -+/** -+ * cpr3_regulator_debugfs_corner_add() - add debugfs files to expose -+ * configuration data for the CPR corner -+ * @vreg: Pointer to the CPR3 regulator -+ * @corner_dir: Pointer to the parent corner dentry for the new files -+ * @index: Pointer to the corner array index -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_debugfs_corner_add(struct cpr3_regulator *vreg, -+ struct dentry *corner_dir, int *index) -+{ -+ struct cpr3_debug_corner_info *info; -+ struct dentry *temp; -+ -+ temp = cpr3_debugfs_create_corner_int(vreg, "floor_volt", S_IRUGO, -+ corner_dir, index, offsetof(struct cpr3_corner, floor_volt)); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "floor_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = cpr3_debugfs_create_corner_int(vreg, "ceiling_volt", S_IRUGO, -+ corner_dir, index, offsetof(struct cpr3_corner, ceiling_volt)); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "ceiling_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = cpr3_debugfs_create_corner_int(vreg, "open_loop_volt", S_IRUGO, -+ corner_dir, index, -+ offsetof(struct cpr3_corner, open_loop_volt)); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "open_loop_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = cpr3_debugfs_create_corner_int(vreg, "last_volt", S_IRUGO, -+ corner_dir, index, offsetof(struct cpr3_corner, last_volt)); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "last_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return; -+ -+ info->vreg = vreg; -+ info->index = index; -+ info->corner = vreg->corner; -+ -+ temp = debugfs_create_file("target_quots", S_IRUGO, corner_dir, -+ info, &cpr3_debug_quot_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "target_quots debugfs file creation failed\n"); -+ return; -+ } -+} -+ -+/** -+ * cpr3_debug_corner_index_set() - debugfs callback used to change the -+ * value of the CPR3 regulator debug_corner index -+ * @data: Pointer to private data which is equal to the CPR3 -+ * regulator pointer -+ * @val: New value for debug_corner -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_corner_index_set(void *data, u64 val) -+{ -+ struct cpr3_regulator *vreg = data; -+ -+ if (val < CPR3_CORNER_OFFSET || val > vreg->corner_count) { -+ cpr3_err(vreg, "invalid corner index %llu; allowed values: %d-%d\n", -+ val, CPR3_CORNER_OFFSET, vreg->corner_count); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&vreg->thread->ctrl->lock); -+ vreg->debug_corner = val - CPR3_CORNER_OFFSET; -+ mutex_unlock(&vreg->thread->ctrl->lock); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_debug_corner_index_get() - debugfs callback used to retrieve -+ * the value of the CPR3 regulator debug_corner index -+ * @data: Pointer to private data which is equal to the CPR3 -+ * regulator pointer -+ * @val: Output parameter written with the value of -+ * debug_corner -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_corner_index_get(void *data, u64 *val) -+{ -+ struct cpr3_regulator *vreg = data; -+ -+ *val = vreg->debug_corner + CPR3_CORNER_OFFSET; -+ -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_index_fops, -+ cpr3_debug_corner_index_get, -+ cpr3_debug_corner_index_set, -+ "%llu\n"); -+ -+/** -+ * cpr3_debug_current_corner_index_get() - debugfs callback used to retrieve -+ * the value of the CPR3 regulator current_corner index -+ * @data: Pointer to private data which is equal to the CPR3 -+ * regulator pointer -+ * @val: Output parameter written with the value of -+ * current_corner -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_current_corner_index_get(void *data, u64 *val) -+{ -+ struct cpr3_regulator *vreg = data; -+ -+ *val = vreg->current_corner + CPR3_CORNER_OFFSET; -+ -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_current_corner_index_fops, -+ cpr3_debug_current_corner_index_get, -+ NULL, "%llu\n"); -+ -+/** -+ * cpr3_regulator_debugfs_vreg_add() - add debugfs files to expose configuration -+ * data for the CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * @thread_dir CPR3 thread debugfs directory handle -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_debugfs_vreg_add(struct cpr3_regulator *vreg, -+ struct dentry *thread_dir) -+{ -+ struct dentry *temp, *corner_dir, *vreg_dir; -+ -+ vreg_dir = debugfs_create_dir(vreg->name, thread_dir); -+ if (IS_ERR_OR_NULL(vreg_dir)) { -+ cpr3_err(vreg, "%s debugfs directory creation failed\n", -+ vreg->name); -+ return; -+ } -+ -+ temp = debugfs_create_int("speed_bin_fuse", S_IRUGO, vreg_dir, -+ &vreg->speed_bin_fuse); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "speed_bin_fuse debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("cpr_rev_fuse", S_IRUGO, vreg_dir, -+ &vreg->cpr_rev_fuse); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "cpr_rev_fuse debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("fuse_combo", S_IRUGO, vreg_dir, -+ &vreg->fuse_combo); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "fuse_combo debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("corner_count", S_IRUGO, vreg_dir, -+ &vreg->corner_count); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "corner_count debugfs file creation failed\n"); -+ return; -+ } -+ -+ corner_dir = debugfs_create_dir("corner", vreg_dir); -+ if (IS_ERR_OR_NULL(corner_dir)) { -+ cpr3_err(vreg, "corner debugfs directory creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_file("index", S_IRUGO | S_IWUSR, corner_dir, -+ vreg, &cpr3_debug_corner_index_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "index debugfs file creation failed\n"); -+ return; -+ } -+ -+ cpr3_regulator_debugfs_corner_add(vreg, corner_dir, -+ &vreg->debug_corner); -+ -+ corner_dir = debugfs_create_dir("current_corner", vreg_dir); -+ if (IS_ERR_OR_NULL(corner_dir)) { -+ cpr3_err(vreg, "current_corner debugfs directory creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_file("index", S_IRUGO, corner_dir, -+ vreg, &cpr3_debug_current_corner_index_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(vreg, "index debugfs file creation failed\n"); -+ return; -+ } -+ -+ cpr3_regulator_debugfs_corner_add(vreg, corner_dir, -+ &vreg->current_corner); -+} -+ -+/** -+ * cpr3_regulator_debugfs_thread_add() - add debugfs files to expose -+ * configuration data for the CPR thread -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_debugfs_thread_add(struct cpr3_thread *thread) -+{ -+ struct cpr3_controller *ctrl = thread->ctrl; -+ struct dentry *aggr_dir, *temp, *thread_dir; -+ struct cpr3_debug_corner_info *info; -+ char buf[20]; -+ int *index; -+ int i; -+ -+ scnprintf(buf, sizeof(buf), "thread%u", thread->thread_id); -+ thread_dir = debugfs_create_dir(buf, thread->ctrl->debugfs); -+ if (IS_ERR_OR_NULL(thread_dir)) { -+ cpr3_err(ctrl, "thread %u %s debugfs directory creation failed\n", -+ thread->thread_id, buf); -+ return; -+ } -+ -+ aggr_dir = debugfs_create_dir("max_aggregated_params", thread_dir); -+ if (IS_ERR_OR_NULL(aggr_dir)) { -+ cpr3_err(ctrl, "thread %u max_aggregated_params debugfs directory creation failed\n", -+ thread->thread_id); -+ return; -+ } -+ -+ temp = debugfs_create_int("floor_volt", S_IRUGO, aggr_dir, -+ &thread->aggr_corner.floor_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "thread %u aggr floor_volt debugfs file creation failed\n", -+ thread->thread_id); -+ return; -+ } -+ -+ temp = debugfs_create_int("ceiling_volt", S_IRUGO, aggr_dir, -+ &thread->aggr_corner.ceiling_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "thread %u aggr ceiling_volt debugfs file creation failed\n", -+ thread->thread_id); -+ return; -+ } -+ -+ temp = debugfs_create_int("open_loop_volt", S_IRUGO, aggr_dir, -+ &thread->aggr_corner.open_loop_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "thread %u aggr open_loop_volt debugfs file creation failed\n", -+ thread->thread_id); -+ return; -+ } -+ -+ temp = debugfs_create_int("last_volt", S_IRUGO, aggr_dir, -+ &thread->aggr_corner.last_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "thread %u aggr last_volt debugfs file creation failed\n", -+ thread->thread_id); -+ return; -+ } -+ -+ info = devm_kzalloc(thread->ctrl->dev, sizeof(*info), GFP_KERNEL); -+ index = devm_kzalloc(thread->ctrl->dev, sizeof(*index), GFP_KERNEL); -+ if (!info || !index) -+ return; -+ *index = 0; -+ info->vreg = &thread->vreg[0]; -+ info->index = index; -+ info->corner = &thread->aggr_corner; -+ -+ temp = debugfs_create_file("target_quots", S_IRUGO, aggr_dir, -+ info, &cpr3_debug_quot_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "thread %u target_quots debugfs file creation failed\n", -+ thread->thread_id); -+ return; -+ } -+ -+ for (i = 0; i < thread->vreg_count; i++) -+ cpr3_regulator_debugfs_vreg_add(&thread->vreg[i], thread_dir); -+} -+ -+/** -+ * cpr3_debug_closed_loop_enable_set() - debugfs callback used to change the -+ * value of the CPR controller cpr_allowed_sw flag which enables or -+ * disables closed-loop operation -+ * @data: Pointer to private data which is equal to the CPR -+ * controller pointer -+ * @val: New value for cpr_allowed_sw -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_closed_loop_enable_set(void *data, u64 val) -+{ -+ struct cpr3_controller *ctrl = data; -+ bool enable = !!val; -+ int rc; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (ctrl->cpr_allowed_sw == enable) -+ goto done; -+ -+ if (enable && !ctrl->cpr_allowed_hw) { -+ cpr3_err(ctrl, "CPR closed-loop operation is not allowed\n"); -+ goto done; -+ } -+ -+ ctrl->cpr_allowed_sw = enable; -+ -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n", -+ enable, rc); -+ goto done; -+ } -+ -+ if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) { -+ rc = cpr3_clock_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "clock enable failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ ctrl->cpr_enabled = true; -+ -+ cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, -+ CPR3_PD_THROTTLE_DISABLE); -+ -+ cpr3_clock_disable(ctrl); -+ ctrl->cpr_enabled = false; -+ } -+ -+ cpr3_debug(ctrl, "closed-loop=%s\n", enable ? "enabled" : "disabled"); -+done: -+ mutex_unlock(&ctrl->lock); -+ return 0; -+} -+ -+/** -+ * cpr3_debug_closed_loop_enable_get() - debugfs callback used to retrieve -+ * the value of the CPR controller cpr_allowed_sw flag which -+ * indicates if closed-loop operation is enabled -+ * @data: Pointer to private data which is equal to the CPR -+ * controller pointer -+ * @val: Output parameter written with the value of -+ * cpr_allowed_sw -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_closed_loop_enable_get(void *data, u64 *val) -+{ -+ struct cpr3_controller *ctrl = data; -+ -+ *val = ctrl->cpr_allowed_sw; -+ -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_closed_loop_enable_fops, -+ cpr3_debug_closed_loop_enable_get, -+ cpr3_debug_closed_loop_enable_set, -+ "%llu\n"); -+ -+/** -+ * cpr3_debug_hw_closed_loop_enable_set() - debugfs callback used to change the -+ * value of the CPR controller use_hw_closed_loop flag which -+ * switches between software closed-loop and hardware closed-loop -+ * operation for CPR3 and CPR4 controllers and between open-loop -+ * and full hardware closed-loop operation for CPRh controllers. -+ * @data: Pointer to private data which is equal to the CPR -+ * controller pointer -+ * @val: New value for use_hw_closed_loop -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) -+{ -+ struct cpr3_controller *ctrl = data; -+ bool use_hw_closed_loop = !!val; -+ struct cpr3_regulator *vreg; -+ bool cpr_enabled; -+ int i, j, k, rc; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (ctrl->use_hw_closed_loop == use_hw_closed_loop) -+ goto done; -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ ctrl->use_hw_closed_loop = use_hw_closed_loop; -+ -+ cpr_enabled = ctrl->cpr_enabled; -+ -+ /* Ensure that CPR clocks are enabled before writing to registers. */ -+ if (!cpr_enabled) { -+ rc = cpr3_clock_enable(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); -+ goto done; -+ } -+ ctrl->cpr_enabled = true; -+ } -+ -+ if (ctrl->use_hw_closed_loop) -+ cpr3_write(ctrl, CPR3_REG_IRQ_EN, 0); -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, -+ CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, -+ ctrl->use_hw_closed_loop -+ ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE -+ : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); -+ } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, -+ ctrl->use_hw_closed_loop -+ ? CPR3_HW_CLOSED_LOOP_ENABLE -+ : CPR3_HW_CLOSED_LOOP_DISABLE); -+ } -+ -+ /* Turn off CPR clocks if they were off before this function call. */ -+ if (!cpr_enabled) { -+ cpr3_clock_disable(ctrl); -+ ctrl->cpr_enabled = false; -+ } -+ -+ if (ctrl->use_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ rc = regulator_enable(ctrl->vdd_limit_regulator); -+ if (rc) { -+ cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } else if (!ctrl->use_hw_closed_loop -+ && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ rc = regulator_disable(ctrl->vdd_limit_regulator); -+ if (rc) { -+ cpr3_err(ctrl, "CPR limit regulator disable failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ /* -+ * Due to APM and mem-acc floor restriction constraints, -+ * the closed-loop voltage may be different when using -+ * software closed-loop vs hardware closed-loop. Therefore, -+ * reset the cached closed-loop voltage for all corners to the -+ * corresponding open-loop voltage when switching between -+ * SW and HW closed-loop mode. -+ */ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ for (k = 0; k < vreg->corner_count; k++) -+ vreg->corner[k].last_volt -+ = vreg->corner[k].open_loop_volt; -+ } -+ } -+ -+ /* Skip last_volt caching */ -+ ctrl->last_corner_was_closed_loop = false; -+ -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n", -+ use_hw_closed_loop, rc); -+ goto done; -+ } -+ -+ cpr3_debug(ctrl, "CPR mode=%s\n", -+ use_hw_closed_loop ? -+ "HW closed-loop" : "SW closed-loop"); -+done: -+ mutex_unlock(&ctrl->lock); -+ return 0; -+} -+ -+/** -+ * cpr3_debug_hw_closed_loop_enable_get() - debugfs callback used to retrieve -+ * the value of the CPR controller use_hw_closed_loop flag which -+ * indicates if hardware closed-loop operation is being used in -+ * place of software closed-loop operation -+ * @data: Pointer to private data which is equal to the CPR -+ * controller pointer -+ * @val: Output parameter written with the value of -+ * use_hw_closed_loop -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_hw_closed_loop_enable_get(void *data, u64 *val) -+{ -+ struct cpr3_controller *ctrl = data; -+ -+ *val = ctrl->use_hw_closed_loop; -+ -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_hw_closed_loop_enable_fops, -+ cpr3_debug_hw_closed_loop_enable_get, -+ cpr3_debug_hw_closed_loop_enable_set, -+ "%llu\n"); -+ -+/** -+ * cpr3_debug_trigger_aging_measurement_set() - debugfs callback used to trigger -+ * another CPR measurement -+ * @data: Pointer to private data which is equal to the CPR -+ * controller pointer -+ * @val: Unused -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_debug_trigger_aging_measurement_set(void *data, u64 val) -+{ -+ struct cpr3_controller *ctrl = data; -+ int rc; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX); -+ ctrl->aging_required = true; -+ ctrl->aging_succeeded = false; -+ ctrl->aging_failed = false; -+ -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not update the CPR controller state, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+done: -+ mutex_unlock(&ctrl->lock); -+ return 0; -+} -+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_trigger_aging_measurement_fops, -+ NULL, -+ cpr3_debug_trigger_aging_measurement_set, -+ "%llu\n"); -+ -+/** -+ * cpr3_regulator_debugfs_ctrl_add() - add debugfs files to expose configuration -+ * data for the CPR controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_debugfs_ctrl_add(struct cpr3_controller *ctrl) -+{ -+ struct dentry *temp, *aggr_dir; -+ int i; -+ -+ /* Add cpr3-regulator base directory if it isn't present already. */ -+ if (cpr3_debugfs_base == NULL) { -+ cpr3_debugfs_base = debugfs_create_dir("cpr3-regulator", NULL); -+ if (IS_ERR_OR_NULL(cpr3_debugfs_base)) { -+ cpr3_err(ctrl, "cpr3-regulator debugfs base directory creation failed\n"); -+ cpr3_debugfs_base = NULL; -+ return; -+ } -+ } -+ -+ ctrl->debugfs = debugfs_create_dir(ctrl->name, cpr3_debugfs_base); -+ if (IS_ERR_OR_NULL(ctrl->debugfs)) { -+ cpr3_err(ctrl, "cpr3-regulator controller debugfs directory creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_file("cpr_closed_loop_enable", S_IRUGO | S_IWUSR, -+ ctrl->debugfs, ctrl, -+ &cpr3_debug_closed_loop_enable_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "cpr_closed_loop_enable debugfs file creation failed\n"); -+ return; -+ } -+ -+ if (ctrl->supports_hw_closed_loop) { -+ temp = debugfs_create_file("use_hw_closed_loop", -+ S_IRUGO | S_IWUSR, ctrl->debugfs, ctrl, -+ &cpr3_debug_hw_closed_loop_enable_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "use_hw_closed_loop debugfs file creation failed\n"); -+ return; -+ } -+ } -+ -+ temp = debugfs_create_int("thread_count", S_IRUGO, ctrl->debugfs, -+ &ctrl->thread_count); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "thread_count debugfs file creation failed\n"); -+ return; -+ } -+ -+ if (ctrl->apm) { -+ temp = debugfs_create_int("apm_threshold_volt", S_IRUGO, -+ ctrl->debugfs, &ctrl->apm_threshold_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "apm_threshold_volt debugfs file creation failed\n"); -+ return; -+ } -+ } -+ -+ if (ctrl->aging_required || ctrl->aging_succeeded -+ || ctrl->aging_failed) { -+ temp = debugfs_create_int("aging_adj_volt", S_IRUGO, -+ ctrl->debugfs, &ctrl->aging_ref_adjust_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aging_adj_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_file("aging_succeeded", S_IRUGO, -+ ctrl->debugfs, &ctrl->aging_succeeded, &fops_bool_ro); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aging_succeeded debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_file("aging_failed", S_IRUGO, -+ ctrl->debugfs, &ctrl->aging_failed, &fops_bool_ro); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aging_failed debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_file("aging_trigger", S_IWUSR, -+ ctrl->debugfs, ctrl, -+ &cpr3_debug_trigger_aging_measurement_fops); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aging_trigger debugfs file creation failed\n"); -+ return; -+ } -+ } -+ -+ aggr_dir = debugfs_create_dir("max_aggregated_voltages", ctrl->debugfs); -+ if (IS_ERR_OR_NULL(aggr_dir)) { -+ cpr3_err(ctrl, "max_aggregated_voltages debugfs directory creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("floor_volt", S_IRUGO, aggr_dir, -+ &ctrl->aggr_corner.floor_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aggr floor_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("ceiling_volt", S_IRUGO, aggr_dir, -+ &ctrl->aggr_corner.ceiling_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aggr ceiling_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("open_loop_volt", S_IRUGO, aggr_dir, -+ &ctrl->aggr_corner.open_loop_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aggr open_loop_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ temp = debugfs_create_int("last_volt", S_IRUGO, aggr_dir, -+ &ctrl->aggr_corner.last_volt); -+ if (IS_ERR_OR_NULL(temp)) { -+ cpr3_err(ctrl, "aggr last_volt debugfs file creation failed\n"); -+ return; -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) -+ cpr3_regulator_debugfs_thread_add(&ctrl->thread[i]); -+} -+ -+/** -+ * cpr3_regulator_debugfs_ctrl_remove() - remove debugfs files for the CPR -+ * controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Note, this function must be called after the controller has been removed from -+ * cpr3_controller_list and while the cpr3_controller_list_mutex lock is held. -+ * -+ * Return: none -+ */ -+static void cpr3_regulator_debugfs_ctrl_remove(struct cpr3_controller *ctrl) -+{ -+ if (list_empty(&cpr3_controller_list)) { -+ debugfs_remove_recursive(cpr3_debugfs_base); -+ cpr3_debugfs_base = NULL; -+ } else { -+ debugfs_remove_recursive(ctrl->debugfs); -+ } -+} -+ -+/** -+ * cpr3_regulator_init_ctrl_data() - performs initialization of CPR controller -+ * elements -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_init_ctrl_data(struct cpr3_controller *ctrl) -+{ -+ /* Read the initial vdd voltage from hardware. */ -+ ctrl->aggr_corner.last_volt -+ = regulator_get_voltage(ctrl->vdd_regulator); -+ if (ctrl->aggr_corner.last_volt < 0) { -+ cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n", -+ ctrl->aggr_corner.last_volt); -+ return ctrl->aggr_corner.last_volt; -+ } -+ ctrl->aggr_corner.open_loop_volt = ctrl->aggr_corner.last_volt; -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_init_vreg_data() - performs initialization of common CPR3 -+ * regulator elements and validate aging configurations -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg) -+{ -+ int i, j; -+ bool init_aging; -+ -+ vreg->current_corner = CPR3_REGULATOR_CORNER_INVALID; -+ vreg->last_closed_loop_corner = CPR3_REGULATOR_CORNER_INVALID; -+ -+ init_aging = vreg->aging_allowed && vreg->thread->ctrl->aging_required; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt; -+ vreg->corner[i].irq_en = CPR3_IRQ_UP | CPR3_IRQ_DOWN; -+ -+ vreg->corner[i].ro_mask = 0; -+ for (j = 0; j < CPR3_RO_COUNT; j++) { -+ if (vreg->corner[i].target_quot[j] == 0) -+ vreg->corner[i].ro_mask |= BIT(j); -+ } -+ -+ if (init_aging) { -+ vreg->corner[i].unaged_floor_volt -+ = vreg->corner[i].floor_volt; -+ vreg->corner[i].unaged_ceiling_volt -+ = vreg->corner[i].ceiling_volt; -+ vreg->corner[i].unaged_open_loop_volt -+ = vreg->corner[i].open_loop_volt; -+ } -+ -+ if (vreg->aging_allowed) { -+ if (vreg->corner[i].unaged_floor_volt <= 0) { -+ cpr3_err(vreg, "invalid unaged_floor_volt[%d] = %d\n", -+ i, vreg->corner[i].unaged_floor_volt); -+ return -EINVAL; -+ } -+ if (vreg->corner[i].unaged_ceiling_volt <= 0) { -+ cpr3_err(vreg, "invalid unaged_ceiling_volt[%d] = %d\n", -+ i, vreg->corner[i].unaged_ceiling_volt); -+ return -EINVAL; -+ } -+ if (vreg->corner[i].unaged_open_loop_volt <= 0) { -+ cpr3_err(vreg, "invalid unaged_open_loop_volt[%d] = %d\n", -+ i, vreg->corner[i].unaged_open_loop_volt); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ if (vreg->aging_allowed && vreg->corner[vreg->aging_corner].ceiling_volt -+ > vreg->thread->ctrl->aging_ref_volt) { -+ cpr3_err(vreg, "aging corner %d ceiling voltage = %d > aging ref voltage = %d uV\n", -+ vreg->aging_corner, -+ vreg->corner[vreg->aging_corner].ceiling_volt, -+ vreg->thread->ctrl->aging_ref_volt); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_suspend() - perform common required CPR3 power down steps -+ * before the system enters suspend -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_regulator_suspend(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ mutex_lock(&ctrl->lock); -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc); -+ mutex_unlock(&ctrl->lock); -+ return rc; -+ } -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ rc = cpr3_closed_loop_disable(ctrl); -+ if (rc) -+ cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc); -+ -+ ctrl->cpr_suspended = true; -+ -+ mutex_unlock(&ctrl->lock); -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_resume() - perform common required CPR3 power up steps after -+ * the system resumes from suspend -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_regulator_resume(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ mutex_lock(&ctrl->lock); -+ -+ ctrl->cpr_suspended = false; -+ rc = cpr3_regulator_update_ctrl_state(ctrl); -+ if (rc) -+ cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc); -+ -+ mutex_unlock(&ctrl->lock); -+ return 0; -+} -+ -+/** -+ * cpr3_regulator_validate_controller() - verify the data passed in via the -+ * cpr3_controller data structure -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl) -+{ -+ struct cpr3_thread *thread; -+ struct cpr3_regulator *vreg; -+ int i, j, allow_boost_vreg_count = 0; -+ -+ if (!ctrl->vdd_regulator) { -+ cpr3_err(ctrl, "vdd regulator missing\n"); -+ return -EINVAL; -+ } else if (ctrl->sensor_count <= 0 -+ || ctrl->sensor_count > CPR3_MAX_SENSOR_COUNT) { -+ cpr3_err(ctrl, "invalid CPR sensor count=%d\n", -+ ctrl->sensor_count); -+ return -EINVAL; -+ } else if (!ctrl->sensor_owner) { -+ cpr3_err(ctrl, "CPR sensor ownership table missing\n"); -+ return -EINVAL; -+ } -+ -+ if (ctrl->aging_required) { -+ for (i = 0; i < ctrl->aging_sensor_count; i++) { -+ if (ctrl->aging_sensor[i].sensor_id -+ >= ctrl->sensor_count) { -+ cpr3_err(ctrl, "aging_sensor[%d] id=%u is not in the value range 0-%d", -+ i, ctrl->aging_sensor[i].sensor_id, -+ ctrl->sensor_count - 1); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ thread = &ctrl->thread[i]; -+ for (j = 0; j < thread->vreg_count; j++) { -+ vreg = &thread->vreg[j]; -+ if (vreg->allow_boost) -+ allow_boost_vreg_count++; -+ } -+ } -+ -+ if (allow_boost_vreg_count > 1) { -+ /* -+ * Boost feature is not allowed to be used for more -+ * than one CPR3 regulator of a CPR3 controller. -+ */ -+ cpr3_err(ctrl, "Boost feature is enabled for more than one regulator\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_panic_callback() - panic notification callback function. This function -+ * is invoked when a kernel panic occurs. -+ * @nfb: Notifier block pointer of CPR3 controller -+ * @event: Value passed unmodified to notifier function -+ * @data: Pointer passed unmodified to notifier function -+ * -+ * Return: NOTIFY_OK -+ */ -+static int cpr3_panic_callback(struct notifier_block *nfb, -+ unsigned long event, void *data) -+{ -+ struct cpr3_controller *ctrl = container_of(nfb, -+ struct cpr3_controller, panic_notifier); -+ struct cpr3_panic_regs_info *regs_info = ctrl->panic_regs_info; -+ struct cpr3_reg_info *reg; -+ int i = 0; -+ -+ for (i = 0; i < regs_info->reg_count; i++) { -+ reg = &(regs_info->regs[i]); -+ reg->value = readl_relaxed(reg->virt_addr); -+ pr_err("%s[0x%08x] = 0x%08x\n", reg->name, reg->addr, -+ reg->value); -+ } -+ /* -+ * Barrier to ensure that the information has been updated in the -+ * structure. -+ */ -+ mb(); -+ -+ return NOTIFY_OK; -+} -+ -+/** -+ * cpr3_regulator_register() - register the regulators for a CPR3 controller and -+ * perform CPR hardware initialization -+ * @pdev: Platform device pointer for the CPR3 controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_regulator_register(struct platform_device *pdev, -+ struct cpr3_controller *ctrl) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ int i, j, rc; -+ -+ if (!dev->of_node) { -+ dev_err(dev, "%s: Device tree node is missing\n", __func__); -+ return -EINVAL; -+ } -+ -+ if (!ctrl || !ctrl->name) { -+ dev_err(dev, "%s: CPR controller data is missing\n", __func__); -+ return -EINVAL; -+ } -+ -+ rc = cpr3_regulator_validate_controller(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "controller validation failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ mutex_init(&ctrl->lock); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpr_ctrl"); -+ if (!res || !res->start) { -+ cpr3_err(ctrl, "CPR controller address is missing\n"); -+ return -ENXIO; -+ } -+ ctrl->cpr_ctrl_base = devm_ioremap(dev, res->start, resource_size(res)); -+ -+ if (ctrl->aging_possible_mask) { -+ /* -+ * Aging possible register address is required if an aging -+ * possible mask has been specified. -+ */ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ "aging_allowed"); -+ if (!res || !res->start) { -+ cpr3_err(ctrl, "CPR aging allowed address is missing\n"); -+ return -ENXIO; -+ } -+ ctrl->aging_possible_reg = devm_ioremap(dev, res->start, -+ resource_size(res)); -+ } -+ -+ ctrl->irq = platform_get_irq_byname(pdev, "cpr"); -+ if (ctrl->irq < 0) { -+ cpr3_err(ctrl, "missing CPR interrupt\n"); -+ return ctrl->irq; -+ } -+ -+ if (ctrl->supports_hw_closed_loop) { -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ ctrl->ceiling_irq = platform_get_irq_byname(pdev, -+ "ceiling"); -+ if (ctrl->ceiling_irq < 0) { -+ cpr3_err(ctrl, "missing ceiling interrupt\n"); -+ return ctrl->ceiling_irq; -+ } -+ } -+ } -+ -+ rc = cpr3_regulator_init_ctrl_data(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ rc = cpr3_regulator_init_vreg_data( -+ &ctrl->thread[i].vreg[j]); -+ if (rc) -+ return rc; -+ cpr3_print_quots(&ctrl->thread[i].vreg[j]); -+ } -+ } -+ -+ /* -+ * Add the maximum possible aging voltage margin until it is possible -+ * to perform an aging measurement. -+ */ -+ if (ctrl->aging_required) -+ cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX); -+ -+ rc = cpr3_regulator_init_ctrl(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "CPR controller initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ /* Register regulator devices for all threads. */ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ rc = cpr3_regulator_vreg_register( -+ &ctrl->thread[i].vreg[j]); -+ if (rc) { -+ cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n", -+ rc); -+ goto free_regulators; -+ } -+ } -+ } -+ -+ rc = devm_request_threaded_irq(dev, ctrl->irq, NULL, -+ cpr3_irq_handler, -+ IRQF_ONESHOT | -+ IRQF_TRIGGER_RISING, -+ "cpr3", ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not request IRQ %d, rc=%d\n", -+ ctrl->irq, rc); -+ goto free_regulators; -+ } -+ -+ if (ctrl->supports_hw_closed_loop && -+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { -+ rc = devm_request_threaded_irq(dev, ctrl->ceiling_irq, NULL, -+ cpr3_ceiling_irq_handler, -+ IRQF_ONESHOT | IRQF_TRIGGER_RISING, -+ "cpr3_ceiling", ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "could not request ceiling IRQ %d, rc=%d\n", -+ ctrl->ceiling_irq, rc); -+ goto free_regulators; -+ } -+ } -+ -+ mutex_lock(&cpr3_controller_list_mutex); -+ cpr3_regulator_debugfs_ctrl_add(ctrl); -+ list_add(&ctrl->list, &cpr3_controller_list); -+ mutex_unlock(&cpr3_controller_list_mutex); -+ -+ if (ctrl->panic_regs_info) { -+ /* Register panic notification call back */ -+ ctrl->panic_notifier.notifier_call = cpr3_panic_callback; -+ atomic_notifier_chain_register(&panic_notifier_list, -+ &ctrl->panic_notifier); -+ } -+ -+ return 0; -+ -+free_regulators: -+ for (i = 0; i < ctrl->thread_count; i++) -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) -+ if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev)) -+ regulator_unregister( -+ ctrl->thread[i].vreg[j].rdev); -+ return rc; -+} -+ -+/** -+ * cpr3_open_loop_regulator_register() - register the regulators for a CPR3 -+ * controller which will always work in Open loop and -+ * won't support close loop. -+ * @pdev: Platform device pointer for the CPR3 controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_open_loop_regulator_register(struct platform_device *pdev, -+ struct cpr3_controller *ctrl) -+{ -+ struct device *dev = &pdev->dev; -+ struct cpr3_regulator *vreg; -+ int i, j, rc; -+ -+ if (!dev->of_node) { -+ dev_err(dev, "%s: Device tree node is missing\n", __func__); -+ return -EINVAL; -+ } -+ -+ if (!ctrl || !ctrl->name) { -+ dev_err(dev, "%s: CPR controller data is missing\n", __func__); -+ return -EINVAL; -+ } -+ -+ if (!ctrl->vdd_regulator) { -+ cpr3_err(ctrl, "vdd regulator missing\n"); -+ return -EINVAL; -+ } -+ -+ mutex_init(&ctrl->lock); -+ -+ rc = cpr3_regulator_init_ctrl_data(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ vreg = &ctrl->thread[i].vreg[j]; -+ vreg->corner[i].last_volt = -+ vreg->corner[i].open_loop_volt; -+ } -+ } -+ -+ /* Register regulator devices for all threads. */ -+ for (i = 0; i < ctrl->thread_count; i++) { -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) { -+ rc = cpr3_regulator_vreg_register( -+ &ctrl->thread[i].vreg[j]); -+ if (rc) { -+ cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n", -+ rc); -+ goto free_regulators; -+ } -+ } -+ } -+ -+ mutex_lock(&cpr3_controller_list_mutex); -+ list_add(&ctrl->list, &cpr3_controller_list); -+ mutex_unlock(&cpr3_controller_list_mutex); -+ -+ return 0; -+ -+free_regulators: -+ for (i = 0; i < ctrl->thread_count; i++) -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) -+ if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev)) -+ regulator_unregister( -+ ctrl->thread[i].vreg[j].rdev); -+ return rc; -+} -+ -+/** -+ * cpr3_regulator_unregister() - unregister the regulators for a CPR3 controller -+ * and perform CPR hardware shutdown -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_regulator_unregister(struct cpr3_controller *ctrl) -+{ -+ int i, j, rc = 0; -+ -+ mutex_lock(&cpr3_controller_list_mutex); -+ list_del(&ctrl->list); -+ cpr3_regulator_debugfs_ctrl_remove(ctrl); -+ mutex_unlock(&cpr3_controller_list_mutex); -+ -+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { -+ rc = cpr3_ctrl_clear_cpr4_config(ctrl); -+ if (rc) -+ cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", -+ rc); -+ } -+ -+ cpr3_ctrl_loop_disable(ctrl); -+ -+ cpr3_closed_loop_disable(ctrl); -+ -+ if (ctrl->vdd_limit_regulator) { -+ regulator_disable(ctrl->vdd_limit_regulator); -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) -+ regulator_unregister(ctrl->thread[i].vreg[j].rdev); -+ -+ if (ctrl->panic_notifier.notifier_call) -+ atomic_notifier_chain_unregister(&panic_notifier_list, -+ &ctrl->panic_notifier); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_open_loop_regulator_unregister() - unregister the regulators for a CPR3 -+ * open loop controller and perform CPR hardware shutdown -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl) -+{ -+ int i, j; -+ -+ mutex_lock(&cpr3_controller_list_mutex); -+ list_del(&ctrl->list); -+ mutex_unlock(&cpr3_controller_list_mutex); -+ -+ if (ctrl->vdd_limit_regulator) { -+ regulator_disable(ctrl->vdd_limit_regulator); -+ } -+ -+ for (i = 0; i < ctrl->thread_count; i++) -+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) -+ regulator_unregister(ctrl->thread[i].vreg[j].rdev); -+ -+ if (ctrl->panic_notifier.notifier_call) -+ atomic_notifier_chain_unregister(&panic_notifier_list, -+ &ctrl->panic_notifier); -+ -+ return 0; -+} ---- /dev/null -+++ b/drivers/regulator/cpr3-regulator.h -@@ -0,0 +1,1211 @@ -+/* -+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __REGULATOR_CPR3_REGULATOR_H__ -+#define __REGULATOR_CPR3_REGULATOR_H__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct cpr3_controller; -+struct cpr3_thread; -+ -+/** -+ * struct cpr3_fuse_param - defines one contiguous segment of a fuse parameter -+ * that is contained within a given row. -+ * @row: Fuse row number -+ * @bit_start: The first bit within the row of the fuse parameter segment -+ * @bit_end: The last bit within the row of the fuse parameter segment -+ * -+ * Each fuse row is 64 bits in length. bit_start and bit_end may take values -+ * from 0 to 63. bit_start must be less than or equal to bit_end. -+ */ -+struct cpr3_fuse_param { -+ unsigned row; -+ unsigned bit_start; -+ unsigned bit_end; -+}; -+ -+/* Each CPR3 sensor has 16 ring oscillators */ -+#define CPR3_RO_COUNT 16 -+ -+/* The maximum number of sensors that can be present on a single CPR loop. */ -+#define CPR3_MAX_SENSOR_COUNT 256 -+ -+/* This constant is used when allocating array printing buffers. */ -+#define MAX_CHARS_PER_INT 10 -+ -+/** -+ * struct cpr4_sdelta - CPR4 controller specific data structure for the sdelta -+ * adjustment table which is used to adjust the VDD supply -+ * voltage automatically based upon the temperature and/or -+ * the number of online CPU cores. -+ * @allow_core_count_adj: Core count adjustments are allowed. -+ * @allow_temp_adj: Temperature based adjustments are allowed. -+ * @max_core_count: Maximum number of cores considered for core count -+ * adjustment logic. -+ * @temp_band_count: Number of temperature bands considered for temperature -+ * based adjustment logic. -+ * @cap_volt: CAP in uV to apply to SDELTA margins with multiple -+ * cpr3-regulators defined for single controller. -+ * @table: SDELTA table with per-online-core and temperature based -+ * adjustments of size (max_core_count * temp_band_count) -+ * Outer: core count -+ * Inner: temperature band -+ * Each element has units of VDD supply steps. Positive -+ * values correspond to a reduction in voltage and negative -+ * value correspond to an increase (this follows the SDELTA -+ * register semantics). -+ * @allow_boost: Voltage boost allowed. -+ * @boost_num_cores: The number of online cores at which the boost voltage -+ * adjustments will be applied -+ * @boost_table: SDELTA table with boost voltage adjustments of size -+ * temp_band_count. Each element has units of VDD supply -+ * steps. Positive values correspond to a reduction in -+ * voltage and negative value correspond to an increase -+ * (this follows the SDELTA register semantics). -+ */ -+struct cpr4_sdelta { -+ bool allow_core_count_adj; -+ bool allow_temp_adj; -+ int max_core_count; -+ int temp_band_count; -+ int cap_volt; -+ int *table; -+ bool allow_boost; -+ int boost_num_cores; -+ int *boost_table; -+}; -+ -+/** -+ * struct cpr3_corner - CPR3 virtual voltage corner data structure -+ * @floor_volt: CPR closed-loop floor voltage in microvolts -+ * @ceiling_volt: CPR closed-loop ceiling voltage in microvolts -+ * @open_loop_volt: CPR open-loop voltage (i.e. initial voltage) in -+ * microvolts -+ * @last_volt: Last known settled CPR closed-loop voltage which is used -+ * when switching to a new corner -+ * @abs_ceiling_volt: The absolute CPR closed-loop ceiling voltage in -+ * microvolts. This is used to limit the ceiling_volt -+ * value when it is increased as a result of aging -+ * adjustment. -+ * @unaged_floor_volt: The CPR closed-loop floor voltage in microvolts before -+ * any aging adjustment is performed -+ * @unaged_ceiling_volt: The CPR closed-loop ceiling voltage in microvolts -+ * before any aging adjustment is performed -+ * @unaged_open_loop_volt: The CPR open-loop voltage (i.e. initial voltage) in -+ * microvolts before any aging adjusment is performed -+ * @system_volt: The system-supply voltage in microvolts or corners or -+ * levels -+ * @mem_acc_volt: The mem-acc-supply voltage in corners -+ * @proc_freq: Processor frequency in Hertz. For CPR rev. 3 and 4 -+ * conrollers, this field is only used by platform specific -+ * CPR3 driver for interpolation. For CPRh-compliant -+ * controllers, this frequency is also utilized by the -+ * clock driver to determine the corner to CPU clock -+ * frequency mappings. -+ * @cpr_fuse_corner: Fused corner index associated with this virtual corner -+ * (only used by platform specific CPR3 driver for -+ * mapping purposes) -+ * @target_quot: Array of target quotient values to use for each ring -+ * oscillator (RO) for this corner. A value of 0 should be -+ * specified as the target quotient for each RO that is -+ * unused by this corner. -+ * @ro_scale: Array of CPR ring oscillator (RO) scaling factors. The -+ * scaling factor for each RO is defined from RO0 to RO15 -+ * with units of QUOT/V. A value of 0 may be specified for -+ * an RO that is unused. -+ * @ro_mask: Bitmap where each of the 16 LSBs indicate if the -+ * corresponding ROs should be masked for this corner -+ * @irq_en: Bitmap of the CPR interrupts to enable for this corner -+ * @aging_derate: The amount to derate the aging voltage adjustment -+ * determined for the reference corner in units of uV/mV. -+ * E.g. a value of 900 would imply that the adjustment for -+ * this corner should be 90% (900/1000) of that for the -+ * reference corner. -+ * @use_open_loop: Boolean indicating that open-loop (i.e CPR disabled) as -+ * opposed to closed-loop operation must be used for this -+ * corner on CPRh controllers. -+ * @sdelta: The CPR4 controller specific data for this corner. This -+ * field is applicable for CPR4 controllers. -+ * -+ * The value of last_volt is initialized inside of the cpr3_regulator_register() -+ * call with the open_loop_volt value. It can later be updated to the settled -+ * VDD supply voltage. The values for unaged_floor_volt, unaged_ceiling_volt, -+ * and unaged_open_loop_volt are initialized inside of cpr3_regulator_register() -+ * if ctrl->aging_required == true. These three values must be pre-initialized -+ * if cpr3_regulator_register() is called with ctrl->aging_required == false and -+ * ctrl->aging_succeeded == true. -+ * -+ * The values of ro_mask and irq_en are initialized inside of the -+ * cpr3_regulator_register() call. -+ */ -+struct cpr3_corner { -+ int floor_volt; -+ int ceiling_volt; -+ int cold_temp_open_loop_volt; -+ int normal_temp_open_loop_volt; -+ int open_loop_volt; -+ int last_volt; -+ int abs_ceiling_volt; -+ int unaged_floor_volt; -+ int unaged_ceiling_volt; -+ int unaged_open_loop_volt; -+ int system_volt; -+ int mem_acc_volt; -+ u32 proc_freq; -+ int cpr_fuse_corner; -+ u32 target_quot[CPR3_RO_COUNT]; -+ u32 ro_scale[CPR3_RO_COUNT]; -+ u32 ro_mask; -+ u32 irq_en; -+ int aging_derate; -+ bool use_open_loop; -+ struct cpr4_sdelta *sdelta; -+}; -+ -+/** -+ * struct cprh_corner_band - CPRh controller specific data structure which -+ * encapsulates the range of corners and the SDELTA -+ * adjustment table to be applied to the corners within -+ * the min and max bounds of the corner band. -+ * @corner: Corner number which defines the corner band boundary -+ * @sdelta: The SDELTA adjustment table which contains core-count -+ * and temp based margin adjustments that are applicable -+ * to the corner band. -+ */ -+struct cprh_corner_band { -+ int corner; -+ struct cpr4_sdelta *sdelta; -+}; -+ -+/** -+ * struct cpr3_fuse_parameters - CPR4 fuse specific data structure which has -+ * the required fuse parameters need for Close Loop CPR -+ * @(*apss_ro_sel_param)[2]: Pointer to RO select fuse details -+ * @(*apss_init_voltage_param)[2]: Pointer to Target voltage fuse details -+ * @(*apss_target_quot_param)[2]: Pointer to Target quot fuse details -+ * @(*apss_quot_offset_param)[2]: Pointer to quot offset fuse details -+ * @cpr_fusing_rev_param: Pointer to CPR revision fuse details -+ * @apss_speed_bin_param: Pointer to Speed bin fuse details -+ * @cpr_boost_fuse_cfg_param: Pointer to Boost fuse cfg details -+ * @apss_boost_fuse_volt_param: Pointer to Boost fuse volt details -+ * @misc_fuse_volt_adj_param: Pointer to Misc fuse volt fuse details -+ */ -+struct cpr3_fuse_parameters { -+ struct cpr3_fuse_param (*apss_ro_sel_param)[2]; -+ struct cpr3_fuse_param (*apss_init_voltage_param)[2]; -+ struct cpr3_fuse_param (*apss_target_quot_param)[2]; -+ struct cpr3_fuse_param (*apss_quot_offset_param)[2]; -+ struct cpr3_fuse_param *cpr_fusing_rev_param; -+ struct cpr3_fuse_param *apss_speed_bin_param; -+ struct cpr3_fuse_param *cpr_boost_fuse_cfg_param; -+ struct cpr3_fuse_param *apss_boost_fuse_volt_param; -+ struct cpr3_fuse_param *misc_fuse_volt_adj_param; -+}; -+ -+struct cpr4_mem_acc_func { -+ void (*set_mem_acc)(struct regulator_dev *); -+ void (*clear_mem_acc)(struct regulator_dev *); -+}; -+ -+/** -+ * struct cpr4_reg_data - CPR4 regulator specific data structure which is -+ * target specific -+ * @cpr_valid_fuse_count: Number of valid fuse corners -+ * @fuse_ref_volt: Pointer to fuse reference voltage -+ * @fuse_step_volt: CPR step voltage available in fuse -+ * @cpr_clk_rate: CPR clock rate -+ * @boost_fuse_ref_volt: Boost fuse reference voltage -+ * @boost_ceiling_volt: Boost ceiling voltage -+ * @boost_floor_volt: Boost floor voltage -+ * @cpr3_fuse_params: Pointer to CPR fuse parameters -+ * @mem_acc_funcs: Pointer to MEM ACC set/clear functions -+ **/ -+struct cpr4_reg_data { -+ u32 cpr_valid_fuse_count; -+ int *fuse_ref_volt; -+ u32 fuse_step_volt; -+ u32 cpr_clk_rate; -+ int boost_fuse_ref_volt; -+ int boost_ceiling_volt; -+ int boost_floor_volt; -+ struct cpr3_fuse_parameters *cpr3_fuse_params; -+ struct cpr4_mem_acc_func *mem_acc_funcs; -+}; -+/** -+ * struct cpr3_reg_data - CPR3 regulator specific data structure which is -+ * target specific -+ * @cpr_valid_fuse_count: Number of valid fuse corners -+ * @(*init_voltage_param)[2]: Pointer to Target voltage fuse details -+ * @fuse_ref_volt: Pointer to fuse reference voltage -+ * @fuse_step_volt: CPR step voltage available in fuse -+ * @cpr_clk_rate: CPR clock rate -+ * @cpr3_fuse_params: Pointer to CPR fuse parameters -+ **/ -+struct cpr3_reg_data { -+ u32 cpr_valid_fuse_count; -+ struct cpr3_fuse_param (*init_voltage_param)[2]; -+ int *fuse_ref_volt; -+ u32 fuse_step_volt; -+ u32 cpr_clk_rate; -+}; -+ -+/** -+ * struct cpr3_regulator - CPR3 logical regulator instance associated with a -+ * given CPR3 hardware thread -+ * @of_node: Device node associated with the device tree child node -+ * of this CPR3 regulator -+ * @thread: Pointer to the CPR3 thread which manages this CPR3 -+ * regulator -+ * @name: Unique name for this CPR3 regulator which is filled -+ * using the device tree regulator-name property -+ * @rdesc: Regulator description for this CPR3 regulator -+ * @rdev: Regulator device pointer for the regulator registered -+ * for this CPR3 regulator -+ * @mem_acc_regulator: Pointer to the optional mem-acc supply regulator used -+ * to manage memory circuitry settings based upon CPR3 -+ * regulator output voltage. -+ * @corner: Array of all corners supported by this CPR3 regulator -+ * @corner_count: The number of elements in the corner array -+ * @corner_band: Array of all corner bands supported by CPRh compatible -+ * controllers -+ * @cpr4_regulator_data Target specific cpr4 regulator data -+ * @cpr3_regulator_data Target specific cpr3 regulator data -+ * @corner_band_count: The number of elements in the corner band array -+ * @platform_fuses: Pointer to platform specific CPR fuse data (only used by -+ * platform specific CPR3 driver) -+ * @speed_bin_fuse: Value read from the speed bin fuse parameter -+ * @speed_bins_supported: The number of speed bins supported by the device tree -+ * configuration for this CPR3 regulator -+ * @cpr_rev_fuse: Value read from the CPR fusing revision fuse parameter -+ * @fuse_combo: Platform specific enum value identifying the specific -+ * combination of fuse values found on a given chip -+ * @fuse_combos_supported: The number of fuse combinations supported by the -+ * device tree configuration for this CPR3 regulator -+ * @fuse_corner_count: Number of corners defined by fuse parameters -+ * @fuse_corner_map: Array of length fuse_corner_count which specifies the -+ * highest corner associated with each fuse corner. Note -+ * that each element must correspond to a valid corner -+ * and that element values must be strictly increasing. -+ * Also, it is acceptable for the lowest fuse corner to map -+ * to a corner other than the lowest. Likewise, it is -+ * acceptable for the highest fuse corner to map to a -+ * corner other than the highest. -+ * @fuse_combo_corner_sum: The sum of the corner counts across all fuse combos -+ * @fuse_combo_offset: The device tree property array offset for the selected -+ * fuse combo -+ * @speed_bin_corner_sum: The sum of the corner counts across all speed bins -+ * This may be specified as 0 if per speed bin parsing -+ * support is not required. -+ * @speed_bin_offset: The device tree property array offset for the selected -+ * speed bin -+ * @fuse_combo_corner_band_sum: The sum of the corner band counts across all -+ * fuse combos -+ * @fuse_combo_corner_band_offset: The device tree property array offset for -+ * the corner band count corresponding to the selected -+ * fuse combo -+ * @speed_bin_corner_band_sum: The sum of the corner band counts across all -+ * speed bins. This may be specified as 0 if per speed bin -+ * parsing support is not required -+ * @speed_bin_corner_band_offset: The device tree property array offset for the -+ * corner band count corresponding to the selected speed -+ * bin -+ * @pd_bypass_mask: Bit mask of power domains associated with this CPR3 -+ * regulator -+ * @dynamic_floor_corner: Index identifying the voltage corner for the CPR3 -+ * regulator whose last_volt value should be used as the -+ * global CPR floor voltage if all of the power domains -+ * associated with this CPR3 regulator are bypassed -+ * @uses_dynamic_floor: Boolean flag indicating that dynamic_floor_corner should -+ * be utilized for the CPR3 regulator -+ * @current_corner: Index identifying the currently selected voltage corner -+ * for the CPR3 regulator or less than 0 if no corner has -+ * been requested -+ * @last_closed_loop_corner: Index identifying the last voltage corner for the -+ * CPR3 regulator which was configured when operating in -+ * CPR closed-loop mode or less than 0 if no corner has -+ * been requested. CPR registers are only written to when -+ * using closed-loop mode. -+ * @aggregated: Boolean flag indicating that this CPR3 regulator -+ * participated in the last aggregation event -+ * @debug_corner: Index identifying voltage corner used for displaying -+ * corner configuration values in debugfs -+ * @vreg_enabled: Boolean defining the enable state of the CPR3 -+ * regulator's regulator within the regulator framework. -+ * @aging_allowed: Boolean defining if CPR aging adjustments are allowed -+ * for this CPR3 regulator given the fuse combo of the -+ * device -+ * @aging_allow_open_loop_adj: Boolean defining if the open-loop voltage of each -+ * corner of this regulator should be adjusted as a result -+ * of an aging measurement. This flag can be set to false -+ * when the open-loop voltage adjustments have been -+ * specified such that they include the maximum possible -+ * aging adjustment. This flag is only used if -+ * aging_allowed == true. -+ * @aging_corner: The corner that should be configured for this regulator -+ * when an aging measurement is performed. -+ * @aging_max_adjust_volt: The maximum aging voltage margin in microvolts that -+ * may be added to the target quotients of this regulator. -+ * A value of 0 may be specified if this regulator does not -+ * require any aging adjustment. -+ * @allow_core_count_adj: Core count adjustments are allowed for this regulator. -+ * @allow_temp_adj: Temperature based adjustments are allowed for this -+ * regulator. -+ * @max_core_count: Maximum number of cores considered for core count -+ * adjustment logic. -+ * @allow_boost: Voltage boost allowed for this regulator. -+ * -+ * This structure contains both configuration and runtime state data. The -+ * elements current_corner, last_closed_loop_corner, aggregated, debug_corner, -+ * and vreg_enabled are state variables. -+ */ -+struct cpr3_regulator { -+ struct device_node *of_node; -+ struct cpr3_thread *thread; -+ const char *name; -+ struct regulator_desc rdesc; -+ struct regulator_dev *rdev; -+ struct regulator *mem_acc_regulator; -+ struct cpr3_corner *corner; -+ int corner_count; -+ struct cprh_corner_band *corner_band; -+ const struct cpr4_reg_data *cpr4_regulator_data; -+ const struct cpr3_reg_data *cpr3_regulator_data; -+ u32 corner_band_count; -+ -+ void *platform_fuses; -+ int speed_bin_fuse; -+ int speed_bins_supported; -+ int cpr_rev_fuse; -+ int part_type; -+ int part_type_supported; -+ int fuse_combo; -+ int fuse_combos_supported; -+ int fuse_corner_count; -+ int *fuse_corner_map; -+ int fuse_combo_corner_sum; -+ int fuse_combo_offset; -+ int speed_bin_corner_sum; -+ int speed_bin_offset; -+ int fuse_combo_corner_band_sum; -+ int fuse_combo_corner_band_offset; -+ int speed_bin_corner_band_sum; -+ int speed_bin_corner_band_offset; -+ u32 pd_bypass_mask; -+ int dynamic_floor_corner; -+ bool uses_dynamic_floor; -+ -+ int current_corner; -+ int last_closed_loop_corner; -+ bool aggregated; -+ int debug_corner; -+ bool vreg_enabled; -+ -+ bool aging_allowed; -+ bool aging_allow_open_loop_adj; -+ int aging_corner; -+ int aging_max_adjust_volt; -+ -+ bool allow_core_count_adj; -+ bool allow_temp_adj; -+ int max_core_count; -+ bool allow_boost; -+}; -+ -+/** -+ * struct cpr3_thread - CPR3 hardware thread data structure -+ * @thread_id: Hardware thread ID -+ * @of_node: Device node associated with the device tree child node -+ * of this CPR3 thread -+ * @ctrl: Pointer to the CPR3 controller which manages this thread -+ * @vreg: Array of CPR3 regulators handled by the CPR3 thread -+ * @vreg_count: Number of elements in the vreg array -+ * @aggr_corner: CPR corner containing the in process aggregated voltage -+ * and target quotient configurations which will be applied -+ * @last_closed_loop_aggr_corner: CPR corner containing the most recent -+ * configurations which were written into hardware -+ * registers when operating in closed loop mode (i.e. with -+ * CPR enabled) -+ * @consecutive_up: The number of consecutive CPR step up events needed to -+ * to trigger an up interrupt -+ * @consecutive_down: The number of consecutive CPR step down events needed to -+ * to trigger a down interrupt -+ * @up_threshold: The number CPR error steps required to generate an up -+ * event -+ * @down_threshold: The number CPR error steps required to generate a down -+ * event -+ * -+ * This structure contains both configuration and runtime state data. The -+ * elements aggr_corner and last_closed_loop_aggr_corner are state variables. -+ */ -+struct cpr3_thread { -+ u32 thread_id; -+ struct device_node *of_node; -+ struct cpr3_controller *ctrl; -+ struct cpr3_regulator *vreg; -+ int vreg_count; -+ struct cpr3_corner aggr_corner; -+ struct cpr3_corner last_closed_loop_aggr_corner; -+ -+ u32 consecutive_up; -+ u32 consecutive_down; -+ u32 up_threshold; -+ u32 down_threshold; -+}; -+ -+/* Per CPR controller data */ -+/** -+ * enum cpr3_mem_acc_corners - Constants which define the number of mem-acc -+ * regulator corners available in the mem-acc corner map array. -+ * %CPR3_MEM_ACC_LOW_CORNER: Index in mem-acc corner map array mapping to the -+ * mem-acc regulator corner -+ * to be used for low voltage vdd supply -+ * %CPR3_MEM_ACC_HIGH_CORNER: Index in mem-acc corner map array mapping to the -+ * mem-acc regulator corner to be used for high -+ * voltage vdd supply -+ * %CPR3_MEM_ACC_CORNERS: Number of elements in the mem-acc corner map -+ * array -+ */ -+enum cpr3_mem_acc_corners { -+ CPR3_MEM_ACC_LOW_CORNER = 0, -+ CPR3_MEM_ACC_HIGH_CORNER = 1, -+ CPR3_MEM_ACC_CORNERS = 2, -+}; -+ -+/** -+ * enum cpr3_count_mode - CPR3 controller count mode which defines the -+ * method that CPR sensor data is acquired -+ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MIN: Capture all CPR sensor readings -+ * simultaneously and report the minimum -+ * value seen in successive measurements -+ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MAX: Capture all CPR sensor readings -+ * simultaneously and report the maximum -+ * value seen in successive measurements -+ * %CPR3_COUNT_MODE_STAGGERED: Read one sensor at a time in a -+ * sequential fashion -+ * %CPR3_COUNT_MODE_ALL_AT_ONCE_AGE: Capture all CPR aging sensor readings -+ * simultaneously. -+ */ -+enum cpr3_count_mode { -+ CPR3_COUNT_MODE_ALL_AT_ONCE_MIN = 0, -+ CPR3_COUNT_MODE_ALL_AT_ONCE_MAX = 1, -+ CPR3_COUNT_MODE_STAGGERED = 2, -+ CPR3_COUNT_MODE_ALL_AT_ONCE_AGE = 3, -+}; -+ -+/** -+ * enum cpr_controller_type - supported CPR controller hardware types -+ * %CPR_CTRL_TYPE_CPR3: HW has CPR3 controller -+ * %CPR_CTRL_TYPE_CPR4: HW has CPR4 controller -+ */ -+enum cpr_controller_type { -+ CPR_CTRL_TYPE_CPR3, -+ CPR_CTRL_TYPE_CPR4, -+}; -+ -+/** -+ * cpr_setting - supported CPR global settings -+ * %CPR_DEFAULT: default mode from dts will be used -+ * %CPR_DISABLED: ceiling voltage will be used for all the corners -+ * %CPR_OPEN_LOOP_EN: CPR will work in OL -+ * %CPR_CLOSED_LOOP_EN: CPR will work in CL, if supported -+ */ -+enum cpr_setting { -+ CPR_DEFAULT = 0, -+ CPR_DISABLED = 1, -+ CPR_OPEN_LOOP_EN = 2, -+ CPR_CLOSED_LOOP_EN = 3, -+}; -+ -+/** -+ * struct cpr3_aging_sensor_info - CPR3 aging sensor information -+ * @sensor_id The index of the CPR3 sensor to be used in the aging -+ * measurement. -+ * @ro_scale The CPR ring oscillator (RO) scaling factor for the -+ * aging sensor with units of QUOT/V. -+ * @init_quot_diff: The fused quotient difference between aged and un-aged -+ * paths that was measured at manufacturing time. -+ * @measured_quot_diff: The quotient difference measured at runtime. -+ * @bypass_mask: Bit mask of the CPR sensors that must be bypassed during -+ * the aging measurement for this sensor -+ * -+ * This structure contains both configuration and runtime state data. The -+ * element measured_quot_diff is a state variable. -+ */ -+struct cpr3_aging_sensor_info { -+ u32 sensor_id; -+ u32 ro_scale; -+ int init_quot_diff; -+ int measured_quot_diff; -+ u32 bypass_mask[CPR3_MAX_SENSOR_COUNT / 32]; -+}; -+ -+/** -+ * struct cpr3_reg_info - Register information data structure -+ * @name: Register name -+ * @addr: Register physical address -+ * @value: Register content -+ * @virt_addr: Register virtual address -+ * -+ * This data structure is used to dump some critical register contents -+ * when the device crashes due to a kernel panic. -+ */ -+struct cpr3_reg_info { -+ const char *name; -+ u32 addr; -+ u32 value; -+ void __iomem *virt_addr; -+}; -+ -+/** -+ * struct cpr3_panic_regs_info - Data structure to dump critical register -+ * contents. -+ * @reg_count: Number of elements in the regs array -+ * @regs: Array of critical registers information -+ * -+ * This data structure is used to dump critical register contents when -+ * the device crashes due to a kernel panic. -+ */ -+struct cpr3_panic_regs_info { -+ int reg_count; -+ struct cpr3_reg_info *regs; -+}; -+ -+/** -+ * struct cpr3_controller - CPR3 controller data structure -+ * @dev: Device pointer for the CPR3 controller device -+ * @name: Unique name for the CPR3 controller -+ * @ctrl_id: Controller ID corresponding to the VDD supply number -+ * that this CPR3 controller manages. -+ * @cpr_ctrl_base: Virtual address of the CPR3 controller base register -+ * @fuse_base: Virtual address of fuse row 0 -+ * @aging_possible_reg: Virtual address of an optional platform-specific -+ * register that must be ready to determine if it is -+ * possible to perform an aging measurement. -+ * @list: list head used in a global cpr3-regulator list so that -+ * cpr3-regulator structs can be found easily in RAM dumps -+ * @thread: Array of CPR3 threads managed by the CPR3 controller -+ * @thread_count: Number of elements in the thread array -+ * @sensor_owner: Array of thread IDs indicating which thread owns a given -+ * CPR sensor -+ * @sensor_count: The number of CPR sensors found on the CPR loop managed -+ * by this CPR controller. Must be equal to the number of -+ * elements in the sensor_owner array -+ * @soc_revision: Revision number of the SoC. This may be unused by -+ * platforms that do not have different behavior for -+ * different SoC revisions. -+ * @lock: Mutex lock used to ensure mutual exclusion between -+ * all of the threads associated with the controller -+ * @vdd_regulator: Pointer to the VDD supply regulator which this CPR3 -+ * controller manages -+ * @system_regulator: Pointer to the optional system-supply regulator upon -+ * which the VDD supply regulator depends. -+ * @mem_acc_regulator: Pointer to the optional mem-acc supply regulator used -+ * to manage memory circuitry settings based upon the -+ * VDD supply output voltage. -+ * @vdd_limit_regulator: Pointer to the VDD supply limit regulator which is used -+ * for hardware closed-loop in order specify ceiling and -+ * floor voltage limits (platform specific) -+ * @system_supply_max_volt: Voltage in microvolts which corresponds to the -+ * absolute ceiling voltage of the system-supply -+ * @mem_acc_threshold_volt: mem-acc threshold voltage in microvolts -+ * @mem_acc_corner_map: mem-acc regulator corners mapping to low and high -+ * voltage mem-acc settings for the memories powered by -+ * this CPR3 controller and its associated CPR3 regulators -+ * @mem_acc_crossover_volt: Voltage in microvolts corresponding to the voltage -+ * that the VDD supply must be set to while a MEM ACC -+ * switch is in progress. This element must be initialized -+ * for CPRh controllers when a MEM ACC threshold voltage is -+ * defined. -+ * @core_clk: Pointer to the CPR3 controller core clock -+ * @iface_clk: Pointer to the CPR3 interface clock (platform specific) -+ * @bus_clk: Pointer to the CPR3 bus clock (platform specific) -+ * @irq: CPR interrupt number -+ * @irq_affinity_mask: The cpumask for the CPUs which the CPR interrupt should -+ * have affinity for -+ * @cpu_hotplug_notifier: CPU hotplug notifier used to reset IRQ affinity when a -+ * CPU is brought back online -+ * @ceiling_irq: Interrupt number for the interrupt that is triggered -+ * when hardware closed-loop attempts to exceed the ceiling -+ * voltage -+ * @apm: Handle to the array power mux (APM) -+ * @apm_threshold_volt: Voltage in microvolts which defines the threshold -+ * voltage to determine the APM supply selection for -+ * each corner -+ * @apm_crossover_volt: Voltage in microvolts corresponding to the voltage that -+ * the VDD supply must be set to while an APM switch is in -+ * progress. This element must be initialized for CPRh -+ * controllers when an APM threshold voltage is defined -+ * @apm_adj_volt: Minimum difference between APM threshold voltage and -+ * open-loop voltage which allows the APM threshold voltage -+ * to be used as a ceiling -+ * @apm_high_supply: APM supply to configure if VDD voltage is greater than -+ * or equal to the APM threshold voltage -+ * @apm_low_supply: APM supply to configure if the VDD voltage is less than -+ * the APM threshold voltage -+ * @base_volt: Minimum voltage in microvolts supported by the VDD -+ * supply managed by this CPR controller -+ * @corner_switch_delay_time: The delay time in nanoseconds used by the CPR -+ * controller to wait for voltage settling before -+ * acknowledging the OSM block after corner changes -+ * @cpr_clock_rate: CPR reference clock frequency in Hz. -+ * @sensor_time: The time in nanoseconds that each sensor takes to -+ * perform a measurement. -+ * @loop_time: The time in nanoseconds between consecutive CPR -+ * measurements. -+ * @up_down_delay_time: The time to delay in nanoseconds between consecutive CPR -+ * measurements when the last measurement recommended -+ * increasing or decreasing the vdd-supply voltage. -+ * (platform specific) -+ * @idle_clocks: Number of CPR reference clock ticks that the CPR -+ * controller waits in transitional states. -+ * @step_quot_init_min: The default minimum CPR step quotient value. The step -+ * quotient is the number of additional ring oscillator -+ * ticks observed when increasing one step in vdd-supply -+ * output voltage. -+ * @step_quot_init_max: The default maximum CPR step quotient value. -+ * @step_volt: Step size in microvolts between available set points -+ * of the VDD supply -+ * @down_error_step_limit: CPR4 hardware closed-loop down error step limit which -+ * defines the maximum number of VDD supply regulator steps -+ * that the voltage may be reduced as the result of a -+ * single CPR measurement. -+ * @up_error_step_limit: CPR4 hardware closed-loop up error step limit which -+ * defines the maximum number of VDD supply regulator steps -+ * that the voltage may be increased as the result of a -+ * single CPR measurement. -+ * @count_mode: CPR controller count mode -+ * @count_repeat: Number of times to perform consecutive sensor -+ * measurements when using all-at-once count modes. -+ * @proc_clock_throttle: Defines the processor clock frequency throttling -+ * register value to use. This can be used to reduce the -+ * clock frequency when a power domain exits a low power -+ * mode until CPR settles at a new voltage. -+ * (platform specific) -+ * @cpr_allowed_hw: Boolean which indicates if closed-loop CPR operation is -+ * permitted for a given chip based upon hardware fuse -+ * values -+ * @cpr_allowed_sw: Boolean which indicates if closed-loop CPR operation is -+ * permitted based upon software policies -+ * @supports_hw_closed_loop: Boolean which indicates if this CPR3/4 controller -+ * physically supports hardware closed-loop CPR operation -+ * @use_hw_closed_loop: Boolean which indicates that this controller will be -+ * using hardware closed-loop operation in place of -+ * software closed-loop operation. -+ * @ctrl_type: CPR controller type -+ * @saw_use_unit_mV: Boolean which indicates the unit used in SAW PVC -+ * interface is mV. -+ * @aggr_corner: CPR corner containing the most recently aggregated -+ * voltage configurations which are being used currently -+ * @cpr_enabled: Boolean which indicates that the CPR controller is -+ * enabled and operating in closed-loop mode. CPR clocks -+ * have been prepared and enabled whenever this flag is -+ * true. -+ * @last_corner_was_closed_loop: Boolean indicating if the last known corners -+ * were updated during closed loop operation. -+ * @cpr_suspended: Boolean which indicates that CPR has been temporarily -+ * disabled while enterring system suspend. -+ * @debugfs: Pointer to the debugfs directory of this CPR3 controller -+ * @aging_ref_volt: Reference voltage in microvolts to configure when -+ * performing CPR aging measurements. -+ * @aging_vdd_mode: vdd-supply regulator mode to configure before performing -+ * a CPR aging measurement. It should be one of -+ * REGULATOR_MODE_*. -+ * @aging_complete_vdd_mode: vdd-supply regulator mode to configure after -+ * performing a CPR aging measurement. It should be one of -+ * REGULATOR_MODE_*. -+ * @aging_ref_adjust_volt: The reference aging voltage margin in microvolts that -+ * should be added to the target quotients of the -+ * regulators managed by this controller after derating. -+ * @aging_required: Flag which indicates that a CPR aging measurement still -+ * needs to be performed for this CPR3 controller. -+ * @aging_succeeded: Flag which indicates that a CPR aging measurement has -+ * completed successfully. -+ * @aging_failed: Flag which indicates that a CPR aging measurement has -+ * failed to complete successfully. -+ * @aging_sensor: Array of CPR3 aging sensors which are used to perform -+ * aging measurements at a runtime. -+ * @aging_sensor_count: Number of elements in the aging_sensor array -+ * @aging_possible_mask: Optional bitmask used to mask off the -+ * aging_possible_reg register. -+ * @aging_possible_val: Optional value that the masked aging_possible_reg -+ * register must have in order for a CPR aging measurement -+ * to be possible. -+ * @step_quot_fixed: Fixed step quotient value used for target quotient -+ * adjustment if use_dynamic_step_quot is not set. -+ * This parameter is only relevant for CPR4 controllers -+ * when using the per-online-core or per-temperature -+ * adjustments. -+ * @initial_temp_band: Temperature band used for calculation of base-line -+ * target quotients (fused). -+ * @use_dynamic_step_quot: Boolean value which indicates that margin adjustment -+ * of target quotient will be based on the step quotient -+ * calculated dynamically in hardware for each RO. -+ * @allow_core_count_adj: Core count adjustments are allowed for this controller -+ * @allow_temp_adj: Temperature based adjustments are allowed for -+ * this controller -+ * @allow_boost: Voltage boost allowed for this controller. -+ * @temp_band_count: Number of temperature bands used for temperature based -+ * adjustment logic -+ * @temp_points: Array of temperature points in decidegrees Celsius used -+ * to specify the ranges for selected temperature bands. -+ * The array must have (temp_band_count - 1) elements -+ * allocated. -+ * @temp_sensor_id_start: Start ID of temperature sensors used for temperature -+ * based adjustments. -+ * @temp_sensor_id_end: End ID of temperature sensors used for temperature -+ * based adjustments. -+ * @voltage_settling_time: The time in nanoseconds that it takes for the -+ * VDD supply voltage to settle after being increased or -+ * decreased by step_volt microvolts which is used when -+ * SDELTA voltage margin adjustments are applied. -+ * @cpr_global_setting: Global setting for this CPR controller -+ * @panic_regs_info: Array of panic registers information which provides the -+ * list of registers to dump when the device crashes. -+ * @panic_notifier: Notifier block registered to global panic notifier list. -+ * -+ * This structure contains both configuration and runtime state data. The -+ * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled, -+ * last_corner_was_closed_loop, cpr_suspended, aging_ref_adjust_volt, -+ * aging_required, aging_succeeded, and aging_failed are state variables. -+ * -+ * The apm* elements do not need to be initialized if the VDD supply managed by -+ * the CPR3 controller does not utilize an APM. -+ * -+ * The elements step_quot_fixed, initial_temp_band, allow_core_count_adj, -+ * allow_temp_adj and temp* need to be initialized for CPR4 controllers which -+ * are using per-online-core or per-temperature adjustments. -+ */ -+struct cpr3_controller { -+ struct device *dev; -+ const char *name; -+ int ctrl_id; -+ void __iomem *cpr_ctrl_base; -+ void __iomem *fuse_base; -+ void __iomem *aging_possible_reg; -+ struct list_head list; -+ struct cpr3_thread *thread; -+ int thread_count; -+ u8 *sensor_owner; -+ int sensor_count; -+ int soc_revision; -+ struct mutex lock; -+ struct regulator *vdd_regulator; -+ struct regulator *system_regulator; -+ struct regulator *mem_acc_regulator; -+ struct regulator *vdd_limit_regulator; -+ int system_supply_max_volt; -+ int mem_acc_threshold_volt; -+ int mem_acc_corner_map[CPR3_MEM_ACC_CORNERS]; -+ int mem_acc_crossover_volt; -+ struct clk *core_clk; -+ struct clk *iface_clk; -+ struct clk *bus_clk; -+ int irq; -+ struct cpumask irq_affinity_mask; -+ struct notifier_block cpu_hotplug_notifier; -+ int ceiling_irq; -+ struct msm_apm_ctrl_dev *apm; -+ int apm_threshold_volt; -+ int apm_crossover_volt; -+ int apm_adj_volt; -+ enum msm_apm_supply apm_high_supply; -+ enum msm_apm_supply apm_low_supply; -+ int base_volt; -+ u32 corner_switch_delay_time; -+ u32 cpr_clock_rate; -+ u32 sensor_time; -+ u32 loop_time; -+ u32 up_down_delay_time; -+ u32 idle_clocks; -+ u32 step_quot_init_min; -+ u32 step_quot_init_max; -+ int step_volt; -+ u32 down_error_step_limit; -+ u32 up_error_step_limit; -+ enum cpr3_count_mode count_mode; -+ u32 count_repeat; -+ u32 proc_clock_throttle; -+ bool cpr_allowed_hw; -+ bool cpr_allowed_sw; -+ bool supports_hw_closed_loop; -+ bool use_hw_closed_loop; -+ enum cpr_controller_type ctrl_type; -+ bool saw_use_unit_mV; -+ struct cpr3_corner aggr_corner; -+ bool cpr_enabled; -+ bool last_corner_was_closed_loop; -+ bool cpr_suspended; -+ struct dentry *debugfs; -+ -+ int aging_ref_volt; -+ unsigned int aging_vdd_mode; -+ unsigned int aging_complete_vdd_mode; -+ int aging_ref_adjust_volt; -+ bool aging_required; -+ bool aging_succeeded; -+ bool aging_failed; -+ struct cpr3_aging_sensor_info *aging_sensor; -+ int aging_sensor_count; -+ u32 cur_sensor_state; -+ u32 aging_possible_mask; -+ u32 aging_possible_val; -+ -+ u32 step_quot_fixed; -+ u32 initial_temp_band; -+ bool use_dynamic_step_quot; -+ bool allow_core_count_adj; -+ bool allow_temp_adj; -+ bool allow_boost; -+ int temp_band_count; -+ int *temp_points; -+ u32 temp_sensor_id_start; -+ u32 temp_sensor_id_end; -+ u32 voltage_settling_time; -+ enum cpr_setting cpr_global_setting; -+ struct cpr3_panic_regs_info *panic_regs_info; -+ struct notifier_block panic_notifier; -+}; -+ -+/* Used for rounding voltages to the closest physically available set point. */ -+#define CPR3_ROUND(n, d) (DIV_ROUND_UP(n, d) * (d)) -+ -+#define cpr3_err(cpr3_thread, message, ...) \ -+ pr_err("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) -+#define cpr3_info(cpr3_thread, message, ...) \ -+ pr_info("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) -+#define cpr3_debug(cpr3_thread, message, ...) \ -+ pr_debug("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__) -+ -+/* -+ * Offset subtracted from voltage corner values passed in from the regulator -+ * framework in order to get internal voltage corner values. This is needed -+ * since the regulator framework treats 0 as an error value at regulator -+ * registration time. -+ */ -+#define CPR3_CORNER_OFFSET 1 -+ -+#ifdef CONFIG_REGULATOR_CPR3 -+ -+int cpr3_regulator_register(struct platform_device *pdev, -+ struct cpr3_controller *ctrl); -+int cpr3_open_loop_regulator_register(struct platform_device *pdev, -+ struct cpr3_controller *ctrl); -+int cpr3_regulator_unregister(struct cpr3_controller *ctrl); -+int cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl); -+int cpr3_regulator_suspend(struct cpr3_controller *ctrl); -+int cpr3_regulator_resume(struct cpr3_controller *ctrl); -+ -+int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, -+ u32 max_thread_id); -+int cpr3_map_fuse_base(struct cpr3_controller *ctrl, -+ struct platform_device *pdev); -+int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, -+ struct platform_device *pdev, u8 start, u8 end); -+int cpr3_read_fuse_param(void __iomem *fuse_base_addr, -+ const struct cpr3_fuse_param *param, u64 *param_value); -+int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, -+ int fuse_len); -+u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x); -+int cpr3_parse_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out); -+int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out); -+int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out); -+int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg); -+int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, -+ u32 *out_value, u32 value_min, u32 value_max); -+int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, -+ u32 *out_value, u32 value_min, u32 value_max); -+int cpr3_parse_common_thread_data(struct cpr3_thread *thread); -+int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl); -+int cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl); -+int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg); -+void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg); -+int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg); -+void cpr3_print_quots(struct cpr3_regulator *vreg); -+int cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt); -+int cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, -+ int *fuse_volt); -+int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, -+ int *fuse_volt); -+int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg); -+int cpr3_quot_adjustment(int ro_scale, int volt_adjust); -+int cpr3_voltage_adjustment(int ro_scale, int quot_adjust); -+int cpr3_parse_closed_loop_voltage_adjustments(struct cpr3_regulator *vreg, -+ u64 *ro_sel, int *volt_adjust, -+ int *volt_adjust_fuse, int *ro_scale); -+int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg, -+ bool use_corner_band); -+int cpr3_apm_init(struct cpr3_controller *ctrl); -+int cpr3_mem_acc_init(struct cpr3_regulator *vreg); -+void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg); -+void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg); -+int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, -+ int *fuse_volt_adjust); -+int cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, -+ bool is_cold); -+int cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp); -+bool cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg); -+ -+#else -+ -+static inline int cpr3_regulator_register(struct platform_device *pdev, -+ struct cpr3_controller *ctrl) -+{ -+ return -ENXIO; -+} -+ -+static inline int -+cpr3_open_loop_regulator_register(struct platform_device *pdev, -+ struct cpr3_controller *ctrl); -+{ -+ return -ENXIO; -+} -+ -+static inline int cpr3_regulator_unregister(struct cpr3_controller *ctrl) -+{ -+ return -ENXIO; -+} -+ -+static inline int -+cpr3_open_loop_regulator_unregister(struct cpr3_controller *ctrl) -+{ -+ return -ENXIO; -+} -+ -+static inline int cpr3_regulator_suspend(struct cpr3_controller *ctrl) -+{ -+ return -ENXIO; -+} -+ -+static inline int cpr3_regulator_resume(struct cpr3_controller *ctrl) -+{ -+ return -ENXIO; -+} -+ -+static inline int cpr3_get_thread_name(struct cpr3_thread *thread, -+ struct device_node *thread_node) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_allocate_threads(struct cpr3_controller *ctrl, -+ u32 min_thread_id, u32 max_thread_id) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_map_fuse_base(struct cpr3_controller *ctrl, -+ struct platform_device *pdev) -+{ -+ return -ENXIO; -+} -+ -+static inline int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, -+ struct platform_device *pdev, u8 start, u8 end) -+{ -+ return 0; -+} -+ -+static inline int cpr3_read_fuse_param(void __iomem *fuse_base_addr, -+ const struct cpr3_fuse_param *param, u64 *param_value) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_convert_open_loop_voltage_fuse(int ref_volt, -+ int step_volt, u32 fuse, int fuse_len) -+{ -+ return -EPERM; -+} -+ -+static inline u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) -+{ -+ return 0; -+} -+ -+static inline int cpr3_parse_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_corner_band_array_property( -+ struct cpr3_regulator *vreg, const char *prop_name, -+ int tuple_size, u32 *out) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_thread_u32(struct cpr3_thread *thread, -+ const char *propname, u32 *out_value, u32 value_min, -+ u32 value_max) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, -+ const char *propname, u32 *out_value, u32 value_min, -+ u32 value_max) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_common_thread_data(struct cpr3_thread *thread) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) -+{ -+ return -EPERM; -+} -+ -+static inline int -+cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) -+{ -+ return -EPERM; -+} -+ -+static inline void cpr3_open_loop_voltage_as_ceiling( -+ struct cpr3_regulator *vreg) -+{ -+ return; -+} -+ -+static inline int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) -+{ -+ return -EPERM; -+} -+ -+static inline void cpr3_print_quots(struct cpr3_regulator *vreg) -+{ -+ return; -+} -+ -+static inline int -+cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt) -+{ -+ return -EPERM; -+} -+ -+static inline int -+cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, -+ int *fuse_volt) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_adjust_fused_open_loop_voltages( -+ struct cpr3_regulator *vreg, int *fuse_volt) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) -+{ -+ return -EPERM; -+} -+ -+static inline int cpr3_quot_adjustment(int ro_scale, int volt_adjust) -+{ -+ return 0; -+} -+ -+static inline int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) -+{ -+ return 0; -+} -+ -+static inline int cpr3_parse_closed_loop_voltage_adjustments( -+ struct cpr3_regulator *vreg, u64 *ro_sel, -+ int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) -+{ -+ return 0; -+} -+ -+static inline int cpr4_parse_core_count_temp_voltage_adj( -+ struct cpr3_regulator *vreg, bool use_corner_band) -+{ -+ return 0; -+} -+ -+static inline int cpr3_apm_init(struct cpr3_controller *ctrl) -+{ -+ return 0; -+} -+ -+static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg) -+{ -+ return 0; -+} -+ -+static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) -+{ -+} -+ -+static inline void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) -+{ -+} -+ -+static inline int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, -+ int *fuse_volt_adjust) -+{ -+ return 0; -+} -+ -+static inline int -+cpr3_handle_temp_open_loop_adjustment(struct cpr3_controller *ctrl, -+ bool is_cold) -+{ -+ return 0; -+} -+ -+static inline bool -+cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg) -+{ -+ return false; -+} -+ -+static inline int -+cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp) -+{ -+ return 0; -+} -+#endif /* CONFIG_REGULATOR_CPR3 */ -+ -+#endif /* __REGULATOR_CPR_REGULATOR_H__ */ ---- /dev/null -+++ b/drivers/regulator/cpr3-util.c -@@ -0,0 +1,2758 @@ -+/* -+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+/* -+ * This file contains utility functions to be used by platform specific CPR3 -+ * regulator drivers. -+ */ -+ -+#define pr_fmt(fmt) "%s: " fmt, __func__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "cpr3-regulator.h" -+ -+#define BYTES_PER_FUSE_ROW 8 -+#define MAX_FUSE_ROW_BIT 63 -+ -+#define CPR3_CONSECUTIVE_UP_DOWN_MIN 0 -+#define CPR3_CONSECUTIVE_UP_DOWN_MAX 15 -+#define CPR3_UP_DOWN_THRESHOLD_MIN 0 -+#define CPR3_UP_DOWN_THRESHOLD_MAX 31 -+#define CPR3_STEP_QUOT_MIN 0 -+#define CPR3_STEP_QUOT_MAX 63 -+#define CPR3_IDLE_CLOCKS_MIN 0 -+#define CPR3_IDLE_CLOCKS_MAX 31 -+ -+/* This constant has units of uV/mV so 1000 corresponds to 100%. */ -+#define CPR3_AGING_DERATE_UNITY 1000 -+ -+static inline int read_ipq_soc_version_major(void) -+{ -+ const int *prop; -+ prop = of_get_property(of_find_node_by_path("/"), "soc_version_major", -+ NULL); -+ -+ if (!prop) -+ return -EINVAL; -+ -+ return le32_to_cpu(*prop); -+} -+ -+/** -+ * cpr3_allocate_regulators() - allocate and initialize CPR3 regulators for a -+ * given thread based upon device tree data -+ * @thread: Pointer to the CPR3 thread -+ * -+ * This function allocates the thread->vreg array based upon the number of -+ * device tree regulator subnodes. It also initializes generic elements of each -+ * regulator struct such as name, of_node, and thread. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_allocate_regulators(struct cpr3_thread *thread) -+{ -+ struct device_node *node; -+ int i, rc; -+ -+ thread->vreg_count = 0; -+ -+ for_each_available_child_of_node(thread->of_node, node) { -+ thread->vreg_count++; -+ } -+ -+ thread->vreg = devm_kcalloc(thread->ctrl->dev, thread->vreg_count, -+ sizeof(*thread->vreg), GFP_KERNEL); -+ if (!thread->vreg) -+ return -ENOMEM; -+ -+ i = 0; -+ for_each_available_child_of_node(thread->of_node, node) { -+ thread->vreg[i].of_node = node; -+ thread->vreg[i].thread = thread; -+ -+ rc = of_property_read_string(node, "regulator-name", -+ &thread->vreg[i].name); -+ if (rc) { -+ dev_err(thread->ctrl->dev, "could not find regulator name, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ i++; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_allocate_threads() - allocate and initialize CPR3 threads for a given -+ * controller based upon device tree data -+ * @ctrl: Pointer to the CPR3 controller -+ * @min_thread_id: Minimum allowed hardware thread ID for this controller -+ * @max_thread_id: Maximum allowed hardware thread ID for this controller -+ * -+ * This function allocates the ctrl->thread array based upon the number of -+ * device tree thread subnodes. It also initializes generic elements of each -+ * thread struct such as thread_id, of_node, ctrl, and vreg array. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id, -+ u32 max_thread_id) -+{ -+ struct device *dev = ctrl->dev; -+ struct device_node *thread_node; -+ int i, j, rc; -+ -+ ctrl->thread_count = 0; -+ -+ for_each_available_child_of_node(dev->of_node, thread_node) { -+ ctrl->thread_count++; -+ } -+ -+ ctrl->thread = devm_kcalloc(dev, ctrl->thread_count, -+ sizeof(*ctrl->thread), GFP_KERNEL); -+ if (!ctrl->thread) -+ return -ENOMEM; -+ -+ i = 0; -+ for_each_available_child_of_node(dev->of_node, thread_node) { -+ ctrl->thread[i].of_node = thread_node; -+ ctrl->thread[i].ctrl = ctrl; -+ -+ rc = of_property_read_u32(thread_node, "qcom,cpr-thread-id", -+ &ctrl->thread[i].thread_id); -+ if (rc) { -+ dev_err(dev, "could not read DT property qcom,cpr-thread-id, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (ctrl->thread[i].thread_id < min_thread_id || -+ ctrl->thread[i].thread_id > max_thread_id) { -+ dev_err(dev, "invalid thread id = %u; not within [%u, %u]\n", -+ ctrl->thread[i].thread_id, min_thread_id, -+ max_thread_id); -+ return -EINVAL; -+ } -+ -+ /* Verify that the thread ID is unique for all child nodes. */ -+ for (j = 0; j < i; j++) { -+ if (ctrl->thread[j].thread_id -+ == ctrl->thread[i].thread_id) { -+ dev_err(dev, "duplicate thread id = %u found\n", -+ ctrl->thread[i].thread_id); -+ return -EINVAL; -+ } -+ } -+ -+ rc = cpr3_allocate_regulators(&ctrl->thread[i]); -+ if (rc) -+ return rc; -+ -+ i++; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_map_fuse_base() - ioremap the base address of the fuse region -+ * @ctrl: Pointer to the CPR3 controller -+ * @pdev: Platform device pointer for the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_map_fuse_base(struct cpr3_controller *ctrl, -+ struct platform_device *pdev) -+{ -+ struct resource *res; -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse_base"); -+ if (!res || !res->start) { -+ dev_err(&pdev->dev, "fuse base address is missing\n"); -+ return -ENXIO; -+ } -+ -+ ctrl->fuse_base = devm_ioremap(&pdev->dev, res->start, -+ resource_size(res)); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_read_tcsr_setting - reads the CPR setting bits from TCSR register -+ * @ctrl: Pointer to the CPR3 controller -+ * @pdev: Platform device pointer for the CPR3 controller -+ * @start: start bit in TCSR register -+ * @end: end bit in TCSR register -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_read_tcsr_setting(struct cpr3_controller *ctrl, -+ struct platform_device *pdev, u8 start, u8 end) -+{ -+ struct resource *res; -+ void __iomem *tcsr_reg; -+ u32 val; -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ "cpr_tcsr_reg"); -+ if (!res || !res->start) -+ return 0; -+ -+ tcsr_reg = ioremap(res->start, resource_size(res)); -+ if (!tcsr_reg) { -+ dev_err(&pdev->dev, "tcsr ioremap failed\n"); -+ return 0; -+ } -+ -+ val = readl_relaxed(tcsr_reg); -+ val &= GENMASK(end, start); -+ val >>= start; -+ -+ switch (val) { -+ case 1: -+ ctrl->cpr_global_setting = CPR_DISABLED; -+ break; -+ case 2: -+ ctrl->cpr_global_setting = CPR_OPEN_LOOP_EN; -+ break; -+ case 3: -+ ctrl->cpr_global_setting = CPR_CLOSED_LOOP_EN; -+ break; -+ default: -+ ctrl->cpr_global_setting = CPR_DEFAULT; -+ } -+ -+ iounmap(tcsr_reg); -+ -+ return 0; -+} -+ -+/** -+ * cpr3_read_fuse_param() - reads a CPR3 fuse parameter out of eFuses -+ * @fuse_base_addr: Virtual memory address of the eFuse base address -+ * @param: Null terminated array of fuse param segments to read -+ * from -+ * @param_value: Output with value read from the eFuses -+ * -+ * This function reads from each of the parameter segments listed in the param -+ * array and concatenates their values together. Reading stops when an element -+ * is reached which has all 0 struct values. The total number of bits specified -+ * for the fuse parameter across all segments must be less than or equal to 64. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_read_fuse_param(void __iomem *fuse_base_addr, -+ const struct cpr3_fuse_param *param, u64 *param_value) -+{ -+ u64 fuse_val, val; -+ int bits; -+ int bits_total = 0; -+ -+ *param_value = 0; -+ -+ while (param->row || param->bit_start || param->bit_end) { -+ if (param->bit_start > param->bit_end -+ || param->bit_end > MAX_FUSE_ROW_BIT) { -+ pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n", -+ param->row, param->bit_start, param->bit_end); -+ return -EINVAL; -+ } -+ -+ bits = param->bit_end - param->bit_start + 1; -+ if (bits_total + bits > 64) { -+ pr_err("Invalid fuse parameter segments; total bits = %d\n", -+ bits_total + bits); -+ return -EINVAL; -+ } -+ -+ fuse_val = readq_relaxed(fuse_base_addr -+ + param->row * BYTES_PER_FUSE_ROW); -+ val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1); -+ *param_value |= val << bits_total; -+ bits_total += bits; -+ -+ param++; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_convert_open_loop_voltage_fuse() - converts an open loop voltage fuse -+ * value into an absolute voltage with units of microvolts -+ * @ref_volt: Reference voltage in microvolts -+ * @step_volt: The step size in microvolts of the fuse LSB -+ * @fuse: Open loop voltage fuse value -+ * @fuse_len: The bit length of the fuse value -+ * -+ * The MSB of the fuse parameter corresponds to a sign bit. If it is set, then -+ * the lower bits correspond to the number of steps to go down from the -+ * reference voltage. If it is not set, then the lower bits correspond to the -+ * number of steps to go up from the reference voltage. -+ */ -+int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse, -+ int fuse_len) -+{ -+ int sign, steps; -+ -+ sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1; -+ steps = fuse & ((1 << (fuse_len - 1)) - 1); -+ -+ return ref_volt + sign * steps * step_volt; -+} -+ -+/** -+ * cpr3_interpolate() - performs linear interpolation -+ * @x1 Lower known x value -+ * @y1 Lower known y value -+ * @x2 Upper known x value -+ * @y2 Upper known y value -+ * @x Intermediate x value -+ * -+ * Returns y where (x, y) falls on the line between (x1, y1) and (x2, y2). -+ * It is required that x1 < x2, y1 <= y2, and x1 <= x <= x2. If these -+ * conditions are not met, then y2 will be returned. -+ */ -+u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x) -+{ -+ u64 temp; -+ -+ if (x1 >= x2 || y1 > y2 || x1 > x || x > x2) -+ return y2; -+ -+ temp = (x2 - x) * (y2 - y1); -+ do_div(temp, (u32)(x2 - x1)); -+ -+ return y2 - temp; -+} -+ -+/** -+ * cpr3_parse_array_property() - fill an array from a portion of the values -+ * specified for a device tree property -+ * @vreg: Pointer to the CPR3 regulator -+ * @prop_name: The name of the device tree property to read from -+ * @tuple_size: The number of elements in each tuple -+ * @out: Output data array which must be of size tuple_size -+ * -+ * cpr3_parse_common_corner_data() must be called for vreg before this function -+ * is called so that fuse combo and speed bin size elements are initialized. -+ * -+ * Three formats are supported for the device tree property: -+ * 1. Length == tuple_size -+ * (reading begins at index 0) -+ * 2. Length == tuple_size * vreg->fuse_combos_supported -+ * (reading begins at index tuple_size * vreg->fuse_combo) -+ * 3. Length == tuple_size * vreg->speed_bins_supported -+ * (reading begins at index tuple_size * vreg->speed_bin_fuse) -+ * -+ * All other property lengths are treated as errors. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out) -+{ -+ struct device_node *node = vreg->of_node; -+ int len = 0; -+ int i, offset, rc; -+ -+ if (!of_find_property(node, prop_name, &len)) { -+ cpr3_err(vreg, "property %s is missing\n", prop_name); -+ return -EINVAL; -+ } -+ -+ if (len == tuple_size * sizeof(u32)) { -+ offset = 0; -+ } else if (len == tuple_size * vreg->fuse_combos_supported -+ * sizeof(u32)) { -+ offset = tuple_size * vreg->fuse_combo; -+ } else if (vreg->speed_bins_supported > 0 && -+ len == tuple_size * vreg->speed_bins_supported * sizeof(u32)) { -+ offset = tuple_size * vreg->speed_bin_fuse; -+ } else { -+ if (vreg->speed_bins_supported > 0) -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", -+ prop_name, len, -+ tuple_size * sizeof(u32), -+ tuple_size * vreg->speed_bins_supported -+ * sizeof(u32), -+ tuple_size * vreg->fuse_combos_supported -+ * sizeof(u32)); -+ else -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", -+ prop_name, len, -+ tuple_size * sizeof(u32), -+ tuple_size * vreg->fuse_combos_supported -+ * sizeof(u32)); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < tuple_size; i++) { -+ rc = of_property_read_u32_index(node, prop_name, offset + i, -+ &out[i]); -+ if (rc) { -+ cpr3_err(vreg, "error reading property %s, rc=%d\n", -+ prop_name, rc); -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_parse_corner_array_property() - fill a per-corner array from a portion -+ * of the values specified for a device tree property -+ * @vreg: Pointer to the CPR3 regulator -+ * @prop_name: The name of the device tree property to read from -+ * @tuple_size: The number of elements in each per-corner tuple -+ * @out: Output data array which must be of size: -+ * tuple_size * vreg->corner_count -+ * -+ * cpr3_parse_common_corner_data() must be called for vreg before this function -+ * is called so that fuse combo and speed bin size elements are initialized. -+ * -+ * Three formats are supported for the device tree property: -+ * 1. Length == tuple_size * vreg->corner_count -+ * (reading begins at index 0) -+ * 2. Length == tuple_size * vreg->fuse_combo_corner_sum -+ * (reading begins at index tuple_size * vreg->fuse_combo_offset) -+ * 3. Length == tuple_size * vreg->speed_bin_corner_sum -+ * (reading begins at index tuple_size * vreg->speed_bin_offset) -+ * -+ * All other property lengths are treated as errors. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out) -+{ -+ struct device_node *node = vreg->of_node; -+ int len = 0; -+ int i, offset, rc; -+ -+ if (!of_find_property(node, prop_name, &len)) { -+ cpr3_err(vreg, "property %s is missing\n", prop_name); -+ return -EINVAL; -+ } -+ -+ if (len == tuple_size * vreg->corner_count * sizeof(u32)) { -+ offset = 0; -+ } else if (len == tuple_size * vreg->fuse_combo_corner_sum -+ * sizeof(u32)) { -+ offset = tuple_size * vreg->fuse_combo_offset; -+ } else if (vreg->speed_bin_corner_sum > 0 && -+ len == tuple_size * vreg->speed_bin_corner_sum * sizeof(u32)) { -+ offset = tuple_size * vreg->speed_bin_offset; -+ } else { -+ if (vreg->speed_bin_corner_sum > 0) -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", -+ prop_name, len, -+ tuple_size * vreg->corner_count * sizeof(u32), -+ tuple_size * vreg->speed_bin_corner_sum -+ * sizeof(u32), -+ tuple_size * vreg->fuse_combo_corner_sum -+ * sizeof(u32)); -+ else -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", -+ prop_name, len, -+ tuple_size * vreg->corner_count * sizeof(u32), -+ tuple_size * vreg->fuse_combo_corner_sum -+ * sizeof(u32)); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < tuple_size * vreg->corner_count; i++) { -+ rc = of_property_read_u32_index(node, prop_name, offset + i, -+ &out[i]); -+ if (rc) { -+ cpr3_err(vreg, "error reading property %s, rc=%d\n", -+ prop_name, rc); -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_parse_corner_band_array_property() - fill a per-corner band array -+ * from a portion of the values specified for a device tree -+ * property -+ * @vreg: Pointer to the CPR3 regulator -+ * @prop_name: The name of the device tree property to read from -+ * @tuple_size: The number of elements in each per-corner band tuple -+ * @out: Output data array which must be of size: -+ * tuple_size * vreg->corner_band_count -+ * -+ * cpr3_parse_common_corner_data() must be called for vreg before this function -+ * is called so that fuse combo and speed bin size elements are initialized. -+ * In addition, corner band fuse combo and speed bin sum and offset elements -+ * must be initialized prior to executing this function. -+ * -+ * Three formats are supported for the device tree property: -+ * 1. Length == tuple_size * vreg->corner_band_count -+ * (reading begins at index 0) -+ * 2. Length == tuple_size * vreg->fuse_combo_corner_band_sum -+ * (reading begins at index tuple_size * -+ * vreg->fuse_combo_corner_band_offset) -+ * 3. Length == tuple_size * vreg->speed_bin_corner_band_sum -+ * (reading begins at index tuple_size * -+ * vreg->speed_bin_corner_band_offset) -+ * -+ * All other property lengths are treated as errors. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg, -+ const char *prop_name, int tuple_size, u32 *out) -+{ -+ struct device_node *node = vreg->of_node; -+ int len = 0; -+ int i, offset, rc; -+ -+ if (!of_find_property(node, prop_name, &len)) { -+ cpr3_err(vreg, "property %s is missing\n", prop_name); -+ return -EINVAL; -+ } -+ -+ if (len == tuple_size * vreg->corner_band_count * sizeof(u32)) { -+ offset = 0; -+ } else if (len == tuple_size * vreg->fuse_combo_corner_band_sum -+ * sizeof(u32)) { -+ offset = tuple_size * vreg->fuse_combo_corner_band_offset; -+ } else if (vreg->speed_bin_corner_band_sum > 0 && -+ len == tuple_size * vreg->speed_bin_corner_band_sum * -+ sizeof(u32)) { -+ offset = tuple_size * vreg->speed_bin_corner_band_offset; -+ } else { -+ if (vreg->speed_bin_corner_band_sum > 0) -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n", -+ prop_name, len, -+ tuple_size * vreg->corner_band_count * -+ sizeof(u32), -+ tuple_size * vreg->speed_bin_corner_band_sum -+ * sizeof(u32), -+ tuple_size * vreg->fuse_combo_corner_band_sum -+ * sizeof(u32)); -+ else -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", -+ prop_name, len, -+ tuple_size * vreg->corner_band_count * -+ sizeof(u32), -+ tuple_size * vreg->fuse_combo_corner_band_sum -+ * sizeof(u32)); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < tuple_size * vreg->corner_band_count; i++) { -+ rc = of_property_read_u32_index(node, prop_name, offset + i, -+ &out[i]); -+ if (rc) { -+ cpr3_err(vreg, "error reading property %s, rc=%d\n", -+ prop_name, rc); -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_parse_common_corner_data() - parse common CPR3 properties relating to -+ * the corners supported by a CPR3 regulator from device tree -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function reads, validates, and utilizes the following device tree -+ * properties: qcom,cpr-fuse-corners, qcom,cpr-fuse-combos, qcom,cpr-speed-bins, -+ * qcom,cpr-speed-bin-corners, qcom,cpr-corners, qcom,cpr-voltage-ceiling, -+ * qcom,cpr-voltage-floor, qcom,corner-frequencies, -+ * and qcom,cpr-corner-fmax-map. -+ * -+ * It initializes these CPR3 regulator elements: corner, corner_count, -+ * fuse_combos_supported, fuse_corner_map, and speed_bins_supported. It -+ * initializes these elements for each corner: ceiling_volt, floor_volt, -+ * proc_freq, and cpr_fuse_corner. -+ * -+ * It requires that the following CPR3 regulator elements be initialized before -+ * being called: fuse_corner_count, fuse_combo, and speed_bin_fuse. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg) -+{ -+ struct device_node *node = vreg->of_node; -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ u32 max_fuse_combos, fuse_corners, aging_allowed = 0; -+ u32 max_speed_bins = 0; -+ u32 *combo_corners; -+ u32 *speed_bin_corners; -+ u32 *temp; -+ int i, j, rc; -+ -+ rc = of_property_read_u32(node, "qcom,cpr-fuse-corners", &fuse_corners); -+ if (rc) { -+ cpr3_err(vreg, "error reading property qcom,cpr-fuse-corners, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (vreg->fuse_corner_count != fuse_corners) { -+ cpr3_err(vreg, "device tree config supports %d fuse corners but the hardware has %d fuse corners\n", -+ fuse_corners, vreg->fuse_corner_count); -+ return -EINVAL; -+ } -+ -+ rc = of_property_read_u32(node, "qcom,cpr-fuse-combos", -+ &max_fuse_combos); -+ if (rc) { -+ cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ /* -+ * Sanity check against arbitrarily large value to avoid excessive -+ * memory allocation. -+ */ -+ if (max_fuse_combos > 100 || max_fuse_combos == 0) { -+ cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n", -+ max_fuse_combos); -+ return -EINVAL; -+ } -+ -+ if (vreg->fuse_combo >= max_fuse_combos) { -+ cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n", -+ max_fuse_combos - 1, vreg->fuse_combo); -+ BUG_ON(1); -+ return -EINVAL; -+ } -+ -+ vreg->fuse_combos_supported = max_fuse_combos; -+ -+ of_property_read_u32(node, "qcom,cpr-speed-bins", &max_speed_bins); -+ -+ /* -+ * Sanity check against arbitrarily large value to avoid excessive -+ * memory allocation. -+ */ -+ if (max_speed_bins > 100) { -+ cpr3_err(vreg, "qcom,cpr-speed-bins is invalid: %u\n", -+ max_speed_bins); -+ return -EINVAL; -+ } -+ -+ if (max_speed_bins && vreg->speed_bin_fuse >= max_speed_bins) { -+ cpr3_err(vreg, "device tree config supports speed bins 0-%u but the hardware has speed bin %d\n", -+ max_speed_bins - 1, vreg->speed_bin_fuse); -+ BUG(); -+ return -EINVAL; -+ } -+ -+ vreg->speed_bins_supported = max_speed_bins; -+ -+ combo_corners = kcalloc(vreg->fuse_combos_supported, -+ sizeof(*combo_corners), GFP_KERNEL); -+ if (!combo_corners) -+ return -ENOMEM; -+ -+ rc = of_property_read_u32_array(node, "qcom,cpr-corners", combo_corners, -+ vreg->fuse_combos_supported); -+ if (rc == -EOVERFLOW) { -+ /* Single value case */ -+ rc = of_property_read_u32(node, "qcom,cpr-corners", -+ combo_corners); -+ for (i = 1; i < vreg->fuse_combos_supported; i++) -+ combo_corners[i] = combo_corners[0]; -+ } -+ if (rc) { -+ cpr3_err(vreg, "error reading property qcom,cpr-corners, rc=%d\n", -+ rc); -+ kfree(combo_corners); -+ return rc; -+ } -+ -+ vreg->fuse_combo_offset = 0; -+ vreg->fuse_combo_corner_sum = 0; -+ for (i = 0; i < vreg->fuse_combos_supported; i++) { -+ vreg->fuse_combo_corner_sum += combo_corners[i]; -+ if (i < vreg->fuse_combo) -+ vreg->fuse_combo_offset += combo_corners[i]; -+ } -+ -+ vreg->corner_count = combo_corners[vreg->fuse_combo]; -+ -+ kfree(combo_corners); -+ -+ vreg->speed_bin_offset = 0; -+ vreg->speed_bin_corner_sum = 0; -+ if (vreg->speed_bins_supported > 0) { -+ speed_bin_corners = kcalloc(vreg->speed_bins_supported, -+ sizeof(*speed_bin_corners), GFP_KERNEL); -+ if (!speed_bin_corners) -+ return -ENOMEM; -+ -+ rc = of_property_read_u32_array(node, -+ "qcom,cpr-speed-bin-corners", speed_bin_corners, -+ vreg->speed_bins_supported); -+ if (rc) { -+ cpr3_err(vreg, "error reading property qcom,cpr-speed-bin-corners, rc=%d\n", -+ rc); -+ kfree(speed_bin_corners); -+ return rc; -+ } -+ -+ for (i = 0; i < vreg->speed_bins_supported; i++) { -+ vreg->speed_bin_corner_sum += speed_bin_corners[i]; -+ if (i < vreg->speed_bin_fuse) -+ vreg->speed_bin_offset += speed_bin_corners[i]; -+ } -+ -+ if (speed_bin_corners[vreg->speed_bin_fuse] -+ != vreg->corner_count) { -+ cpr3_err(vreg, "qcom,cpr-corners and qcom,cpr-speed-bin-corners conflict on number of corners: %d vs %u\n", -+ vreg->corner_count, -+ speed_bin_corners[vreg->speed_bin_fuse]); -+ kfree(speed_bin_corners); -+ return -EINVAL; -+ } -+ -+ kfree(speed_bin_corners); -+ } -+ -+ vreg->corner = devm_kcalloc(ctrl->dev, vreg->corner_count, -+ sizeof(*vreg->corner), GFP_KERNEL); -+ temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); -+ if (!vreg->corner || !temp) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-ceiling", -+ 1, temp); -+ if (rc) -+ goto free_temp; -+ for (i = 0; i < vreg->corner_count; i++) { -+ vreg->corner[i].ceiling_volt -+ = CPR3_ROUND(temp[i], ctrl->step_volt); -+ vreg->corner[i].abs_ceiling_volt = vreg->corner[i].ceiling_volt; -+ } -+ -+ rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-floor", -+ 1, temp); -+ if (rc) -+ goto free_temp; -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].floor_volt -+ = CPR3_ROUND(temp[i], ctrl->step_volt); -+ -+ /* Validate ceiling and floor values */ -+ for (i = 0; i < vreg->corner_count; i++) { -+ if (vreg->corner[i].floor_volt -+ > vreg->corner[i].ceiling_volt) { -+ cpr3_err(vreg, "CPR floor[%d]=%d > ceiling[%d]=%d uV\n", -+ i, vreg->corner[i].floor_volt, -+ i, vreg->corner[i].ceiling_volt); -+ rc = -EINVAL; -+ goto free_temp; -+ } -+ } -+ -+ /* Load optional system-supply voltages */ -+ if (of_property_present(vreg->of_node, "qcom,system-voltage")) { -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,system-voltage", 1, temp); -+ if (rc) -+ goto free_temp; -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].system_volt = temp[i]; -+ } -+ -+ rc = cpr3_parse_corner_array_property(vreg, "qcom,corner-frequencies", -+ 1, temp); -+ if (rc) -+ goto free_temp; -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].proc_freq = temp[i]; -+ -+ /* Validate frequencies */ -+ for (i = 1; i < vreg->corner_count; i++) { -+ if (vreg->corner[i].proc_freq -+ < vreg->corner[i - 1].proc_freq) { -+ cpr3_err(vreg, "invalid frequency: freq[%d]=%u < freq[%d]=%u\n", -+ i, vreg->corner[i].proc_freq, i - 1, -+ vreg->corner[i - 1].proc_freq); -+ rc = -EINVAL; -+ goto free_temp; -+ } -+ } -+ -+ vreg->fuse_corner_map = devm_kcalloc(ctrl->dev, vreg->fuse_corner_count, -+ sizeof(*vreg->fuse_corner_map), GFP_KERNEL); -+ if (!vreg->fuse_corner_map) { -+ rc = -ENOMEM; -+ goto free_temp; -+ } -+ -+ rc = cpr3_parse_array_property(vreg, "qcom,cpr-corner-fmax-map", -+ vreg->fuse_corner_count, temp); -+ if (rc) -+ goto free_temp; -+ for (i = 0; i < vreg->fuse_corner_count; i++) { -+ vreg->fuse_corner_map[i] = temp[i] - CPR3_CORNER_OFFSET; -+ if (temp[i] < CPR3_CORNER_OFFSET -+ || temp[i] > vreg->corner_count + CPR3_CORNER_OFFSET) { -+ cpr3_err(vreg, "invalid corner value specified in qcom,cpr-corner-fmax-map: %u\n", -+ temp[i]); -+ rc = -EINVAL; -+ goto free_temp; -+ } else if (i > 0 && temp[i - 1] >= temp[i]) { -+ cpr3_err(vreg, "invalid corner %u less than or equal to previous corner %u\n", -+ temp[i], temp[i - 1]); -+ rc = -EINVAL; -+ goto free_temp; -+ } -+ } -+ if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count) -+ cpr3_debug(vreg, "Note: highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n", -+ temp[vreg->fuse_corner_count - 1], -+ vreg->corner_count); -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ for (j = 0; j < vreg->fuse_corner_count; j++) { -+ if (i + CPR3_CORNER_OFFSET <= temp[j]) { -+ vreg->corner[i].cpr_fuse_corner = j; -+ break; -+ } -+ } -+ if (j == vreg->fuse_corner_count) { -+ /* -+ * Handle the case where the highest fuse corner maps -+ * to a corner below the highest corner. -+ */ -+ vreg->corner[i].cpr_fuse_corner -+ = vreg->fuse_corner_count - 1; -+ } -+ } -+ -+ if (of_property_present(vreg->of_node, -+ "qcom,allow-aging-voltage-adjustment")) { -+ rc = cpr3_parse_array_property(vreg, -+ "qcom,allow-aging-voltage-adjustment", -+ 1, &aging_allowed); -+ if (rc) -+ goto free_temp; -+ -+ vreg->aging_allowed = aging_allowed; -+ } -+ -+ if (of_property_present(vreg->of_node, -+ "qcom,allow-aging-open-loop-voltage-adjustment")) { -+ rc = cpr3_parse_array_property(vreg, -+ "qcom,allow-aging-open-loop-voltage-adjustment", -+ 1, &aging_allowed); -+ if (rc) -+ goto free_temp; -+ -+ vreg->aging_allow_open_loop_adj = aging_allowed; -+ } -+ -+ if (vreg->aging_allowed) { -+ if (ctrl->aging_ref_volt <= 0) { -+ cpr3_err(ctrl, "qcom,cpr-aging-ref-voltage must be specified\n"); -+ rc = -EINVAL; -+ goto free_temp; -+ } -+ -+ rc = cpr3_parse_array_property(vreg, -+ "qcom,cpr-aging-max-voltage-adjustment", -+ 1, &vreg->aging_max_adjust_volt); -+ if (rc) -+ goto free_temp; -+ -+ rc = cpr3_parse_array_property(vreg, -+ "qcom,cpr-aging-ref-corner", 1, &vreg->aging_corner); -+ if (rc) { -+ goto free_temp; -+ } else if (vreg->aging_corner < CPR3_CORNER_OFFSET -+ || vreg->aging_corner > vreg->corner_count - 1 -+ + CPR3_CORNER_OFFSET) { -+ cpr3_err(vreg, "aging reference corner=%d not in range [%d, %d]\n", -+ vreg->aging_corner, CPR3_CORNER_OFFSET, -+ vreg->corner_count - 1 + CPR3_CORNER_OFFSET); -+ rc = -EINVAL; -+ goto free_temp; -+ } -+ vreg->aging_corner -= CPR3_CORNER_OFFSET; -+ -+ if (of_property_present(vreg->of_node, "qcom,cpr-aging-derate")) { -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,cpr-aging-derate", 1, temp); -+ if (rc) -+ goto free_temp; -+ -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].aging_derate = temp[i]; -+ } else { -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].aging_derate -+ = CPR3_AGING_DERATE_UNITY; -+ } -+ } -+ -+free_temp: -+ kfree(temp); -+ return rc; -+} -+ -+/** -+ * cpr3_parse_thread_u32() - parse the specified property from the CPR3 thread's -+ * device tree node and verify that it is within the allowed limits -+ * @thread: Pointer to the CPR3 thread -+ * @propname: The name of the device tree property to read -+ * @out_value: The output pointer to fill with the value read -+ * @value_min: The minimum allowed property value -+ * @value_max: The maximum allowed property value -+ * -+ * This function prints a verbose error message if the property is missing or -+ * has a value which is not within the specified range. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname, -+ u32 *out_value, u32 value_min, u32 value_max) -+{ -+ int rc; -+ -+ rc = of_property_read_u32(thread->of_node, propname, out_value); -+ if (rc) { -+ cpr3_err(thread->ctrl, "thread %u error reading property %s, rc=%d\n", -+ thread->thread_id, propname, rc); -+ return rc; -+ } -+ -+ if (*out_value < value_min || *out_value > value_max) { -+ cpr3_err(thread->ctrl, "thread %u %s=%u is invalid; allowed range: [%u, %u]\n", -+ thread->thread_id, propname, *out_value, value_min, -+ value_max); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_parse_ctrl_u32() - parse the specified property from the CPR3 -+ * controller's device tree node and verify that it is within the -+ * allowed limits -+ * @ctrl: Pointer to the CPR3 controller -+ * @propname: The name of the device tree property to read -+ * @out_value: The output pointer to fill with the value read -+ * @value_min: The minimum allowed property value -+ * @value_max: The maximum allowed property value -+ * -+ * This function prints a verbose error message if the property is missing or -+ * has a value which is not within the specified range. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname, -+ u32 *out_value, u32 value_min, u32 value_max) -+{ -+ int rc; -+ -+ rc = of_property_read_u32(ctrl->dev->of_node, propname, out_value); -+ if (rc) { -+ cpr3_err(ctrl, "error reading property %s, rc=%d\n", -+ propname, rc); -+ return rc; -+ } -+ -+ if (*out_value < value_min || *out_value > value_max) { -+ cpr3_err(ctrl, "%s=%u is invalid; allowed range: [%u, %u]\n", -+ propname, *out_value, value_min, value_max); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_parse_common_thread_data() - parse common CPR3 thread properties from -+ * device tree -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_common_thread_data(struct cpr3_thread *thread) -+{ -+ int rc; -+ -+ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-up", -+ &thread->consecutive_up, CPR3_CONSECUTIVE_UP_DOWN_MIN, -+ CPR3_CONSECUTIVE_UP_DOWN_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-down", -+ &thread->consecutive_down, CPR3_CONSECUTIVE_UP_DOWN_MIN, -+ CPR3_CONSECUTIVE_UP_DOWN_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-up-threshold", -+ &thread->up_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, -+ CPR3_UP_DOWN_THRESHOLD_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_thread_u32(thread, "qcom,cpr-down-threshold", -+ &thread->down_threshold, CPR3_UP_DOWN_THRESHOLD_MIN, -+ CPR3_UP_DOWN_THRESHOLD_MAX); -+ if (rc) -+ return rc; -+ -+ return rc; -+} -+ -+/** -+ * cpr3_parse_irq_affinity() - parse CPR IRQ affinity information -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl) -+{ -+ struct device_node *cpu_node; -+ int i, cpu; -+ int len = 0; -+ -+ if (!of_find_property(ctrl->dev->of_node, "qcom,cpr-interrupt-affinity", -+ &len)) { -+ /* No IRQ affinity required */ -+ return 0; -+ } -+ -+ len /= sizeof(u32); -+ -+ for (i = 0; i < len; i++) { -+ cpu_node = of_parse_phandle(ctrl->dev->of_node, -+ "qcom,cpr-interrupt-affinity", i); -+ if (!cpu_node) { -+ cpr3_err(ctrl, "could not find CPU node %d\n", i); -+ return -EINVAL; -+ } -+ -+ for_each_possible_cpu(cpu) { -+ if (of_get_cpu_node(cpu, NULL) == cpu_node) { -+ cpumask_set_cpu(cpu, &ctrl->irq_affinity_mask); -+ break; -+ } -+ } -+ of_node_put(cpu_node); -+ } -+ -+ return 0; -+} -+ -+static int cpr3_panic_notifier_init(struct cpr3_controller *ctrl) -+{ -+ struct device_node *node = ctrl->dev->of_node; -+ struct cpr3_panic_regs_info *panic_regs_info; -+ struct cpr3_reg_info *regs; -+ int i, reg_count, len, rc = 0; -+ -+ if (!of_find_property(node, "qcom,cpr-panic-reg-addr-list", &len)) { -+ /* panic register address list not specified */ -+ return rc; -+ } -+ -+ reg_count = len / sizeof(u32); -+ if (!reg_count) { -+ cpr3_err(ctrl, "qcom,cpr-panic-reg-addr-list has invalid len = %d\n", -+ len); -+ return -EINVAL; -+ } -+ -+ if (!of_property_present(node, "qcom,cpr-panic-reg-name-list")) { -+ cpr3_err(ctrl, "property qcom,cpr-panic-reg-name-list not specified\n"); -+ return -EINVAL; -+ } -+ -+ len = of_property_count_strings(node, "qcom,cpr-panic-reg-name-list"); -+ if (reg_count != len) { -+ cpr3_err(ctrl, "qcom,cpr-panic-reg-name-list should have %d strings\n", -+ reg_count); -+ return -EINVAL; -+ } -+ -+ panic_regs_info = devm_kzalloc(ctrl->dev, sizeof(*panic_regs_info), -+ GFP_KERNEL); -+ if (!panic_regs_info) -+ return -ENOMEM; -+ -+ regs = devm_kcalloc(ctrl->dev, reg_count, sizeof(*regs), GFP_KERNEL); -+ if (!regs) -+ return -ENOMEM; -+ -+ for (i = 0; i < reg_count; i++) { -+ rc = of_property_read_string_index(node, -+ "qcom,cpr-panic-reg-name-list", i, -+ &(regs[i].name)); -+ if (rc) { -+ cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-name-list, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = of_property_read_u32_index(node, -+ "qcom,cpr-panic-reg-addr-list", i, -+ &(regs[i].addr)); -+ if (rc) { -+ cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-addr-list, rc=%d\n", -+ rc); -+ return rc; -+ } -+ regs[i].virt_addr = devm_ioremap(ctrl->dev, regs[i].addr, 0x4); -+ if (!regs[i].virt_addr) { -+ pr_err("Unable to map panic register addr 0x%08x\n", -+ regs[i].addr); -+ return -EINVAL; -+ } -+ regs[i].value = 0xFFFFFFFF; -+ } -+ -+ panic_regs_info->reg_count = reg_count; -+ panic_regs_info->regs = regs; -+ ctrl->panic_regs_info = panic_regs_info; -+ -+ return rc; -+} -+ -+/** -+ * cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from -+ * device tree -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-sensor-time", -+ &ctrl->sensor_time, 0, UINT_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-loop-time", -+ &ctrl->loop_time, 0, UINT_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-idle-cycles", -+ &ctrl->idle_clocks, CPR3_IDLE_CLOCKS_MIN, -+ CPR3_IDLE_CLOCKS_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-min", -+ &ctrl->step_quot_init_min, CPR3_STEP_QUOT_MIN, -+ CPR3_STEP_QUOT_MAX); -+ if (rc) -+ return rc; -+ -+ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-max", -+ &ctrl->step_quot_init_max, CPR3_STEP_QUOT_MIN, -+ CPR3_STEP_QUOT_MAX); -+ if (rc) -+ return rc; -+ -+ rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", -+ &ctrl->step_volt); -+ if (rc) { -+ cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", -+ rc); -+ return rc; -+ } -+ if (ctrl->step_volt <= 0) { -+ cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", -+ ctrl->step_volt); -+ return -EINVAL; -+ } -+ -+ rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-count-mode", -+ &ctrl->count_mode, CPR3_COUNT_MODE_ALL_AT_ONCE_MIN, -+ CPR3_COUNT_MODE_STAGGERED); -+ if (rc) -+ return rc; -+ -+ /* Count repeat is optional */ -+ ctrl->count_repeat = 0; -+ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-count-repeat", -+ &ctrl->count_repeat); -+ -+ ctrl->cpr_allowed_sw = -+ of_property_read_bool(ctrl->dev->of_node, "qcom,cpr-enable") || -+ ctrl->cpr_global_setting == CPR_CLOSED_LOOP_EN; -+ -+ rc = cpr3_parse_irq_affinity(ctrl); -+ if (rc) -+ return rc; -+ -+ /* Aging reference voltage is optional */ -+ ctrl->aging_ref_volt = 0; -+ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage", -+ &ctrl->aging_ref_volt); -+ -+ /* Aging possible bitmask is optional */ -+ ctrl->aging_possible_mask = 0; -+ of_property_read_u32(ctrl->dev->of_node, -+ "qcom,cpr-aging-allowed-reg-mask", -+ &ctrl->aging_possible_mask); -+ -+ if (ctrl->aging_possible_mask) { -+ /* -+ * Aging possible register value required if bitmask is -+ * specified -+ */ -+ rc = cpr3_parse_ctrl_u32(ctrl, -+ "qcom,cpr-aging-allowed-reg-value", -+ &ctrl->aging_possible_val, 0, UINT_MAX); -+ if (rc) -+ return rc; -+ } -+ -+ if (of_property_present(ctrl->dev->of_node, "clock-names")) { -+ ctrl->core_clk = devm_clk_get(ctrl->dev, "core_clk"); -+ if (IS_ERR(ctrl->core_clk)) { -+ rc = PTR_ERR(ctrl->core_clk); -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable request core clock, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ rc = cpr3_panic_notifier_init(ctrl); -+ if (rc) -+ return rc; -+ -+ if (of_property_present(ctrl->dev->of_node, "vdd-supply")) { -+ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); -+ if (IS_ERR(ctrl->vdd_regulator)) { -+ rc = PTR_ERR(ctrl->vdd_regulator); -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } else { -+ cpr3_err(ctrl, "vdd supply is not defined\n"); -+ return -ENODEV; -+ } -+ -+ ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, -+ "system"); -+ if (IS_ERR(ctrl->system_regulator)) { -+ rc = PTR_ERR(ctrl->system_regulator); -+ if (rc != -EPROBE_DEFER) { -+ rc = 0; -+ ctrl->system_regulator = NULL; -+ } else { -+ return rc; -+ } -+ } -+ -+ ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, -+ "mem-acc"); -+ if (IS_ERR(ctrl->mem_acc_regulator)) { -+ rc = PTR_ERR(ctrl->mem_acc_regulator); -+ if (rc != -EPROBE_DEFER) { -+ rc = 0; -+ ctrl->mem_acc_regulator = NULL; -+ } else { -+ return rc; -+ } -+ } -+ -+ return rc; -+} -+ -+/** -+ * cpr3_parse_open_loop_common_ctrl_data() - parse common open loop CPR3 -+ * controller properties from device tree -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_open_loop_common_ctrl_data(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step", -+ &ctrl->step_volt); -+ if (rc) { -+ cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (ctrl->step_volt <= 0) { -+ cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n", -+ ctrl->step_volt); -+ return -EINVAL; -+ } -+ -+ if (of_property_present(ctrl->dev->of_node, "vdd-supply")) { -+ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd"); -+ if (IS_ERR(ctrl->vdd_regulator)) { -+ rc = PTR_ERR(ctrl->vdd_regulator); -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } else { -+ cpr3_err(ctrl, "vdd supply is not defined\n"); -+ return -ENODEV; -+ } -+ -+ ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev, -+ "system"); -+ if (IS_ERR(ctrl->system_regulator)) { -+ rc = PTR_ERR(ctrl->system_regulator); -+ if (rc != -EPROBE_DEFER) { -+ rc = 0; -+ ctrl->system_regulator = NULL; -+ } else { -+ return rc; -+ } -+ } else { -+ rc = regulator_enable(ctrl->system_regulator); -+ } -+ -+ ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev, -+ "mem-acc"); -+ if (IS_ERR(ctrl->mem_acc_regulator)) { -+ rc = PTR_ERR(ctrl->mem_acc_regulator); -+ if (rc != -EPROBE_DEFER) { -+ rc = 0; -+ ctrl->mem_acc_regulator = NULL; -+ } else { -+ return rc; -+ } -+ } -+ -+ return rc; -+} -+ -+/** -+ * cpr3_limit_open_loop_voltages() - modify the open-loop voltage of each corner -+ * so that it fits within the floor to ceiling -+ * voltage range of the corner -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function clips the open-loop voltage for each corner so that it is -+ * limited to the floor to ceiling range. It also rounds each open-loop voltage -+ * so that it corresponds to a set point available to the underlying regulator. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg) -+{ -+ int i, volt; -+ -+ cpr3_debug(vreg, "open-loop voltages after trimming and rounding:\n"); -+ for (i = 0; i < vreg->corner_count; i++) { -+ volt = CPR3_ROUND(vreg->corner[i].open_loop_volt, -+ vreg->thread->ctrl->step_volt); -+ if (volt < vreg->corner[i].floor_volt) -+ volt = vreg->corner[i].floor_volt; -+ else if (volt > vreg->corner[i].ceiling_volt) -+ volt = vreg->corner[i].ceiling_volt; -+ vreg->corner[i].open_loop_volt = volt; -+ cpr3_debug(vreg, "corner[%2d]: open-loop=%d uV\n", i, volt); -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr3_open_loop_voltage_as_ceiling() - configures the ceiling voltage for each -+ * corner to equal the open-loop voltage if the relevant device -+ * tree property is found for the CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function assumes that the the open-loop voltage for each corner has -+ * already been rounded to the nearest allowed set point and that it falls -+ * within the floor to ceiling range. -+ * -+ * Return: none -+ */ -+void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg) -+{ -+ int i; -+ -+ if (!of_property_read_bool(vreg->of_node, -+ "qcom,cpr-scaled-open-loop-voltage-as-ceiling")) -+ return; -+ -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].ceiling_volt -+ = vreg->corner[i].open_loop_volt; -+} -+ -+/** -+ * cpr3_limit_floor_voltages() - raise the floor voltage of each corner so that -+ * the optional maximum floor to ceiling voltage range specified in -+ * device tree is satisfied -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function also ensures that the open-loop voltage for each corner falls -+ * within the final floor to ceiling voltage range and that floor voltages -+ * increase monotonically. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg) -+{ -+ char *prop = "qcom,cpr-floor-to-ceiling-max-range"; -+ int i, floor_new; -+ u32 *floor_range; -+ int rc = 0; -+ -+ if (!of_property_present(vreg->of_node, prop)) -+ goto enforce_monotonicity; -+ -+ floor_range = kcalloc(vreg->corner_count, sizeof(*floor_range), -+ GFP_KERNEL); -+ if (!floor_range) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_corner_array_property(vreg, prop, 1, floor_range); -+ if (rc) -+ goto free_floor_adjust; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ if ((s32)floor_range[i] >= 0) { -+ floor_new = CPR3_ROUND(vreg->corner[i].ceiling_volt -+ - floor_range[i], -+ vreg->thread->ctrl->step_volt); -+ -+ vreg->corner[i].floor_volt = max(floor_new, -+ vreg->corner[i].floor_volt); -+ if (vreg->corner[i].open_loop_volt -+ < vreg->corner[i].floor_volt) -+ vreg->corner[i].open_loop_volt -+ = vreg->corner[i].floor_volt; -+ } -+ } -+ -+free_floor_adjust: -+ kfree(floor_range); -+ -+enforce_monotonicity: -+ /* Ensure that floor voltages increase monotonically. */ -+ for (i = 1; i < vreg->corner_count; i++) { -+ if (vreg->corner[i].floor_volt -+ < vreg->corner[i - 1].floor_volt) { -+ cpr3_debug(vreg, "corner %d floor voltage=%d uV < corner %d voltage=%d uV; overriding: corner %d voltage=%d\n", -+ i, vreg->corner[i].floor_volt, -+ i - 1, vreg->corner[i - 1].floor_volt, -+ i, vreg->corner[i - 1].floor_volt); -+ vreg->corner[i].floor_volt -+ = vreg->corner[i - 1].floor_volt; -+ -+ if (vreg->corner[i].open_loop_volt -+ < vreg->corner[i].floor_volt) -+ vreg->corner[i].open_loop_volt -+ = vreg->corner[i].floor_volt; -+ if (vreg->corner[i].ceiling_volt -+ < vreg->corner[i].floor_volt) -+ vreg->corner[i].ceiling_volt -+ = vreg->corner[i].floor_volt; -+ } -+ } -+ -+ return rc; -+} -+ -+/** -+ * cpr3_print_quots() - print CPR target quotients into the kernel log for -+ * debugging purposes -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: none -+ */ -+void cpr3_print_quots(struct cpr3_regulator *vreg) -+{ -+ int i, j, pos; -+ size_t buflen; -+ char *buf; -+ -+ buflen = sizeof(*buf) * CPR3_RO_COUNT * (MAX_CHARS_PER_INT + 2); -+ buf = kzalloc(buflen, GFP_KERNEL); -+ if (!buf) -+ return; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ for (j = 0, pos = 0; j < CPR3_RO_COUNT; j++) -+ pos += scnprintf(buf + pos, buflen - pos, " %u", -+ vreg->corner[i].target_quot[j]); -+ cpr3_debug(vreg, "target quots[%2d]:%s\n", i, buf); -+ } -+ -+ kfree(buf); -+} -+ -+/** -+ * cpr3_determine_part_type() - determine the part type (SS/TT/FF). -+ * -+ * qcom,cpr-part-types prop tells the number of part types for which correction -+ * voltages are different. Another prop qcom,cpr-parts-voltage will contain the -+ * open loop fuse voltage which will be compared with this part voltage -+ * and accordingly part type will de determined. -+ * -+ * if qcom,cpr-part-types has value n, then qcom,cpr-parts-voltage will be -+ * array of n - 1 elements which will contain the voltage in increasing order. -+ * This function compares the fused volatge with all these voltage and returns -+ * the first index for which the fused volatge is greater. -+ * -+ * @vreg: Pointer to the CPR3 regulator -+ * @fuse_volt: fused open loop voltage which will be compared with -+ * qcom,cpr-parts-voltage array -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_determine_part_type(struct cpr3_regulator *vreg, int fuse_volt) -+{ -+ int i, rc, len; -+ u32 volt; -+ int soc_version_major; -+ char prop_name[100]; -+ const char prop_name_def[] = "qcom,cpr-parts-voltage"; -+ const char prop_name_v2[] = "qcom,cpr-parts-voltage-v2"; -+ -+ soc_version_major = read_ipq_soc_version_major(); -+ BUG_ON(soc_version_major <= 0); -+ -+ if (of_property_read_u32(vreg->of_node, "qcom,cpr-part-types", -+ &vreg->part_type_supported)) -+ return 0; -+ -+ if (soc_version_major > 1) -+ strscpy(prop_name, prop_name_v2, sizeof(prop_name_v2)); -+ else -+ strscpy(prop_name, prop_name_def, sizeof(prop_name_def)); -+ -+ if (!of_find_property(vreg->of_node, prop_name, &len)) { -+ cpr3_err(vreg, "property %s is missing\n", prop_name); -+ return -EINVAL; -+ } -+ -+ if (len != (vreg->part_type_supported - 1) * sizeof(u32)) { -+ cpr3_err(vreg, "wrong len in qcom,cpr-parts-voltage\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < vreg->part_type_supported - 1; i++) { -+ rc = of_property_read_u32_index(vreg->of_node, -+ prop_name, i, &volt); -+ if (rc) { -+ cpr3_err(vreg, "error reading property %s, rc=%d\n", -+ prop_name, rc); -+ return rc; -+ } -+ -+ if (fuse_volt < volt) -+ break; -+ } -+ -+ vreg->part_type = i; -+ return 0; -+} -+ -+int cpr3_determine_temp_base_open_loop_correction(struct cpr3_regulator *vreg, -+ int *fuse_volt) -+{ -+ int i, rc, prev_volt; -+ int *volt_adjust; -+ char prop_str[75]; -+ int soc_version_major = read_ipq_soc_version_major(); -+ -+ BUG_ON(soc_version_major <= 0); -+ -+ if (vreg->part_type_supported) { -+ if (soc_version_major > 1) -+ snprintf(prop_str, sizeof(prop_str), -+ "qcom,cpr-cold-temp-voltage-adjustment-v2-%d", -+ vreg->part_type); -+ else -+ snprintf(prop_str, sizeof(prop_str), -+ "qcom,cpr-cold-temp-voltage-adjustment-%d", -+ vreg->part_type); -+ } else { -+ strscpy(prop_str, "qcom,cpr-cold-temp-voltage-adjustment", -+ sizeof(prop_str)); -+ } -+ -+ if (!of_property_present(vreg->of_node, prop_str)) { -+ /* No adjustment required. */ -+ cpr3_info(vreg, "No cold temperature adjustment required.\n"); -+ return 0; -+ } -+ -+ volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), -+ GFP_KERNEL); -+ if (!volt_adjust) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_array_property(vreg, prop_str, -+ vreg->fuse_corner_count, volt_adjust); -+ if (rc) { -+ cpr3_err(vreg, "could not load cold temp voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->fuse_corner_count; i++) { -+ if (volt_adjust[i]) { -+ prev_volt = fuse_volt[i]; -+ fuse_volt[i] += volt_adjust[i]; -+ cpr3_debug(vreg, -+ "adjusted fuse corner %d open-loop voltage: %d -> %d uV\n", -+ i, prev_volt, fuse_volt[i]); -+ } -+ } -+ -+done: -+ kfree(volt_adjust); -+ return rc; -+} -+ -+/** -+ * cpr3_can_adjust_cold_temp() - Is cold temperature adjustment available -+ * -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function checks the cold temperature threshold is available -+ * -+ * Return: true on cold temperature threshold is available, else false -+ */ -+bool cpr3_can_adjust_cold_temp(struct cpr3_regulator *vreg) -+{ -+ char prop_str[75]; -+ int soc_version_major = read_ipq_soc_version_major(); -+ -+ BUG_ON(soc_version_major <= 0); -+ -+ if (soc_version_major > 1) -+ strscpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", -+ sizeof(prop_str)); -+ else -+ strscpy(prop_str, "qcom,cpr-cold-temp-threshold", -+ sizeof(prop_str)); -+ -+ if (!of_property_present(vreg->of_node, prop_str)) { -+ /* No adjustment required. */ -+ return false; -+ } else -+ return true; -+} -+ -+/** -+ * cpr3_get_cold_temp_threshold() - get cold temperature threshold -+ * -+ * @vreg: Pointer to the CPR3 regulator -+ * @cold_temp: cold temperature read. -+ * -+ * This function reads the cold temperature threshold below which -+ * cold temperature adjustment margins will be applied. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_get_cold_temp_threshold(struct cpr3_regulator *vreg, int *cold_temp) -+{ -+ int rc; -+ u32 temp; -+ char req_prop_str[75], prop_str[75]; -+ int soc_version_major = read_ipq_soc_version_major(); -+ -+ BUG_ON(soc_version_major <= 0); -+ -+ if (vreg->part_type_supported) { -+ if (soc_version_major > 1) -+ snprintf(req_prop_str, sizeof(req_prop_str), -+ "qcom,cpr-cold-temp-voltage-adjustment-v2-%d", -+ vreg->part_type); -+ else -+ snprintf(req_prop_str, sizeof(req_prop_str), -+ "qcom,cpr-cold-temp-voltage-adjustment-%d", -+ vreg->part_type); -+ } else { -+ strscpy(req_prop_str, "qcom,cpr-cold-temp-voltage-adjustment", -+ sizeof(req_prop_str)); -+ } -+ -+ if (soc_version_major > 1) -+ strscpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", -+ sizeof(prop_str)); -+ else -+ strscpy(prop_str, "qcom,cpr-cold-temp-threshold", -+ sizeof(prop_str)); -+ -+ if (!of_property_present(vreg->of_node, req_prop_str)) { -+ /* No adjustment required. */ -+ cpr3_info(vreg, "Cold temperature adjustment not required.\n"); -+ return 0; -+ } -+ -+ if (!of_property_present(vreg->of_node, prop_str)) { -+ /* No adjustment required. */ -+ cpr3_err(vreg, "Missing %s required for %s\n", -+ prop_str, req_prop_str); -+ return -EINVAL; -+ } -+ -+ rc = of_property_read_u32(vreg->of_node, prop_str, &temp); -+ if (rc) { -+ cpr3_err(vreg, "error reading property %s, rc=%d\n", -+ prop_str, rc); -+ return rc; -+ } -+ -+ *cold_temp = temp; -+ return 0; -+} -+ -+/** -+ * cpr3_adjust_fused_open_loop_voltages() - adjust the fused open-loop voltages -+ * for each fuse corner according to device tree values -+ * @vreg: Pointer to the CPR3 regulator -+ * @fuse_volt: Pointer to an array of the fused open-loop voltage -+ * values -+ * -+ * Voltage values in fuse_volt are modified in place. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg, -+ int *fuse_volt) -+{ -+ int i, rc, prev_volt; -+ int *volt_adjust; -+ char prop_str[75]; -+ int soc_version_major = read_ipq_soc_version_major(); -+ -+ BUG_ON(soc_version_major <= 0); -+ -+ if (vreg->part_type_supported) { -+ if (soc_version_major > 1) -+ snprintf(prop_str, sizeof(prop_str), -+ "qcom,cpr-open-loop-voltage-fuse-adjustment-v2-%d", -+ vreg->part_type); -+ else -+ snprintf(prop_str, sizeof(prop_str), -+ "qcom,cpr-open-loop-voltage-fuse-adjustment-%d", -+ vreg->part_type); -+ } else { -+ strscpy(prop_str, "qcom,cpr-open-loop-voltage-fuse-adjustment", -+ sizeof(prop_str)); -+ } -+ -+ if (!of_property_present(vreg->of_node, prop_str)) { -+ /* No adjustment required. */ -+ return 0; -+ } -+ -+ volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust), -+ GFP_KERNEL); -+ if (!volt_adjust) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_array_property(vreg, -+ prop_str, vreg->fuse_corner_count, volt_adjust); -+ if (rc) { -+ cpr3_err(vreg, "could not load open-loop fused voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->fuse_corner_count; i++) { -+ if (volt_adjust[i]) { -+ prev_volt = fuse_volt[i]; -+ fuse_volt[i] += volt_adjust[i]; -+ cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n", -+ i, prev_volt, fuse_volt[i]); -+ } -+ } -+ -+done: -+ kfree(volt_adjust); -+ return rc; -+} -+ -+/** -+ * cpr3_adjust_open_loop_voltages() - adjust the open-loop voltages for each -+ * corner according to device tree values -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg) -+{ -+ int i, rc, prev_volt, min_volt; -+ int *volt_adjust, *volt_diff; -+ -+ if (!of_property_present(vreg->of_node, -+ "qcom,cpr-open-loop-voltage-adjustment")) { -+ /* No adjustment required. */ -+ return 0; -+ } -+ -+ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), -+ GFP_KERNEL); -+ volt_diff = kcalloc(vreg->corner_count, sizeof(*volt_diff), GFP_KERNEL); -+ if (!volt_adjust || !volt_diff) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,cpr-open-loop-voltage-adjustment", 1, volt_adjust); -+ if (rc) { -+ cpr3_err(vreg, "could not load open-loop voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ if (volt_adjust[i]) { -+ prev_volt = vreg->corner[i].open_loop_volt; -+ vreg->corner[i].open_loop_volt += volt_adjust[i]; -+ cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n", -+ i, prev_volt, vreg->corner[i].open_loop_volt); -+ } -+ } -+ -+ if (of_property_present(vreg->of_node, -+ "qcom,cpr-open-loop-voltage-min-diff")) { -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,cpr-open-loop-voltage-min-diff", 1, volt_diff); -+ if (rc) { -+ cpr3_err(vreg, "could not load minimum open-loop voltage differences, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ /* -+ * Ensure that open-loop voltages increase monotonically with respect -+ * to configurable minimum allowed differences. -+ */ -+ for (i = 1; i < vreg->corner_count; i++) { -+ min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i]; -+ if (vreg->corner[i].open_loop_volt < min_volt) { -+ cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n", -+ i, vreg->corner[i].open_loop_volt, -+ i - 1, vreg->corner[i - 1].open_loop_volt, -+ volt_diff[i], i, min_volt); -+ vreg->corner[i].open_loop_volt = min_volt; -+ } -+ } -+ -+done: -+ kfree(volt_diff); -+ kfree(volt_adjust); -+ return rc; -+} -+ -+/** -+ * cpr3_quot_adjustment() - returns the quotient adjustment value resulting from -+ * the specified voltage adjustment and RO scaling factor -+ * @ro_scale: The CPR ring oscillator (RO) scaling factor with units -+ * of QUOT/V -+ * @volt_adjust: The amount to adjust the voltage by in units of -+ * microvolts. This value may be positive or negative. -+ */ -+int cpr3_quot_adjustment(int ro_scale, int volt_adjust) -+{ -+ unsigned long long temp; -+ int quot_adjust; -+ int sign = 1; -+ -+ if (ro_scale < 0) { -+ sign = -sign; -+ ro_scale = -ro_scale; -+ } -+ -+ if (volt_adjust < 0) { -+ sign = -sign; -+ volt_adjust = -volt_adjust; -+ } -+ -+ temp = (unsigned long long)ro_scale * (unsigned long long)volt_adjust; -+ do_div(temp, 1000000); -+ -+ quot_adjust = temp; -+ quot_adjust *= sign; -+ -+ return quot_adjust; -+} -+ -+/** -+ * cpr3_voltage_adjustment() - returns the voltage adjustment value resulting -+ * from the specified quotient adjustment and RO scaling factor -+ * @ro_scale: The CPR ring oscillator (RO) scaling factor with units -+ * of QUOT/V -+ * @quot_adjust: The amount to adjust the quotient by in units of -+ * QUOT. This value may be positive or negative. -+ */ -+int cpr3_voltage_adjustment(int ro_scale, int quot_adjust) -+{ -+ unsigned long long temp; -+ int volt_adjust; -+ int sign = 1; -+ -+ if (ro_scale < 0) { -+ sign = -sign; -+ ro_scale = -ro_scale; -+ } -+ -+ if (quot_adjust < 0) { -+ sign = -sign; -+ quot_adjust = -quot_adjust; -+ } -+ -+ if (ro_scale == 0) -+ return 0; -+ -+ temp = (unsigned long long)quot_adjust * 1000000; -+ do_div(temp, ro_scale); -+ -+ volt_adjust = temp; -+ volt_adjust *= sign; -+ -+ return volt_adjust; -+} -+ -+/** -+ * cpr3_parse_closed_loop_voltage_adjustments() - load per-fuse-corner and -+ * per-corner closed-loop adjustment values from device tree -+ * @vreg: Pointer to the CPR3 regulator -+ * @ro_sel: Array of ring oscillator values selected for each -+ * fuse corner -+ * @volt_adjust: Pointer to array which will be filled with the -+ * per-corner closed-loop adjustment voltages -+ * @volt_adjust_fuse: Pointer to array which will be filled with the -+ * per-fuse-corner closed-loop adjustment voltages -+ * @ro_scale: Pointer to array which will be filled with the -+ * per-fuse-corner RO scaling factor values with units of -+ * QUOT/V -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_parse_closed_loop_voltage_adjustments( -+ struct cpr3_regulator *vreg, u64 *ro_sel, -+ int *volt_adjust, int *volt_adjust_fuse, int *ro_scale) -+{ -+ int i, rc; -+ u32 *ro_all_scale; -+ -+ char volt_adj[] = "qcom,cpr-closed-loop-voltage-adjustment"; -+ char volt_fuse_adj[] = "qcom,cpr-closed-loop-voltage-fuse-adjustment"; -+ char ro_scaling[] = "qcom,cpr-ro-scaling-factor"; -+ -+ if (!of_property_present(vreg->of_node, volt_adj) -+ && !of_property_present(vreg->of_node, volt_fuse_adj) -+ && !vreg->aging_allowed) { -+ /* No adjustment required. */ -+ return 0; -+ } else if (!of_property_present(vreg->of_node, ro_scaling)) { -+ cpr3_err(vreg, "Missing %s required for closed-loop voltage adjustment.\n", -+ ro_scaling); -+ return -EINVAL; -+ } -+ -+ ro_all_scale = kcalloc(vreg->fuse_corner_count * CPR3_RO_COUNT, -+ sizeof(*ro_all_scale), GFP_KERNEL); -+ if (!ro_all_scale) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_array_property(vreg, ro_scaling, -+ vreg->fuse_corner_count * CPR3_RO_COUNT, ro_all_scale); -+ if (rc) { -+ cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->fuse_corner_count; i++) -+ ro_scale[i] = ro_all_scale[i * CPR3_RO_COUNT + ro_sel[i]]; -+ -+ for (i = 0; i < vreg->corner_count; i++) -+ memcpy(vreg->corner[i].ro_scale, -+ &ro_all_scale[vreg->corner[i].cpr_fuse_corner * CPR3_RO_COUNT], -+ sizeof(*ro_all_scale) * CPR3_RO_COUNT); -+ -+ if (of_property_present(vreg->of_node, volt_fuse_adj)) { -+ rc = cpr3_parse_array_property(vreg, volt_fuse_adj, -+ vreg->fuse_corner_count, volt_adjust_fuse); -+ if (rc) { -+ cpr3_err(vreg, "could not load closed-loop fused voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ if (of_property_present(vreg->of_node, volt_adj)) { -+ rc = cpr3_parse_corner_array_property(vreg, volt_adj, -+ 1, volt_adjust); -+ if (rc) { -+ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+done: -+ kfree(ro_all_scale); -+ return rc; -+} -+ -+/** -+ * cpr3_apm_init() - initialize APM data for a CPR3 controller -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * This function loads memory array power mux (APM) data from device tree -+ * if it is present and requests a handle to the appropriate APM controller -+ * device. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_apm_init(struct cpr3_controller *ctrl) -+{ -+ struct device_node *node = ctrl->dev->of_node; -+ int rc; -+ -+ if (!of_property_present(node, "qcom,apm-ctrl")) { -+ /* No APM used */ -+ return 0; -+ } -+ -+ ctrl->apm = msm_apm_ctrl_dev_get(ctrl->dev); -+ if (IS_ERR(ctrl->apm)) { -+ rc = PTR_ERR(ctrl->apm); -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "APM get failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ rc = of_property_read_u32(node, "qcom,apm-threshold-voltage", -+ &ctrl->apm_threshold_volt); -+ if (rc) { -+ cpr3_err(ctrl, "error reading qcom,apm-threshold-voltage, rc=%d\n", -+ rc); -+ return rc; -+ } -+ ctrl->apm_threshold_volt -+ = CPR3_ROUND(ctrl->apm_threshold_volt, ctrl->step_volt); -+ -+ /* No error check since this is an optional property. */ -+ of_property_read_u32(node, "qcom,apm-hysteresis-voltage", -+ &ctrl->apm_adj_volt); -+ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); -+ -+ ctrl->apm_high_supply = MSM_APM_SUPPLY_APCC; -+ ctrl->apm_low_supply = MSM_APM_SUPPLY_MX; -+ -+ return 0; -+} -+ -+/** -+ * cpr3_mem_acc_init() - initialize mem-acc regulator data for -+ * a CPR3 regulator -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_mem_acc_init(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ u32 *temp; -+ int i, rc; -+ -+ if (!ctrl->mem_acc_regulator) { -+ cpr3_info(ctrl, "not using memory accelerator regulator\n"); -+ return 0; -+ } -+ -+ temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL); -+ if (!temp) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_corner_array_property(vreg, "qcom,mem-acc-voltage", -+ 1, temp); -+ if (rc) { -+ cpr3_err(ctrl, "could not load mem-acc corners, rc=%d\n", rc); -+ } else { -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].mem_acc_volt = temp[i]; -+ } -+ -+ kfree(temp); -+ return rc; -+} -+ -+/** -+ * cpr4_load_core_and_temp_adj() - parse amount of voltage adjustment for -+ * per-online-core and per-temperature voltage adjustment for a -+ * given corner or corner band from device tree. -+ * @vreg: Pointer to the CPR3 regulator -+ * @num: Corner number or corner band number -+ * @use_corner_band: Boolean indicating if the CPR3 regulator supports -+ * adjustments per corner band -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_load_core_and_temp_adj(struct cpr3_regulator *vreg, -+ int num, bool use_corner_band) -+{ -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ struct cpr4_sdelta *sdelta; -+ int sdelta_size, i, j, pos, rc = 0; -+ char str[75]; -+ size_t buflen; -+ char *buf; -+ -+ sdelta = use_corner_band ? vreg->corner_band[num].sdelta : -+ vreg->corner[num].sdelta; -+ -+ if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) { -+ /* corner doesn't need sdelta table */ -+ sdelta->max_core_count = 0; -+ sdelta->temp_band_count = 0; -+ return rc; -+ } -+ -+ sdelta_size = sdelta->max_core_count * sdelta->temp_band_count; -+ if (use_corner_band) -+ snprintf(str, sizeof(str), -+ "corner_band=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", -+ num, sdelta->max_core_count, -+ sdelta->temp_band_count, sdelta_size); -+ else -+ snprintf(str, sizeof(str), -+ "corner=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n", -+ num, sdelta->max_core_count, -+ sdelta->temp_band_count, sdelta_size); -+ -+ cpr3_debug(vreg, "%s", str); -+ -+ sdelta->table = devm_kcalloc(ctrl->dev, sdelta_size, -+ sizeof(*sdelta->table), GFP_KERNEL); -+ if (!sdelta->table) -+ return -ENOMEM; -+ -+ if (use_corner_band) -+ snprintf(str, sizeof(str), -+ "qcom,cpr-corner-band%d-temp-core-voltage-adjustment", -+ num + CPR3_CORNER_OFFSET); -+ else -+ snprintf(str, sizeof(str), -+ "qcom,cpr-corner%d-temp-core-voltage-adjustment", -+ num + CPR3_CORNER_OFFSET); -+ -+ rc = cpr3_parse_array_property(vreg, str, sdelta_size, -+ sdelta->table); -+ if (rc) { -+ cpr3_err(vreg, "could not load %s, rc=%d\n", str, rc); -+ return rc; -+ } -+ -+ /* -+ * Convert sdelta margins from uV to PMIC steps and apply negation to -+ * follow the SDELTA register semantics. -+ */ -+ for (i = 0; i < sdelta_size; i++) -+ sdelta->table[i] = -(sdelta->table[i] / ctrl->step_volt); -+ -+ buflen = sizeof(*buf) * sdelta_size * (MAX_CHARS_PER_INT + 2); -+ buf = kzalloc(buflen, GFP_KERNEL); -+ if (!buf) -+ return rc; -+ -+ for (i = 0; i < sdelta->max_core_count; i++) { -+ for (j = 0, pos = 0; j < sdelta->temp_band_count; j++) -+ pos += scnprintf(buf + pos, buflen - pos, " %u", -+ sdelta->table[i * sdelta->temp_band_count + j]); -+ cpr3_debug(vreg, "sdelta[%d]:%s\n", i, buf); -+ } -+ -+ kfree(buf); -+ return rc; -+} -+ -+/** -+ * cpr4_parse_core_count_temp_voltage_adj() - parse configuration data for -+ * per-online-core and per-temperature voltage adjustment for -+ * a CPR3 regulator from device tree. -+ * @vreg: Pointer to the CPR3 regulator -+ * @use_corner_band: Boolean indicating if the CPR3 regulator supports -+ * adjustments per corner band -+ * -+ * This function supports parsing of per-online-core and per-temperature -+ * adjustments per corner or per corner band. CPR controllers which support -+ * corner bands apply the same adjustments to all corners within a corner band. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr4_parse_core_count_temp_voltage_adj( -+ struct cpr3_regulator *vreg, bool use_corner_band) -+{ -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ struct device_node *node = vreg->of_node; -+ struct cpr3_corner *corner; -+ struct cpr4_sdelta *sdelta; -+ int i, sdelta_table_count, rc = 0; -+ int *allow_core_count_adj = NULL, *allow_temp_adj = NULL; -+ char prop_str[75]; -+ -+ if (of_property_present(node, use_corner_band ? -+ "qcom,corner-band-allow-temp-adjustment" -+ : "qcom,corner-allow-temp-adjustment")) { -+ if (!ctrl->allow_temp_adj) { -+ cpr3_err(ctrl, "Temperature adjustment configurations missing\n"); -+ return -EINVAL; -+ } -+ -+ vreg->allow_temp_adj = true; -+ } -+ -+ if (of_property_present(node, use_corner_band ? -+ "qcom,corner-band-allow-core-count-adjustment" -+ : "qcom,corner-allow-core-count-adjustment")) { -+ rc = of_property_read_u32(node, "qcom,max-core-count", -+ &vreg->max_core_count); -+ if (rc) { -+ cpr3_err(vreg, "error reading qcom,max-core-count, rc=%d\n", -+ rc); -+ return -EINVAL; -+ } -+ -+ vreg->allow_core_count_adj = true; -+ ctrl->allow_core_count_adj = true; -+ } -+ -+ if (!vreg->allow_temp_adj && !vreg->allow_core_count_adj) { -+ /* -+ * Both per-online-core and temperature based adjustments are -+ * disabled for this regulator. -+ */ -+ return 0; -+ } else if (!vreg->allow_core_count_adj) { -+ /* -+ * Only per-temperature voltage adjusments are allowed. -+ * Keep max core count value as 1 to allocate SDELTA. -+ */ -+ vreg->max_core_count = 1; -+ } -+ -+ if (vreg->allow_core_count_adj) { -+ allow_core_count_adj = kcalloc(vreg->corner_count, -+ sizeof(*allow_core_count_adj), -+ GFP_KERNEL); -+ if (!allow_core_count_adj) -+ return -ENOMEM; -+ -+ snprintf(prop_str, sizeof(prop_str), "%s", use_corner_band ? -+ "qcom,corner-band-allow-core-count-adjustment" : -+ "qcom,corner-allow-core-count-adjustment"); -+ -+ rc = use_corner_band ? -+ cpr3_parse_corner_band_array_property(vreg, prop_str, -+ 1, allow_core_count_adj) : -+ cpr3_parse_corner_array_property(vreg, prop_str, -+ 1, allow_core_count_adj); -+ if (rc) { -+ cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, -+ rc); -+ goto done; -+ } -+ } -+ -+ if (vreg->allow_temp_adj) { -+ allow_temp_adj = kcalloc(vreg->corner_count, -+ sizeof(*allow_temp_adj), GFP_KERNEL); -+ if (!allow_temp_adj) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ snprintf(prop_str, sizeof(prop_str), "%s", use_corner_band ? -+ "qcom,corner-band-allow-temp-adjustment" : -+ "qcom,corner-allow-temp-adjustment"); -+ -+ rc = use_corner_band ? -+ cpr3_parse_corner_band_array_property(vreg, prop_str, -+ 1, allow_temp_adj) : -+ cpr3_parse_corner_array_property(vreg, prop_str, -+ 1, allow_temp_adj); -+ if (rc) { -+ cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str, -+ rc); -+ goto done; -+ } -+ } -+ -+ sdelta_table_count = use_corner_band ? vreg->corner_band_count : -+ vreg->corner_count; -+ -+ for (i = 0; i < sdelta_table_count; i++) { -+ sdelta = devm_kzalloc(ctrl->dev, sizeof(*corner->sdelta), -+ GFP_KERNEL); -+ if (!sdelta) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ if (allow_core_count_adj) -+ sdelta->allow_core_count_adj = allow_core_count_adj[i]; -+ if (allow_temp_adj) -+ sdelta->allow_temp_adj = allow_temp_adj[i]; -+ sdelta->max_core_count = vreg->max_core_count; -+ sdelta->temp_band_count = ctrl->temp_band_count; -+ -+ if (use_corner_band) -+ vreg->corner_band[i].sdelta = sdelta; -+ else -+ vreg->corner[i].sdelta = sdelta; -+ -+ rc = cpr4_load_core_and_temp_adj(vreg, i, use_corner_band); -+ if (rc) { -+ cpr3_err(vreg, "corner/band %d core and temp adjustment loading failed, rc=%d\n", -+ i, rc); -+ goto done; -+ } -+ } -+ -+done: -+ kfree(allow_core_count_adj); -+ kfree(allow_temp_adj); -+ -+ return rc; -+} -+ -+/** -+ * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages -+ * so that they do not overlap the APM threshold voltage. -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * The memory array power mux (APM) must be configured for a specific supply -+ * based upon where the VDD voltage lies with respect to the APM threshold -+ * voltage. When using CPR hardware closed-loop, the voltage may vary anywhere -+ * between the floor and ceiling voltage without software notification. -+ * Therefore, it is required that the floor to ceiling range for every corner -+ * not intersect the APM threshold voltage. This function adjusts the floor to -+ * ceiling range for each corner which violates this requirement. -+ * -+ * The following algorithm is applied: -+ * if floor < threshold <= ceiling: -+ * if open_loop >= threshold, then floor = threshold - adj -+ * else ceiling = threshold - step -+ * where: -+ * adj = APM hysteresis voltage established to minimize the number of -+ * corners with artificially increased floor voltages -+ * step = voltage in microvolts of a single step of the VDD supply -+ * -+ * The open-loop voltage is also bounded by the new floor or ceiling value as -+ * needed. -+ * -+ * Return: none -+ */ -+void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ struct cpr3_corner *corner; -+ int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop; -+ -+ if (!ctrl->apm_threshold_volt) { -+ /* APM not being used. */ -+ return; -+ } -+ -+ ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt, -+ ctrl->step_volt); -+ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt); -+ -+ threshold = ctrl->apm_threshold_volt; -+ adj = ctrl->apm_adj_volt; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ corner = &vreg->corner[i]; -+ -+ if (threshold <= corner->floor_volt -+ || threshold > corner->ceiling_volt) -+ continue; -+ -+ prev_floor = corner->floor_volt; -+ prev_ceiling = corner->ceiling_volt; -+ prev_open_loop = corner->open_loop_volt; -+ -+ if (corner->open_loop_volt >= threshold) { -+ corner->floor_volt = max(corner->floor_volt, -+ threshold - adj); -+ if (corner->open_loop_volt < corner->floor_volt) -+ corner->open_loop_volt = corner->floor_volt; -+ } else { -+ corner->ceiling_volt = threshold - ctrl->step_volt; -+ } -+ -+ if (corner->floor_volt != prev_floor -+ || corner->ceiling_volt != prev_ceiling -+ || corner->open_loop_volt != prev_open_loop) -+ cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", -+ threshold, adj, i, prev_floor, prev_ceiling, -+ prev_open_loop, corner->floor_volt, -+ corner->ceiling_volt, corner->open_loop_volt); -+ } -+} -+ -+/** -+ * cprh_adjust_voltages_for_mem_acc() - adjust per-corner floor and ceiling -+ * voltages so that they do not intersect the MEM ACC threshold -+ * voltage -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * The following algorithm is applied: -+ * if floor < threshold <= ceiling: -+ * if open_loop >= threshold, then floor = threshold -+ * else ceiling = threshold - step -+ * where: -+ * step = voltage in microvolts of a single step of the VDD supply -+ * -+ * The open-loop voltage is also bounded by the new floor or ceiling value as -+ * needed. -+ * -+ * Return: none -+ */ -+void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ struct cpr3_corner *corner; -+ int i, threshold, prev_ceiling, prev_floor, prev_open_loop; -+ -+ if (!ctrl->mem_acc_threshold_volt) { -+ /* MEM ACC not being used. */ -+ return; -+ } -+ -+ ctrl->mem_acc_threshold_volt = CPR3_ROUND(ctrl->mem_acc_threshold_volt, -+ ctrl->step_volt); -+ -+ threshold = ctrl->mem_acc_threshold_volt; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ corner = &vreg->corner[i]; -+ -+ if (threshold <= corner->floor_volt -+ || threshold > corner->ceiling_volt) -+ continue; -+ -+ prev_floor = corner->floor_volt; -+ prev_ceiling = corner->ceiling_volt; -+ prev_open_loop = corner->open_loop_volt; -+ -+ if (corner->open_loop_volt >= threshold) { -+ corner->floor_volt = max(corner->floor_volt, threshold); -+ if (corner->open_loop_volt < corner->floor_volt) -+ corner->open_loop_volt = corner->floor_volt; -+ } else { -+ corner->ceiling_volt = threshold - ctrl->step_volt; -+ } -+ -+ if (corner->floor_volt != prev_floor -+ || corner->ceiling_volt != prev_ceiling -+ || corner->open_loop_volt != prev_open_loop) -+ cpr3_debug(vreg, "MEM ACC threshold=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n", -+ threshold, i, prev_floor, prev_ceiling, -+ prev_open_loop, corner->floor_volt, -+ corner->ceiling_volt, corner->open_loop_volt); -+ } -+} -+ -+/** -+ * cpr3_apply_closed_loop_offset_voltages() - modify the closed-loop voltage -+ * adjustments by the amounts that are needed for this -+ * fuse combo -+ * @vreg: Pointer to the CPR3 regulator -+ * @volt_adjust: Array of closed-loop voltage adjustment values of length -+ * vreg->corner_count which is further adjusted based upon -+ * offset voltage fuse values. -+ * @fuse_volt_adjust: Fused closed-loop voltage adjustment values of length -+ * vreg->fuse_corner_count. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr3_apply_closed_loop_offset_voltages(struct cpr3_regulator *vreg, -+ int *volt_adjust, int *fuse_volt_adjust) -+{ -+ u32 *corner_map; -+ int rc = 0, i; -+ -+ if (!of_property_present(vreg->of_node, -+ "qcom,cpr-fused-closed-loop-voltage-adjustment-map")) { -+ /* No closed-loop offset required. */ -+ return 0; -+ } -+ -+ corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map), -+ GFP_KERNEL); -+ if (!corner_map) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", -+ 1, corner_map); -+ if (rc) -+ goto done; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ if (corner_map[i] == 0) { -+ continue; -+ } else if (corner_map[i] > vreg->fuse_corner_count) { -+ cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n", -+ i, corner_map[i]); -+ rc = -EINVAL; -+ goto done; -+ } -+ -+ volt_adjust[i] += fuse_volt_adjust[corner_map[i] - 1]; -+ } -+ -+done: -+ kfree(corner_map); -+ return rc; -+} -+ -+/** -+ * cpr3_enforce_inc_quotient_monotonicity() - Ensure that target quotients -+ * increase monotonically from lower to higher corners -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static void cpr3_enforce_inc_quotient_monotonicity(struct cpr3_regulator *vreg) -+{ -+ int i, j; -+ -+ for (i = 1; i < vreg->corner_count; i++) { -+ for (j = 0; j < CPR3_RO_COUNT; j++) { -+ if (vreg->corner[i].target_quot[j] -+ && vreg->corner[i].target_quot[j] -+ < vreg->corner[i - 1].target_quot[j]) { -+ cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", -+ i, j, -+ vreg->corner[i].target_quot[j], -+ i - 1, j, -+ vreg->corner[i - 1].target_quot[j], -+ i, j, -+ vreg->corner[i - 1].target_quot[j]); -+ vreg->corner[i].target_quot[j] -+ = vreg->corner[i - 1].target_quot[j]; -+ } -+ } -+ } -+} -+ -+/** -+ * cpr3_enforce_dec_quotient_monotonicity() - Ensure that target quotients -+ * decrease monotonically from higher to lower corners -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static void cpr3_enforce_dec_quotient_monotonicity(struct cpr3_regulator *vreg) -+{ -+ int i, j; -+ -+ for (i = vreg->corner_count - 2; i >= 0; i--) { -+ for (j = 0; j < CPR3_RO_COUNT; j++) { -+ if (vreg->corner[i + 1].target_quot[j] -+ && vreg->corner[i].target_quot[j] -+ > vreg->corner[i + 1].target_quot[j]) { -+ cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", -+ i, j, -+ vreg->corner[i].target_quot[j], -+ i + 1, j, -+ vreg->corner[i + 1].target_quot[j], -+ i, j, -+ vreg->corner[i + 1].target_quot[j]); -+ vreg->corner[i].target_quot[j] -+ = vreg->corner[i + 1].target_quot[j]; -+ } -+ } -+ } -+} -+ -+/** -+ * _cpr3_adjust_target_quotients() - adjust the target quotients for each -+ * corner of the regulator according to input adjustment and -+ * scaling arrays -+ * @vreg: Pointer to the CPR3 regulator -+ * @volt_adjust: Pointer to an array of closed-loop voltage adjustments -+ * with units of microvolts. The array must have -+ * vreg->corner_count number of elements. -+ * @ro_scale: Pointer to a flattened 2D array of RO scaling factors. -+ * The array must have an inner dimension of CPR3_RO_COUNT -+ * and an outer dimension of vreg->corner_count -+ * @label: Null terminated string providing a label for the type -+ * of adjustment. -+ * -+ * Return: true if any corners received a positive voltage adjustment (> 0), -+ * else false -+ */ -+static bool _cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, -+ const int *volt_adjust, const int *ro_scale, const char *label) -+{ -+ int i, j, quot_adjust; -+ bool is_increasing = false; -+ u32 prev_quot; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ for (j = 0; j < CPR3_RO_COUNT; j++) { -+ if (vreg->corner[i].target_quot[j]) { -+ quot_adjust = cpr3_quot_adjustment( -+ ro_scale[i * CPR3_RO_COUNT + j], -+ volt_adjust[i]); -+ if (quot_adjust) { -+ prev_quot = vreg->corner[i]. -+ target_quot[j]; -+ vreg->corner[i].target_quot[j] -+ += quot_adjust; -+ cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n", -+ i, j, label, prev_quot, -+ vreg->corner[i].target_quot[j], -+ volt_adjust[i]); -+ } -+ } -+ } -+ if (volt_adjust[i] > 0) -+ is_increasing = true; -+ } -+ -+ return is_increasing; -+} -+ -+/** -+ * cpr3_adjust_target_quotients() - adjust the target quotients for each -+ * corner according to device tree values and fuse values -+ * @vreg: Pointer to the CPR3 regulator -+ * @fuse_volt_adjust: Fused closed-loop voltage adjustment values of length -+ * vreg->fuse_corner_count. This parameter could be null -+ * pointer when no fused adjustments are needed. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg, -+ int *fuse_volt_adjust) -+{ -+ int i, rc; -+ int *volt_adjust, *ro_scale; -+ bool explicit_adjustment, fused_adjustment, is_increasing; -+ -+ explicit_adjustment = of_property_present(vreg->of_node, -+ "qcom,cpr-closed-loop-voltage-adjustment"); -+ fused_adjustment = of_property_present(vreg->of_node, -+ "qcom,cpr-fused-closed-loop-voltage-adjustment-map"); -+ -+ if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { -+ /* No adjustment required. */ -+ return 0; -+ } else if (!of_property_present(vreg->of_node, -+ "qcom,cpr-ro-scaling-factor")) { -+ cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n"); -+ return -EINVAL; -+ } -+ -+ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), -+ GFP_KERNEL); -+ ro_scale = kcalloc(vreg->corner_count * CPR3_RO_COUNT, -+ sizeof(*ro_scale), GFP_KERNEL); -+ if (!volt_adjust || !ro_scale) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,cpr-ro-scaling-factor", CPR3_RO_COUNT, ro_scale); -+ if (rc) { -+ cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->corner_count; i++) -+ memcpy(vreg->corner[i].ro_scale, &ro_scale[i * CPR3_RO_COUNT], -+ sizeof(*ro_scale) * CPR3_RO_COUNT); -+ -+ if (explicit_adjustment) { -+ rc = cpr3_parse_corner_array_property(vreg, -+ "qcom,cpr-closed-loop-voltage-adjustment", -+ 1, volt_adjust); -+ if (rc) { -+ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ _cpr3_adjust_target_quotients(vreg, volt_adjust, ro_scale, -+ "from DT"); -+ cpr3_enforce_inc_quotient_monotonicity(vreg); -+ } -+ -+ if (fused_adjustment && fuse_volt_adjust) { -+ memset(volt_adjust, 0, -+ sizeof(*volt_adjust) * vreg->corner_count); -+ -+ rc = cpr3_apply_closed_loop_offset_voltages(vreg, volt_adjust, -+ fuse_volt_adjust); -+ if (rc) { -+ cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ is_increasing = _cpr3_adjust_target_quotients(vreg, volt_adjust, -+ ro_scale, "from fuse"); -+ if (is_increasing) -+ cpr3_enforce_inc_quotient_monotonicity(vreg); -+ else -+ cpr3_enforce_dec_quotient_monotonicity(vreg); -+ } -+ -+done: -+ kfree(volt_adjust); -+ kfree(ro_scale); -+ return rc; -+} ---- /dev/null -+++ b/drivers/regulator/cpr4-apss-regulator.c -@@ -0,0 +1,1813 @@ -+/* -+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#define pr_fmt(fmt) "%s: " fmt, __func__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "cpr3-regulator.h" -+ -+#define IPQ807x_APSS_FUSE_CORNERS 4 -+#define IPQ817x_APPS_FUSE_CORNERS 2 -+#define IPQ6018_APSS_FUSE_CORNERS 4 -+#define IPQ9574_APSS_FUSE_CORNERS 4 -+ -+u32 g_valid_fuse_count = IPQ807x_APSS_FUSE_CORNERS; -+ -+/** -+ * struct cpr4_ipq807x_apss_fuses - APSS specific fuse data for IPQ807x -+ * @ro_sel: Ring oscillator select fuse parameter value for each -+ * fuse corner -+ * @init_voltage: Initial (i.e. open-loop) voltage fuse parameter value -+ * for each fuse corner (raw, not converted to a voltage) -+ * @target_quot: CPR target quotient fuse parameter value for each fuse -+ * corner -+ * @quot_offset: CPR target quotient offset fuse parameter value for each -+ * fuse corner (raw, not unpacked) used for target quotient -+ * interpolation -+ * @speed_bin: Application processor speed bin fuse parameter value for -+ * the given chip -+ * @cpr_fusing_rev: CPR fusing revision fuse parameter value -+ * @boost_cfg: CPR boost configuration fuse parameter value -+ * @boost_voltage: CPR boost voltage fuse parameter value (raw, not -+ * converted to a voltage) -+ * -+ * This struct holds the values for all of the fuses read from memory. -+ */ -+struct cpr4_ipq807x_apss_fuses { -+ u64 ro_sel[IPQ807x_APSS_FUSE_CORNERS]; -+ u64 init_voltage[IPQ807x_APSS_FUSE_CORNERS]; -+ u64 target_quot[IPQ807x_APSS_FUSE_CORNERS]; -+ u64 quot_offset[IPQ807x_APSS_FUSE_CORNERS]; -+ u64 speed_bin; -+ u64 cpr_fusing_rev; -+ u64 boost_cfg; -+ u64 boost_voltage; -+ u64 misc; -+}; -+ -+/* -+ * fuse combo = fusing revision + 8 * (speed bin) -+ * where: fusing revision = 0 - 7 and speed bin = 0 - 7 -+ */ -+#define CPR4_IPQ807x_APSS_FUSE_COMBO_COUNT 64 -+ -+/* -+ * Constants which define the name of each fuse corner. -+ */ -+enum cpr4_ipq807x_apss_fuse_corner { -+ CPR4_IPQ807x_APSS_FUSE_CORNER_SVS = 0, -+ CPR4_IPQ807x_APSS_FUSE_CORNER_NOM = 1, -+ CPR4_IPQ807x_APSS_FUSE_CORNER_TURBO = 2, -+ CPR4_IPQ807x_APSS_FUSE_CORNER_STURBO = 3, -+}; -+ -+static const char * const cpr4_ipq807x_apss_fuse_corner_name[] = { -+ [CPR4_IPQ807x_APSS_FUSE_CORNER_SVS] = "SVS", -+ [CPR4_IPQ807x_APSS_FUSE_CORNER_NOM] = "NOM", -+ [CPR4_IPQ807x_APSS_FUSE_CORNER_TURBO] = "TURBO", -+ [CPR4_IPQ807x_APSS_FUSE_CORNER_STURBO] = "STURBO", -+}; -+ -+/* -+ * IPQ807x APSS fuse parameter locations: -+ * -+ * Structs are organized with the following dimensions: -+ * Outer: 0 to 3 for fuse corners from lowest to highest corner -+ * Inner: large enough to hold the longest set of parameter segments which -+ * fully defines a fuse parameter, +1 (for NULL termination). -+ * Each segment corresponds to a contiguous group of bits from a -+ * single fuse row. These segments are concatentated together in -+ * order to form the full fuse parameter value. The segments for -+ * a given parameter may correspond to different fuse rows. -+ */ -+static struct cpr3_fuse_param -+ipq807x_apss_ro_sel_param[IPQ807x_APSS_FUSE_CORNERS][2] = { -+ {{73, 8, 11}, {} }, -+ {{73, 4, 7}, {} }, -+ {{73, 0, 3}, {} }, -+ {{73, 12, 15}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq807x_apss_init_voltage_param[IPQ807x_APSS_FUSE_CORNERS][2] = { -+ {{71, 18, 23}, {} }, -+ {{71, 12, 17}, {} }, -+ {{71, 6, 11}, {} }, -+ {{71, 0, 5}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq807x_apss_target_quot_param[IPQ807x_APSS_FUSE_CORNERS][2] = { -+ {{72, 32, 43}, {} }, -+ {{72, 20, 31}, {} }, -+ {{72, 8, 19}, {} }, -+ {{72, 44, 55}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq807x_apss_quot_offset_param[IPQ807x_APSS_FUSE_CORNERS][2] = { -+ {{} }, -+ {{71, 46, 52}, {} }, -+ {{71, 39, 45}, {} }, -+ {{71, 32, 38}, {} }, -+}; -+ -+static struct cpr3_fuse_param ipq807x_cpr_fusing_rev_param[] = { -+ {71, 53, 55}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq807x_apss_speed_bin_param[] = { -+ {36, 40, 42}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq807x_cpr_boost_fuse_cfg_param[] = { -+ {36, 43, 45}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq807x_apss_boost_fuse_volt_param[] = { -+ {71, 0, 5}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq807x_misc_fuse_volt_adj_param[] = { -+ {36, 54, 54}, -+ {}, -+}; -+ -+static struct cpr3_fuse_parameters ipq807x_fuse_params = { -+ .apss_ro_sel_param = ipq807x_apss_ro_sel_param, -+ .apss_init_voltage_param = ipq807x_apss_init_voltage_param, -+ .apss_target_quot_param = ipq807x_apss_target_quot_param, -+ .apss_quot_offset_param = ipq807x_apss_quot_offset_param, -+ .cpr_fusing_rev_param = ipq807x_cpr_fusing_rev_param, -+ .apss_speed_bin_param = ipq807x_apss_speed_bin_param, -+ .cpr_boost_fuse_cfg_param = ipq807x_cpr_boost_fuse_cfg_param, -+ .apss_boost_fuse_volt_param = ipq807x_apss_boost_fuse_volt_param, -+ .misc_fuse_volt_adj_param = ipq807x_misc_fuse_volt_adj_param -+}; -+ -+/* -+ * The number of possible values for misc fuse is -+ * 2^(#bits defined for misc fuse) -+ */ -+#define IPQ807x_MISC_FUSE_VAL_COUNT BIT(1) -+ -+/* -+ * Open loop voltage fuse reference voltages in microvolts for IPQ807x -+ */ -+static int ipq807x_apss_fuse_ref_volt -+ [IPQ807x_APSS_FUSE_CORNERS] = { -+ 720000, -+ 864000, -+ 992000, -+ 1064000, -+}; -+ -+#define IPQ807x_APSS_FUSE_STEP_VOLT 8000 -+#define IPQ807x_APSS_VOLTAGE_FUSE_SIZE 6 -+#define IPQ807x_APSS_QUOT_OFFSET_SCALE 5 -+ -+#define IPQ807x_APSS_CPR_SENSOR_COUNT 6 -+ -+#define IPQ807x_APSS_CPR_CLOCK_RATE 19200000 -+ -+#define IPQ807x_APSS_MAX_TEMP_POINTS 3 -+#define IPQ807x_APSS_TEMP_SENSOR_ID_START 4 -+#define IPQ807x_APSS_TEMP_SENSOR_ID_END 13 -+/* -+ * Boost voltage fuse reference and ceiling voltages in microvolts for -+ * IPQ807x. -+ */ -+#define IPQ807x_APSS_BOOST_FUSE_REF_VOLT 1140000 -+#define IPQ807x_APSS_BOOST_CEILING_VOLT 1140000 -+#define IPQ807x_APSS_BOOST_FLOOR_VOLT 900000 -+#define MAX_BOOST_CONFIG_FUSE_VALUE 8 -+ -+#define IPQ807x_APSS_CPR_SDELTA_CORE_COUNT 15 -+ -+#define IPQ807x_APSS_CPR_TCSR_START 8 -+#define IPQ807x_APSS_CPR_TCSR_END 9 -+ -+/* -+ * Array of integer values mapped to each of the boost config fuse values to -+ * indicate boost enable/disable status. -+ */ -+static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1}; -+ -+/* -+ * IPQ6018 (Few parameters are changed, remaining are same as IPQ807x) -+ */ -+#define IPQ6018_APSS_FUSE_STEP_VOLT 12500 -+#define IPQ6018_APSS_CPR_CLOCK_RATE 24000000 -+ -+static struct cpr3_fuse_param -+ipq6018_apss_ro_sel_param[IPQ6018_APSS_FUSE_CORNERS][2] = { -+ {{75, 8, 11}, {} }, -+ {{75, 4, 7}, {} }, -+ {{75, 0, 3}, {} }, -+ {{75, 12, 15}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq6018_apss_init_voltage_param[IPQ6018_APSS_FUSE_CORNERS][2] = { -+ {{73, 18, 23}, {} }, -+ {{73, 12, 17}, {} }, -+ {{73, 6, 11}, {} }, -+ {{73, 0, 5}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq6018_apss_target_quot_param[IPQ6018_APSS_FUSE_CORNERS][2] = { -+ {{74, 32, 43}, {} }, -+ {{74, 20, 31}, {} }, -+ {{74, 8, 19}, {} }, -+ {{74, 44, 55}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq6018_apss_quot_offset_param[IPQ6018_APSS_FUSE_CORNERS][2] = { -+ {{} }, -+ {{73, 48, 55}, {} }, -+ {{73, 40, 47}, {} }, -+ {{73, 32, 39}, {} }, -+}; -+ -+static struct cpr3_fuse_param ipq6018_cpr_fusing_rev_param[] = { -+ {75, 16, 18}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq6018_apss_speed_bin_param[] = { -+ {36, 40, 42}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq6018_cpr_boost_fuse_cfg_param[] = { -+ {36, 43, 45}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq6018_apss_boost_fuse_volt_param[] = { -+ {73, 0, 5}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq6018_misc_fuse_volt_adj_param[] = { -+ {36, 54, 54}, -+ {}, -+}; -+ -+static struct cpr3_fuse_parameters ipq6018_fuse_params = { -+ .apss_ro_sel_param = ipq6018_apss_ro_sel_param, -+ .apss_init_voltage_param = ipq6018_apss_init_voltage_param, -+ .apss_target_quot_param = ipq6018_apss_target_quot_param, -+ .apss_quot_offset_param = ipq6018_apss_quot_offset_param, -+ .cpr_fusing_rev_param = ipq6018_cpr_fusing_rev_param, -+ .apss_speed_bin_param = ipq6018_apss_speed_bin_param, -+ .cpr_boost_fuse_cfg_param = ipq6018_cpr_boost_fuse_cfg_param, -+ .apss_boost_fuse_volt_param = ipq6018_apss_boost_fuse_volt_param, -+ .misc_fuse_volt_adj_param = ipq6018_misc_fuse_volt_adj_param -+}; -+ -+ -+/* -+ * Boost voltage fuse reference and ceiling voltages in microvolts for -+ * IPQ6018. -+ */ -+#define IPQ6018_APSS_BOOST_FUSE_REF_VOLT 1140000 -+#define IPQ6018_APSS_BOOST_CEILING_VOLT 1140000 -+#define IPQ6018_APSS_BOOST_FLOOR_VOLT 900000 -+ -+/* -+ * Open loop voltage fuse reference voltages in microvolts for IPQ807x -+ */ -+static int ipq6018_apss_fuse_ref_volt -+ [IPQ6018_APSS_FUSE_CORNERS] = { -+ 725000, -+ 862500, -+ 987500, -+ 1062500, -+}; -+ -+/* -+ * IPQ6018 Memory ACC settings on TCSR -+ * -+ * Turbo_L1: write TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x10 -+ * write TCSR_CUSTOM_VDDAPC0_ACC_1 0x1 -+ * Other modes: write TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x0 -+ * write TCSR_CUSTOM_VDDAPC0_ACC_1 0x0 -+ * -+ */ -+#define IPQ6018_APSS_MEM_ACC_TCSR_COUNT 2 -+#define TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0 0x1946178 -+#define TCSR_CUSTOM_VDDAPC0_ACC_1 0x1946124 -+ -+struct mem_acc_tcsr { -+ u32 phy_addr; -+ void __iomem *ioremap_addr; -+ u32 value; -+}; -+ -+static struct mem_acc_tcsr ipq6018_mem_acc_tcsr[IPQ6018_APSS_MEM_ACC_TCSR_COUNT] = { -+ {TCSR_MEM_ACC_SW_OVERRIDE_LEGACY_APC0, NULL, 0x10}, -+ {TCSR_CUSTOM_VDDAPC0_ACC_1, NULL, 0x1}, -+}; -+ -+/* -+ * IPQ9574 (Few parameters are changed, remaining are same as IPQ6018) -+ */ -+#define IPQ9574_APSS_FUSE_STEP_VOLT 10000 -+ -+static struct cpr3_fuse_param -+ipq9574_apss_ro_sel_param[IPQ9574_APSS_FUSE_CORNERS][2] = { -+ {{107, 4, 7}, {} }, -+ {{107, 0, 3}, {} }, -+ {{106, 4, 7}, {} }, -+ {{106, 0, 3}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq9574_apss_init_voltage_param[IPQ9574_APSS_FUSE_CORNERS][2] = { -+ {{104, 24, 29}, {} }, -+ {{104, 18, 23}, {} }, -+ {{104, 12, 17}, {} }, -+ {{104, 6, 11}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq9574_apss_target_quot_param[IPQ9574_APSS_FUSE_CORNERS][2] = { -+ {{106, 32, 43}, {} }, -+ {{106, 20, 31}, {} }, -+ {{106, 8, 19}, {} }, -+ {{106, 44, 55}, {} }, -+}; -+ -+static struct cpr3_fuse_param -+ipq9574_apss_quot_offset_param[IPQ9574_APSS_FUSE_CORNERS][2] = { -+ {{} }, -+ {{105, 48, 55}, {} }, -+ {{105, 40, 47}, {} }, -+ {{105, 32, 39}, {} }, -+}; -+ -+static struct cpr3_fuse_param ipq9574_cpr_fusing_rev_param[] = { -+ {107, 8, 10}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq9574_apss_speed_bin_param[] = { -+ {0, 40, 42}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq9574_cpr_boost_fuse_cfg_param[] = { -+ {0, 43, 45}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq9574_apss_boost_fuse_volt_param[] = { -+ {104, 0, 5}, -+ {}, -+}; -+ -+static struct cpr3_fuse_param ipq9574_misc_fuse_volt_adj_param[] = { -+ {0, 54, 54}, -+ {}, -+}; -+ -+static struct cpr3_fuse_parameters ipq9574_fuse_params = { -+ .apss_ro_sel_param = ipq9574_apss_ro_sel_param, -+ .apss_init_voltage_param = ipq9574_apss_init_voltage_param, -+ .apss_target_quot_param = ipq9574_apss_target_quot_param, -+ .apss_quot_offset_param = ipq9574_apss_quot_offset_param, -+ .cpr_fusing_rev_param = ipq9574_cpr_fusing_rev_param, -+ .apss_speed_bin_param = ipq9574_apss_speed_bin_param, -+ .cpr_boost_fuse_cfg_param = ipq9574_cpr_boost_fuse_cfg_param, -+ .apss_boost_fuse_volt_param = ipq9574_apss_boost_fuse_volt_param, -+ .misc_fuse_volt_adj_param = ipq9574_misc_fuse_volt_adj_param -+}; -+ -+/* -+ * Open loop voltage fuse reference voltages in microvolts for IPQ9574 -+ */ -+static int ipq9574_apss_fuse_ref_volt -+ [IPQ9574_APSS_FUSE_CORNERS] = { -+ 725000, -+ 862500, -+ 987500, -+ 1062500, -+}; -+ -+/** -+ * cpr4_ipq807x_apss_read_fuse_data() - load APSS specific fuse parameter values -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * This function allocates a cpr4_ipq807x_apss_fuses struct, fills it with -+ * values read out of hardware fuses, and finally copies common fuse values -+ * into the CPR3 regulator struct. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_ipq807x_apss_read_fuse_data(struct cpr3_regulator *vreg) -+{ -+ void __iomem *base = vreg->thread->ctrl->fuse_base; -+ struct cpr4_ipq807x_apss_fuses *fuse; -+ int i, rc; -+ -+ fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL); -+ if (!fuse) -+ return -ENOMEM; -+ -+ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->apss_speed_bin_param, -+ &fuse->speed_bin); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc); -+ return rc; -+ } -+ cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin); -+ -+ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->cpr_fusing_rev_param, -+ &fuse->cpr_fusing_rev); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n", -+ rc); -+ return rc; -+ } -+ cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev); -+ -+ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->misc_fuse_volt_adj_param, -+ &fuse->misc); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read misc voltage adjustment fuse, rc=%d\n", -+ rc); -+ return rc; -+ } -+ cpr3_info(vreg, "CPR misc fuse value = %llu\n", fuse->misc); -+ if (fuse->misc >= IPQ807x_MISC_FUSE_VAL_COUNT) { -+ cpr3_err(vreg, "CPR misc fuse value = %llu, should be < %lu\n", -+ fuse->misc, IPQ807x_MISC_FUSE_VAL_COUNT); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < g_valid_fuse_count; i++) { -+ rc = cpr3_read_fuse_param(base, -+ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_init_voltage_param[i], -+ &fuse->init_voltage[i]); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n", -+ i, rc); -+ return rc; -+ } -+ -+ rc = cpr3_read_fuse_param(base, -+ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_target_quot_param[i], -+ &fuse->target_quot[i]); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n", -+ i, rc); -+ return rc; -+ } -+ -+ rc = cpr3_read_fuse_param(base, -+ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_ro_sel_param[i], -+ &fuse->ro_sel[i]); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n", -+ i, rc); -+ return rc; -+ } -+ -+ rc = cpr3_read_fuse_param(base, -+ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_quot_offset_param[i], -+ &fuse->quot_offset[i]); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n", -+ i, rc); -+ return rc; -+ } -+ } -+ -+ rc = cpr3_read_fuse_param(base, vreg->cpr4_regulator_data->cpr3_fuse_params->cpr_boost_fuse_cfg_param, -+ &fuse->boost_cfg); -+ if (rc) { -+ cpr3_err(vreg, "Unable to read CPR boost config fuse, rc=%d\n", -+ rc); -+ return rc; -+ } -+ cpr3_info(vreg, "Voltage boost fuse config = %llu boost = %s\n", -+ fuse->boost_cfg, boost_fuse[fuse->boost_cfg] -+ ? "enable" : "disable"); -+ -+ rc = cpr3_read_fuse_param(base, -+ vreg->cpr4_regulator_data->cpr3_fuse_params->apss_boost_fuse_volt_param, -+ &fuse->boost_voltage); -+ if (rc) { -+ cpr3_err(vreg, "failed to read boost fuse voltage, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin; -+ if (vreg->fuse_combo >= CPR4_IPQ807x_APSS_FUSE_COMBO_COUNT) { -+ cpr3_err(vreg, "invalid CPR fuse combo = %d found\n", -+ vreg->fuse_combo); -+ return -EINVAL; -+ } -+ -+ vreg->speed_bin_fuse = fuse->speed_bin; -+ vreg->cpr_rev_fuse = fuse->cpr_fusing_rev; -+ vreg->fuse_corner_count = g_valid_fuse_count; -+ vreg->platform_fuses = fuse; -+ -+ return 0; -+} -+ -+/** -+ * cpr4_apss_parse_corner_data() - parse APSS corner data from device tree -+ * properties of the CPR3 regulator's device node -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_parse_corner_data(struct cpr3_regulator *vreg) -+{ -+ struct device_node *node = vreg->of_node; -+ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; -+ u32 *temp = NULL; -+ int i, rc; -+ -+ rc = cpr3_parse_common_corner_data(vreg); -+ if (rc) { -+ cpr3_err(vreg, "error reading corner data, rc=%d\n", rc); -+ return rc; -+ } -+ -+ /* If fuse has incorrect RO Select values and dtsi has "qcom,cpr-ro-sel" -+ * entry with RO select values other than zero, then dtsi values will -+ * be used. -+ */ -+ if (of_property_present(node, "qcom,cpr-ro-sel")) { -+ temp = kcalloc(vreg->fuse_corner_count, sizeof(*temp), -+ GFP_KERNEL); -+ if (!temp) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_array_property(vreg, "qcom,cpr-ro-sel", -+ vreg->fuse_corner_count, temp); -+ if (rc) -+ goto done; -+ -+ for (i = 0; i < vreg->fuse_corner_count; i++) { -+ if (temp[i] != 0) -+ fuse->ro_sel[i] = temp[i]; -+ } -+ } -+done: -+ kfree(temp); -+ return rc; -+} -+ -+/** -+ * cpr4_apss_parse_misc_fuse_voltage_adjustments() - fill an array from a -+ * portion of the voltage adjustments specified based on -+ * miscellaneous fuse bits. -+ * @vreg: Pointer to the CPR3 regulator -+ * @volt_adjust: Voltage adjustment output data array which must be -+ * of size vreg->corner_count -+ * -+ * cpr3_parse_common_corner_data() must be called for vreg before this function -+ * is called so that speed bin size elements are initialized. -+ * -+ * Two formats are supported for the device tree property: -+ * 1. Length == tuple_list_size * vreg->corner_count -+ * (reading begins at index 0) -+ * 2. Length == tuple_list_size * vreg->speed_bin_corner_sum -+ * (reading begins at index tuple_list_size * vreg->speed_bin_offset) -+ * -+ * Here, tuple_list_size is the number of possible values for misc fuse. -+ * All other property lengths are treated as errors. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_parse_misc_fuse_voltage_adjustments( -+ struct cpr3_regulator *vreg, u32 *volt_adjust) -+{ -+ struct device_node *node = vreg->of_node; -+ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; -+ int tuple_list_size = IPQ807x_MISC_FUSE_VAL_COUNT; -+ int i, offset, rc, len = 0; -+ const char *prop_name = "qcom,cpr-misc-fuse-voltage-adjustment"; -+ -+ if (!of_find_property(node, prop_name, &len)) { -+ cpr3_err(vreg, "property %s is missing\n", prop_name); -+ return -EINVAL; -+ } -+ -+ if (len == tuple_list_size * vreg->corner_count * sizeof(u32)) { -+ offset = 0; -+ } else if (vreg->speed_bin_corner_sum > 0 && -+ len == tuple_list_size * vreg->speed_bin_corner_sum -+ * sizeof(u32)) { -+ offset = tuple_list_size * vreg->speed_bin_offset -+ + fuse->misc * vreg->corner_count; -+ } else { -+ if (vreg->speed_bin_corner_sum > 0) -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n", -+ prop_name, len, -+ tuple_list_size * vreg->corner_count -+ * sizeof(u32), -+ tuple_list_size * vreg->speed_bin_corner_sum -+ * sizeof(u32)); -+ else -+ cpr3_err(vreg, "property %s has invalid length=%d, should be %zu\n", -+ prop_name, len, -+ tuple_list_size * vreg->corner_count -+ * sizeof(u32)); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ rc = of_property_read_u32_index(node, prop_name, offset + i, -+ &volt_adjust[i]); -+ if (rc) { -+ cpr3_err(vreg, "error reading property %s, rc=%d\n", -+ prop_name, rc); -+ return rc; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr4_ipq807x_apss_calculate_open_loop_voltages() - calculate the open-loop -+ * voltage for each corner of a CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * If open-loop voltage interpolation is allowed in device tree, then -+ * this function calculates the open-loop voltage for a given corner using -+ * linear interpolation. This interpolation is performed using the processor -+ * frequencies of the lower and higher Fmax corners along with their fused -+ * open-loop voltages. -+ * -+ * If open-loop voltage interpolation is not allowed, then this function uses -+ * the Fmax fused open-loop voltage for all of the corners associated with a -+ * given fuse corner. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_ipq807x_apss_calculate_open_loop_voltages( -+ struct cpr3_regulator *vreg) -+{ -+ struct device_node *node = vreg->of_node; -+ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ int i, j, rc = 0; -+ bool allow_interpolation; -+ u64 freq_low, volt_low, freq_high, volt_high; -+ int *fuse_volt, *misc_adj_volt; -+ int *fmax_corner; -+ -+ fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt), -+ GFP_KERNEL); -+ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), -+ GFP_KERNEL); -+ if (!fuse_volt || !fmax_corner) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->fuse_corner_count; i++) { -+ if (ctrl->cpr_global_setting == CPR_DISABLED) -+ fuse_volt[i] = vreg->cpr4_regulator_data->fuse_ref_volt[i]; -+ else -+ fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse( -+ vreg->cpr4_regulator_data->fuse_ref_volt[i], -+ vreg->cpr4_regulator_data->fuse_step_volt, -+ fuse->init_voltage[i], -+ IPQ807x_APSS_VOLTAGE_FUSE_SIZE); -+ -+ /* Log fused open-loop voltage values for debugging purposes. */ -+ cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", -+ cpr4_ipq807x_apss_fuse_corner_name[i], -+ fuse_volt[i]); -+ } -+ -+ rc = cpr3_determine_part_type(vreg, -+ fuse_volt[vreg->fuse_corner_count - 1]); -+ if (rc) { -+ cpr3_err(vreg, "fused part type detection failed failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt); -+ if (rc) { -+ cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ allow_interpolation = of_property_read_bool(node, -+ "qcom,allow-voltage-interpolation"); -+ -+ for (i = 1; i < vreg->fuse_corner_count; i++) { -+ if (fuse_volt[i] < fuse_volt[i - 1]) { -+ cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n", -+ i, fuse_volt[i], i - 1, fuse_volt[i - 1], -+ i, fuse_volt[i - 1]); -+ fuse_volt[i] = fuse_volt[i - 1]; -+ } -+ } -+ -+ if (!allow_interpolation) { -+ /* Use fused open-loop voltage for lower frequencies. */ -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].open_loop_volt -+ = fuse_volt[vreg->corner[i].cpr_fuse_corner]; -+ goto done; -+ } -+ -+ /* Determine highest corner mapped to each fuse corner */ -+ j = vreg->fuse_corner_count - 1; -+ for (i = vreg->corner_count - 1; i >= 0; i--) { -+ if (vreg->corner[i].cpr_fuse_corner == j) { -+ fmax_corner[j] = i; -+ j--; -+ } -+ } -+ if (j >= 0) { -+ cpr3_err(vreg, "invalid fuse corner mapping\n"); -+ rc = -EINVAL; -+ goto done; -+ } -+ -+ /* -+ * Interpolation is not possible for corners mapped to the lowest fuse -+ * corner so use the fuse corner value directly. -+ */ -+ for (i = 0; i <= fmax_corner[0]; i++) -+ vreg->corner[i].open_loop_volt = fuse_volt[0]; -+ -+ /* Interpolate voltages for the higher fuse corners. */ -+ for (i = 1; i < vreg->fuse_corner_count; i++) { -+ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; -+ volt_low = fuse_volt[i - 1]; -+ freq_high = vreg->corner[fmax_corner[i]].proc_freq; -+ volt_high = fuse_volt[i]; -+ -+ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) -+ vreg->corner[j].open_loop_volt = cpr3_interpolate( -+ freq_low, volt_low, freq_high, volt_high, -+ vreg->corner[j].proc_freq); -+ } -+ -+done: -+ if (rc == 0) { -+ cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n"); -+ for (i = 0; i < vreg->corner_count; i++) -+ cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i, -+ vreg->corner[i].open_loop_volt); -+ -+ rc = cpr3_adjust_open_loop_voltages(vreg); -+ if (rc) -+ cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n", -+ rc); -+ -+ if (of_property_present(node, -+ "qcom,cpr-misc-fuse-voltage-adjustment" -+ )) { -+ misc_adj_volt = kcalloc(vreg->corner_count, -+ sizeof(*misc_adj_volt), GFP_KERNEL); -+ if (!misc_adj_volt) { -+ rc = -ENOMEM; -+ goto _exit; -+ } -+ -+ rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg, -+ misc_adj_volt); -+ if (rc) { -+ cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n", -+ rc); -+ kfree(misc_adj_volt); -+ goto _exit; -+ } -+ -+ for (i = 0; i < vreg->corner_count; i++) -+ vreg->corner[i].open_loop_volt -+ += misc_adj_volt[i]; -+ kfree(misc_adj_volt); -+ } -+ } -+ -+_exit: -+ kfree(fuse_volt); -+ kfree(fmax_corner); -+ return rc; -+} -+ -+/** -+ * cpr4_ipq807x_apss_set_no_interpolation_quotients() - use the fused target -+ * quotient values for lower frequencies. -+ * @vreg: Pointer to the CPR3 regulator -+ * @volt_adjust: Pointer to array of per-corner closed-loop adjustment -+ * voltages -+ * @volt_adjust_fuse: Pointer to array of per-fuse-corner closed-loop -+ * adjustment voltages -+ * @ro_scale: Pointer to array of per-fuse-corner RO scaling factor -+ * values with units of QUOT/V -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_ipq807x_apss_set_no_interpolation_quotients( -+ struct cpr3_regulator *vreg, int *volt_adjust, -+ int *volt_adjust_fuse, int *ro_scale) -+{ -+ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; -+ u32 quot, ro; -+ int quot_adjust; -+ int i, fuse_corner; -+ -+ for (i = 0; i < vreg->corner_count; i++) { -+ fuse_corner = vreg->corner[i].cpr_fuse_corner; -+ quot = fuse->target_quot[fuse_corner]; -+ quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner], -+ volt_adjust_fuse[fuse_corner] + -+ volt_adjust[i]); -+ ro = fuse->ro_sel[fuse_corner]; -+ vreg->corner[i].target_quot[ro] = quot + quot_adjust; -+ cpr3_debug(vreg, "corner=%d RO=%u target quot=%u\n", -+ i, ro, quot); -+ -+ if (quot_adjust) -+ cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n", -+ i, ro, quot, vreg->corner[i].target_quot[ro], -+ volt_adjust_fuse[fuse_corner] + -+ volt_adjust[i]); -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr4_ipq807x_apss_calculate_target_quotients() - calculate the CPR target -+ * quotient for each corner of a CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * If target quotient interpolation is allowed in device tree, then this -+ * function calculates the target quotient for a given corner using linear -+ * interpolation. This interpolation is performed using the processor -+ * frequencies of the lower and higher Fmax corners along with the fused -+ * target quotient and quotient offset of the higher Fmax corner. -+ * -+ * If target quotient interpolation is not allowed, then this function uses -+ * the Fmax fused target quotient for all of the corners associated with a -+ * given fuse corner. -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_ipq807x_apss_calculate_target_quotients( -+ struct cpr3_regulator *vreg) -+{ -+ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; -+ int rc; -+ bool allow_interpolation; -+ u64 freq_low, freq_high, prev_quot; -+ u64 *quot_low; -+ u64 *quot_high; -+ u32 quot, ro; -+ int i, j, fuse_corner, quot_adjust; -+ int *fmax_corner; -+ int *volt_adjust, *volt_adjust_fuse, *ro_scale; -+ int *voltage_adj_misc; -+ -+ /* Log fused quotient values for debugging purposes. */ -+ for (i = CPR4_IPQ807x_APSS_FUSE_CORNER_SVS; -+ i < vreg->fuse_corner_count; i++) -+ cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n", -+ cpr4_ipq807x_apss_fuse_corner_name[i], -+ fuse->ro_sel[i], fuse->target_quot[i], -+ fuse->ro_sel[i], fuse->quot_offset[i] * -+ IPQ807x_APSS_QUOT_OFFSET_SCALE); -+ -+ allow_interpolation = of_property_read_bool(vreg->of_node, -+ "qcom,allow-quotient-interpolation"); -+ -+ volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust), -+ GFP_KERNEL); -+ volt_adjust_fuse = kcalloc(vreg->fuse_corner_count, -+ sizeof(*volt_adjust_fuse), GFP_KERNEL); -+ ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale), -+ GFP_KERNEL); -+ fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner), -+ GFP_KERNEL); -+ quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low), -+ GFP_KERNEL); -+ quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high), -+ GFP_KERNEL); -+ if (!volt_adjust || !volt_adjust_fuse || !ro_scale || -+ !fmax_corner || !quot_low || !quot_high) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0], -+ volt_adjust, volt_adjust_fuse, ro_scale); -+ if (rc) { -+ cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n", -+ rc); -+ goto done; -+ } -+ -+ if (of_property_present(vreg->of_node, -+ "qcom,cpr-misc-fuse-voltage-adjustment")) { -+ voltage_adj_misc = kcalloc(vreg->corner_count, -+ sizeof(*voltage_adj_misc), GFP_KERNEL); -+ if (!voltage_adj_misc) { -+ rc = -ENOMEM; -+ goto done; -+ } -+ -+ rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg, -+ voltage_adj_misc); -+ if (rc) { -+ cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n", -+ rc); -+ kfree(voltage_adj_misc); -+ goto done; -+ } -+ -+ for (i = 0; i < vreg->corner_count; i++) -+ volt_adjust[i] += voltage_adj_misc[i]; -+ -+ kfree(voltage_adj_misc); -+ } -+ -+ if (!allow_interpolation) { -+ /* Use fused target quotients for lower frequencies. */ -+ return cpr4_ipq807x_apss_set_no_interpolation_quotients( -+ vreg, volt_adjust, volt_adjust_fuse, ro_scale); -+ } -+ -+ /* Determine highest corner mapped to each fuse corner */ -+ j = vreg->fuse_corner_count - 1; -+ for (i = vreg->corner_count - 1; i >= 0; i--) { -+ if (vreg->corner[i].cpr_fuse_corner == j) { -+ fmax_corner[j] = i; -+ j--; -+ } -+ } -+ if (j >= 0) { -+ cpr3_err(vreg, "invalid fuse corner mapping\n"); -+ rc = -EINVAL; -+ goto done; -+ } -+ -+ /* -+ * Interpolation is not possible for corners mapped to the lowest fuse -+ * corner so use the fuse corner value directly. -+ */ -+ i = CPR4_IPQ807x_APSS_FUSE_CORNER_SVS; -+ quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]); -+ quot = fuse->target_quot[i] + quot_adjust; -+ quot_high[i] = quot_low[i] = quot; -+ ro = fuse->ro_sel[i]; -+ if (quot_adjust) -+ cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n", -+ i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]); -+ -+ for (i = 0; i <= fmax_corner[CPR4_IPQ807x_APSS_FUSE_CORNER_SVS]; -+ i++) -+ vreg->corner[i].target_quot[ro] = quot; -+ -+ for (i = CPR4_IPQ807x_APSS_FUSE_CORNER_NOM; -+ i < vreg->fuse_corner_count; i++) { -+ quot_high[i] = fuse->target_quot[i]; -+ if (fuse->ro_sel[i] == fuse->ro_sel[i - 1]) -+ quot_low[i] = quot_high[i - 1]; -+ else -+ quot_low[i] = quot_high[i] -+ - fuse->quot_offset[i] -+ * IPQ807x_APSS_QUOT_OFFSET_SCALE; -+ if (quot_high[i] < quot_low[i]) { -+ cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n", -+ i, quot_high[i], i, quot_low[i], -+ i, quot_low[i]); -+ quot_high[i] = quot_low[i]; -+ } -+ } -+ -+ /* Perform per-fuse-corner target quotient adjustment */ -+ for (i = 1; i < vreg->fuse_corner_count; i++) { -+ quot_adjust = cpr3_quot_adjustment(ro_scale[i], -+ volt_adjust_fuse[i]); -+ if (quot_adjust) { -+ prev_quot = quot_high[i]; -+ quot_high[i] += quot_adjust; -+ cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n", -+ i, fuse->ro_sel[i], prev_quot, quot_high[i], -+ volt_adjust_fuse[i]); -+ } -+ -+ if (fuse->ro_sel[i] == fuse->ro_sel[i - 1]) -+ quot_low[i] = quot_high[i - 1]; -+ else -+ quot_low[i] += cpr3_quot_adjustment(ro_scale[i], -+ volt_adjust_fuse[i - 1]); -+ -+ if (quot_high[i] < quot_low[i]) { -+ cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n", -+ i, quot_high[i], i, quot_low[i], -+ i, quot_low[i]); -+ quot_high[i] = quot_low[i]; -+ } -+ } -+ -+ /* Interpolate voltages for the higher fuse corners. */ -+ for (i = 1; i < vreg->fuse_corner_count; i++) { -+ freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq; -+ freq_high = vreg->corner[fmax_corner[i]].proc_freq; -+ -+ ro = fuse->ro_sel[i]; -+ for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++) -+ vreg->corner[j].target_quot[ro] = cpr3_interpolate( -+ freq_low, quot_low[i], freq_high, quot_high[i], -+ vreg->corner[j].proc_freq); -+ } -+ -+ /* Perform per-corner target quotient adjustment */ -+ for (i = 0; i < vreg->corner_count; i++) { -+ fuse_corner = vreg->corner[i].cpr_fuse_corner; -+ ro = fuse->ro_sel[fuse_corner]; -+ quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner], -+ volt_adjust[i]); -+ if (quot_adjust) { -+ prev_quot = vreg->corner[i].target_quot[ro]; -+ vreg->corner[i].target_quot[ro] += quot_adjust; -+ cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n", -+ i, ro, prev_quot, -+ vreg->corner[i].target_quot[ro], -+ volt_adjust[i]); -+ } -+ } -+ -+ /* Ensure that target quotients increase monotonically */ -+ for (i = 1; i < vreg->corner_count; i++) { -+ ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner]; -+ if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro -+ && vreg->corner[i].target_quot[ro] -+ < vreg->corner[i - 1].target_quot[ro]) { -+ cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n", -+ i, ro, vreg->corner[i].target_quot[ro], -+ i - 1, ro, vreg->corner[i - 1].target_quot[ro], -+ i, ro, vreg->corner[i - 1].target_quot[ro]); -+ vreg->corner[i].target_quot[ro] -+ = vreg->corner[i - 1].target_quot[ro]; -+ } -+ } -+ -+done: -+ kfree(volt_adjust); -+ kfree(volt_adjust_fuse); -+ kfree(ro_scale); -+ kfree(fmax_corner); -+ kfree(quot_low); -+ kfree(quot_high); -+ return rc; -+} -+ -+/** -+ * cpr4_apss_print_settings() - print out APSS CPR configuration settings into -+ * the kernel log for debugging purposes -+ * @vreg: Pointer to the CPR3 regulator -+ */ -+static void cpr4_apss_print_settings(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_corner *corner; -+ int i; -+ -+ cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n"); -+ for (i = 0; i < vreg->corner_count; i++) { -+ corner = &vreg->corner[i]; -+ cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n", -+ i, corner->proc_freq, corner->cpr_fuse_corner, -+ corner->floor_volt, corner->open_loop_volt, -+ corner->ceiling_volt); -+ } -+ -+ if (vreg->thread->ctrl->apm) -+ cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n", -+ vreg->thread->ctrl->apm_threshold_volt, -+ vreg->thread->ctrl->apm_adj_volt); -+} -+ -+/** -+ * cpr4_apss_init_thread() - perform steps necessary to initialize the -+ * configuration data for a CPR3 thread -+ * @thread: Pointer to the CPR3 thread -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_init_thread(struct cpr3_thread *thread) -+{ -+ int rc; -+ -+ rc = cpr3_parse_common_thread_data(thread); -+ if (rc) { -+ cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n", -+ thread->thread_id, rc); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+/** -+ * cpr4_apss_parse_temp_adj_properties() - parse temperature based -+ * adjustment properties from device tree. -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_parse_temp_adj_properties(struct cpr3_controller *ctrl) -+{ -+ struct device_node *of_node = ctrl->dev->of_node; -+ int rc, i, len, temp_point_count; -+ -+ if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) { -+ /* -+ * Temperature based adjustments are not defined. Single -+ * temperature band is still valid for per-online-core -+ * adjustments. -+ */ -+ ctrl->temp_band_count = 1; -+ return 0; -+ } -+ -+ temp_point_count = len / sizeof(u32); -+ if (temp_point_count <= 0 || -+ temp_point_count > IPQ807x_APSS_MAX_TEMP_POINTS) { -+ cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n", -+ temp_point_count, IPQ807x_APSS_MAX_TEMP_POINTS); -+ return -EINVAL; -+ } -+ -+ ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count, -+ sizeof(*ctrl->temp_points), GFP_KERNEL); -+ if (!ctrl->temp_points) -+ return -ENOMEM; -+ -+ rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map", -+ ctrl->temp_points, temp_point_count); -+ if (rc) { -+ cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ for (i = 0; i < temp_point_count; i++) -+ cpr3_debug(ctrl, "Temperature Point %d=%d\n", i, -+ ctrl->temp_points[i]); -+ -+ /* -+ * If t1, t2, and t3 are the temperature points, then the temperature -+ * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). -+ */ -+ ctrl->temp_band_count = temp_point_count + 1; -+ cpr3_debug(ctrl, "Number of temp bands =%d\n", ctrl->temp_band_count); -+ -+ rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band", -+ &ctrl->initial_temp_band); -+ if (rc) { -+ cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (ctrl->initial_temp_band >= ctrl->temp_band_count) { -+ cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n", -+ ctrl->initial_temp_band, ctrl->temp_band_count - 1); -+ return -EINVAL; -+ } -+ -+ ctrl->temp_sensor_id_start = IPQ807x_APSS_TEMP_SENSOR_ID_START; -+ ctrl->temp_sensor_id_end = IPQ807x_APSS_TEMP_SENSOR_ID_END; -+ ctrl->allow_temp_adj = true; -+ return rc; -+} -+ -+/** -+ * cpr4_apss_parse_boost_properties() - parse configuration data for boost -+ * voltage adjustment for CPR3 regulator from device tree. -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_parse_boost_properties(struct cpr3_regulator *vreg) -+{ -+ struct cpr3_controller *ctrl = vreg->thread->ctrl; -+ struct cpr4_ipq807x_apss_fuses *fuse = vreg->platform_fuses; -+ struct cpr3_corner *corner; -+ int i, boost_voltage, final_boost_volt, rc = 0; -+ int *boost_table = NULL, *boost_temp_adj = NULL; -+ int boost_voltage_adjust = 0, boost_num_cores = 0; -+ u32 boost_allowed = 0; -+ -+ if (!boost_fuse[fuse->boost_cfg]) -+ /* Voltage boost is disabled in fuse */ -+ return 0; -+ -+ if (of_property_present(vreg->of_node, "qcom,allow-boost")) { -+ rc = cpr3_parse_array_property(vreg, "qcom,allow-boost", 1, -+ &boost_allowed); -+ if (rc) -+ return rc; -+ } -+ -+ if (!boost_allowed) { -+ /* Voltage boost is not enabled for this regulator */ -+ return 0; -+ } -+ -+ boost_voltage = cpr3_convert_open_loop_voltage_fuse( -+ vreg->cpr4_regulator_data->boost_fuse_ref_volt, -+ vreg->cpr4_regulator_data->fuse_step_volt, -+ fuse->boost_voltage, -+ IPQ807x_APSS_VOLTAGE_FUSE_SIZE); -+ -+ /* Log boost voltage value for debugging purposes. */ -+ cpr3_info(vreg, "Boost open-loop=%7d uV\n", boost_voltage); -+ -+ if (of_property_present(vreg->of_node, -+ "qcom,cpr-boost-voltage-fuse-adjustment")) { -+ rc = cpr3_parse_array_property(vreg, -+ "qcom,cpr-boost-voltage-fuse-adjustment", -+ 1, &boost_voltage_adjust); -+ if (rc) { -+ cpr3_err(vreg, "qcom,cpr-boost-voltage-fuse-adjustment reading failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ boost_voltage += boost_voltage_adjust; -+ /* Log boost voltage value for debugging purposes. */ -+ cpr3_info(vreg, "Adjusted boost open-loop=%7d uV\n", -+ boost_voltage); -+ } -+ -+ /* Limit boost voltage value between ceiling and floor voltage limits */ -+ boost_voltage = min(boost_voltage, vreg->cpr4_regulator_data->boost_ceiling_volt); -+ boost_voltage = max(boost_voltage, vreg->cpr4_regulator_data->boost_floor_volt); -+ -+ /* -+ * The boost feature can only be used for the highest voltage corner. -+ * Also, keep core-count adjustments disabled when the boost feature -+ * is enabled. -+ */ -+ corner = &vreg->corner[vreg->corner_count - 1]; -+ if (!corner->sdelta) { -+ /* -+ * If core-count/temp adjustments are not defined, the cpr4 -+ * sdelta for this corner will not be allocated. Allocate it -+ * here for boost configuration. -+ */ -+ corner->sdelta = devm_kzalloc(ctrl->dev, -+ sizeof(*corner->sdelta), GFP_KERNEL); -+ if (!corner->sdelta) -+ return -ENOMEM; -+ } -+ corner->sdelta->temp_band_count = ctrl->temp_band_count; -+ -+ rc = of_property_read_u32(vreg->of_node, "qcom,cpr-num-boost-cores", -+ &boost_num_cores); -+ if (rc) { -+ cpr3_err(vreg, "qcom,cpr-num-boost-cores reading failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (boost_num_cores <= 0 || -+ boost_num_cores > IPQ807x_APSS_CPR_SDELTA_CORE_COUNT) { -+ cpr3_err(vreg, "Invalid boost number of cores = %d\n", -+ boost_num_cores); -+ return -EINVAL; -+ } -+ corner->sdelta->boost_num_cores = boost_num_cores; -+ -+ boost_table = devm_kcalloc(ctrl->dev, corner->sdelta->temp_band_count, -+ sizeof(*boost_table), GFP_KERNEL); -+ if (!boost_table) -+ return -ENOMEM; -+ -+ if (of_property_present(vreg->of_node, -+ "qcom,cpr-boost-temp-adjustment")) { -+ boost_temp_adj = kcalloc(corner->sdelta->temp_band_count, -+ sizeof(*boost_temp_adj), GFP_KERNEL); -+ if (!boost_temp_adj) -+ return -ENOMEM; -+ -+ rc = cpr3_parse_array_property(vreg, -+ "qcom,cpr-boost-temp-adjustment", -+ corner->sdelta->temp_band_count, -+ boost_temp_adj); -+ if (rc) { -+ cpr3_err(vreg, "qcom,cpr-boost-temp-adjustment reading failed, rc=%d\n", -+ rc); -+ goto done; -+ } -+ } -+ -+ for (i = 0; i < corner->sdelta->temp_band_count; i++) { -+ /* Apply static adjustments to boost voltage */ -+ final_boost_volt = boost_voltage + (boost_temp_adj == NULL -+ ? 0 : boost_temp_adj[i]); -+ /* -+ * Limit final adjusted boost voltage value between ceiling -+ * and floor voltage limits -+ */ -+ final_boost_volt = min(final_boost_volt, -+ vreg->cpr4_regulator_data->boost_ceiling_volt); -+ final_boost_volt = max(final_boost_volt, -+ vreg->cpr4_regulator_data->boost_floor_volt); -+ -+ boost_table[i] = (corner->open_loop_volt - final_boost_volt) -+ / ctrl->step_volt; -+ cpr3_debug(vreg, "Adjusted boost voltage margin for temp band %d = %d steps\n", -+ i, boost_table[i]); -+ } -+ -+ corner->ceiling_volt = vreg->cpr4_regulator_data->boost_ceiling_volt; -+ corner->sdelta->boost_table = boost_table; -+ corner->sdelta->allow_boost = true; -+ corner->sdelta->allow_core_count_adj = false; -+ vreg->allow_boost = true; -+ ctrl->allow_boost = true; -+done: -+ kfree(boost_temp_adj); -+ return rc; -+} -+ -+/** -+ * cpr4_apss_init_regulator() - perform all steps necessary to initialize the -+ * configuration data for a CPR3 regulator -+ * @vreg: Pointer to the CPR3 regulator -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg) -+{ -+ struct cpr4_ipq807x_apss_fuses *fuse; -+ int rc; -+ -+ rc = cpr4_ipq807x_apss_read_fuse_data(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc); -+ return rc; -+ } -+ -+ fuse = vreg->platform_fuses; -+ -+ rc = cpr4_apss_parse_corner_data(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr3_mem_acc_init(vreg); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(vreg, "unable to initialize mem-acc regulator settings, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr4_ipq807x_apss_calculate_open_loop_voltages(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr3_limit_open_loop_voltages(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ cpr3_open_loop_voltage_as_ceiling(vreg); -+ -+ rc = cpr3_limit_floor_voltages(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc); -+ return rc; -+ } -+ -+ rc = cpr4_ipq807x_apss_calculate_target_quotients(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false); -+ if (rc) { -+ cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (vreg->allow_core_count_adj && (vreg->max_core_count <= 0 -+ || vreg->max_core_count > -+ IPQ807x_APSS_CPR_SDELTA_CORE_COUNT)) { -+ cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n", -+ vreg->max_core_count); -+ return -EINVAL; -+ } -+ -+ rc = cpr4_apss_parse_boost_properties(vreg); -+ if (rc) { -+ cpr3_err(vreg, "unable to parse boost adjustments, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ cpr4_apss_print_settings(vreg); -+ -+ return rc; -+} -+ -+/** -+ * cpr4_apss_init_controller() - perform APSS CPR4 controller specific -+ * initializations -+ * @ctrl: Pointer to the CPR3 controller -+ * -+ * Return: 0 on success, errno on failure -+ */ -+static int cpr4_apss_init_controller(struct cpr3_controller *ctrl) -+{ -+ int rc; -+ -+ rc = cpr3_parse_common_ctrl_data(ctrl); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = of_property_read_u32(ctrl->dev->of_node, -+ "qcom,cpr-down-error-step-limit", -+ &ctrl->down_error_step_limit); -+ if (rc) { -+ cpr3_err(ctrl, "error reading qcom,cpr-down-error-step-limit, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = of_property_read_u32(ctrl->dev->of_node, -+ "qcom,cpr-up-error-step-limit", -+ &ctrl->up_error_step_limit); -+ if (rc) { -+ cpr3_err(ctrl, "error reading qcom,cpr-up-error-step-limit, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ /* -+ * Use fixed step quotient if specified otherwise use dynamic -+ * calculated per RO step quotient -+ */ -+ of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed", -+ &ctrl->step_quot_fixed); -+ ctrl->use_dynamic_step_quot = ctrl->step_quot_fixed ? false : true; -+ -+ ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node, -+ "qcom,cpr-saw-use-unit-mV"); -+ -+ of_property_read_u32(ctrl->dev->of_node, -+ "qcom,cpr-voltage-settling-time", -+ &ctrl->voltage_settling_time); -+ -+ if (of_property_present(ctrl->dev->of_node, "vdd-limit-supply")) { -+ ctrl->vdd_limit_regulator = -+ devm_regulator_get(ctrl->dev, "vdd-limit"); -+ if (IS_ERR(ctrl->vdd_limit_regulator)) { -+ rc = PTR_ERR(ctrl->vdd_limit_regulator); -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable to request vdd-limit regulator, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ rc = cpr3_apm_init(ctrl); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "unable to initialize APM settings, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr4_apss_parse_temp_adj_properties(ctrl); -+ if (rc) { -+ cpr3_err(ctrl, "unable to parse temperature adjustment properties, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ ctrl->sensor_count = IPQ807x_APSS_CPR_SENSOR_COUNT; -+ -+ /* -+ * APSS only has one thread (0) per controller so the zeroed -+ * array does not need further modification. -+ */ -+ ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count, -+ sizeof(*ctrl->sensor_owner), GFP_KERNEL); -+ if (!ctrl->sensor_owner) -+ return -ENOMEM; -+ -+ ctrl->ctrl_type = CPR_CTRL_TYPE_CPR4; -+ ctrl->supports_hw_closed_loop = false; -+ ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node, -+ "qcom,cpr-hw-closed-loop"); -+ return 0; -+} -+ -+static int cpr4_apss_regulator_suspend(struct platform_device *pdev, -+ pm_message_t state) -+{ -+ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); -+ -+ return cpr3_regulator_suspend(ctrl); -+} -+ -+static int cpr4_apss_regulator_resume(struct platform_device *pdev) -+{ -+ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); -+ -+ return cpr3_regulator_resume(ctrl); -+} -+ -+static void ipq6018_set_mem_acc(struct regulator_dev *rdev) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ -+ ipq6018_mem_acc_tcsr[0].ioremap_addr = -+ ioremap(ipq6018_mem_acc_tcsr[0].phy_addr, 0x4); -+ ipq6018_mem_acc_tcsr[1].ioremap_addr = -+ ioremap(ipq6018_mem_acc_tcsr[1].phy_addr, 0x4); -+ -+ if ((ipq6018_mem_acc_tcsr[0].ioremap_addr != NULL) && -+ (ipq6018_mem_acc_tcsr[1].ioremap_addr != NULL) && -+ (vreg->current_corner == (vreg->corner_count - CPR3_CORNER_OFFSET))) { -+ -+ writel_relaxed(ipq6018_mem_acc_tcsr[0].value, -+ ipq6018_mem_acc_tcsr[0].ioremap_addr); -+ writel_relaxed(ipq6018_mem_acc_tcsr[1].value, -+ ipq6018_mem_acc_tcsr[1].ioremap_addr); -+ } -+} -+ -+static void ipq6018_clr_mem_acc(struct regulator_dev *rdev) -+{ -+ struct cpr3_regulator *vreg = rdev_get_drvdata(rdev); -+ -+ if ((ipq6018_mem_acc_tcsr[0].ioremap_addr != NULL) && -+ (ipq6018_mem_acc_tcsr[1].ioremap_addr != NULL) && -+ (vreg->current_corner != vreg->corner_count - CPR3_CORNER_OFFSET)) { -+ writel_relaxed(0x0, ipq6018_mem_acc_tcsr[0].ioremap_addr); -+ writel_relaxed(0x0, ipq6018_mem_acc_tcsr[1].ioremap_addr); -+ } -+ -+ iounmap(ipq6018_mem_acc_tcsr[0].ioremap_addr); -+ iounmap(ipq6018_mem_acc_tcsr[1].ioremap_addr); -+} -+ -+static struct cpr4_mem_acc_func ipq6018_mem_acc_funcs = { -+ .set_mem_acc = ipq6018_set_mem_acc, -+ .clear_mem_acc = ipq6018_clr_mem_acc -+}; -+ -+static const struct cpr4_reg_data ipq807x_cpr_apss = { -+ .cpr_valid_fuse_count = IPQ807x_APSS_FUSE_CORNERS, -+ .fuse_ref_volt = ipq807x_apss_fuse_ref_volt, -+ .fuse_step_volt = IPQ807x_APSS_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ807x_APSS_CPR_CLOCK_RATE, -+ .boost_fuse_ref_volt= IPQ807x_APSS_BOOST_FUSE_REF_VOLT, -+ .boost_ceiling_volt= IPQ807x_APSS_BOOST_CEILING_VOLT, -+ .boost_floor_volt= IPQ807x_APSS_BOOST_FLOOR_VOLT, -+ .cpr3_fuse_params = &ipq807x_fuse_params, -+ .mem_acc_funcs = NULL, -+}; -+ -+static const struct cpr4_reg_data ipq817x_cpr_apss = { -+ .cpr_valid_fuse_count = IPQ817x_APPS_FUSE_CORNERS, -+ .fuse_ref_volt = ipq807x_apss_fuse_ref_volt, -+ .fuse_step_volt = IPQ807x_APSS_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ807x_APSS_CPR_CLOCK_RATE, -+ .boost_fuse_ref_volt= IPQ807x_APSS_BOOST_FUSE_REF_VOLT, -+ .boost_ceiling_volt= IPQ807x_APSS_BOOST_CEILING_VOLT, -+ .boost_floor_volt= IPQ807x_APSS_BOOST_FLOOR_VOLT, -+ .cpr3_fuse_params = &ipq807x_fuse_params, -+ .mem_acc_funcs = NULL, -+}; -+ -+static const struct cpr4_reg_data ipq6018_cpr_apss = { -+ .cpr_valid_fuse_count = IPQ6018_APSS_FUSE_CORNERS, -+ .fuse_ref_volt = ipq6018_apss_fuse_ref_volt, -+ .fuse_step_volt = IPQ6018_APSS_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ6018_APSS_CPR_CLOCK_RATE, -+ .boost_fuse_ref_volt = IPQ6018_APSS_BOOST_FUSE_REF_VOLT, -+ .boost_ceiling_volt = IPQ6018_APSS_BOOST_CEILING_VOLT, -+ .boost_floor_volt = IPQ6018_APSS_BOOST_FLOOR_VOLT, -+ .cpr3_fuse_params = &ipq6018_fuse_params, -+ .mem_acc_funcs = &ipq6018_mem_acc_funcs, -+}; -+ -+static const struct cpr4_reg_data ipq9574_cpr_apss = { -+ .cpr_valid_fuse_count = IPQ9574_APSS_FUSE_CORNERS, -+ .fuse_ref_volt = ipq9574_apss_fuse_ref_volt, -+ .fuse_step_volt = IPQ9574_APSS_FUSE_STEP_VOLT, -+ .cpr_clk_rate = IPQ6018_APSS_CPR_CLOCK_RATE, -+ .boost_fuse_ref_volt = IPQ6018_APSS_BOOST_FUSE_REF_VOLT, -+ .boost_ceiling_volt = IPQ6018_APSS_BOOST_CEILING_VOLT, -+ .boost_floor_volt = IPQ6018_APSS_BOOST_FLOOR_VOLT, -+ .cpr3_fuse_params = &ipq9574_fuse_params, -+ .mem_acc_funcs = NULL, -+}; -+ -+static const struct of_device_id cpr4_regulator_match_table[] = { -+ { -+ .compatible = "qcom,cpr4-ipq807x-apss-regulator", -+ .data = &ipq807x_cpr_apss -+ }, -+ { -+ .compatible = "qcom,cpr4-ipq817x-apss-regulator", -+ .data = &ipq817x_cpr_apss -+ }, -+ { -+ .compatible = "qcom,cpr4-ipq6018-apss-regulator", -+ .data = &ipq6018_cpr_apss -+ }, -+ { -+ .compatible = "qcom,cpr4-ipq9574-apss-regulator", -+ .data = &ipq9574_cpr_apss -+ }, -+ {} -+}; -+ -+static int cpr4_apss_regulator_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct cpr3_controller *ctrl; -+ const struct cpr4_reg_data *cpr_data; -+ int i, rc; -+ -+ if (!dev->of_node) { -+ dev_err(dev, "Device tree node is missing\n"); -+ return -EINVAL; -+ } -+ -+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); -+ if (!ctrl) -+ return -ENOMEM; -+ -+ cpr_data = of_device_get_match_data(&pdev->dev); -+ g_valid_fuse_count = cpr_data->cpr_valid_fuse_count; -+ dev_info(dev, "CPR valid fuse count: %d\n", g_valid_fuse_count); -+ ctrl->cpr_clock_rate = cpr_data->cpr_clk_rate; -+ -+ ctrl->dev = dev; -+ /* Set to false later if anything precludes CPR operation. */ -+ ctrl->cpr_allowed_hw = true; -+ -+ rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name", -+ &ctrl->name); -+ if (rc) { -+ cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr3_map_fuse_base(ctrl, pdev); -+ if (rc) { -+ cpr3_err(ctrl, "could not map fuse base address\n"); -+ return rc; -+ } -+ -+ rc = cpr3_read_tcsr_setting(ctrl, pdev, IPQ807x_APSS_CPR_TCSR_START, -+ IPQ807x_APSS_CPR_TCSR_END); -+ if (rc) { -+ cpr3_err(ctrl, "could not read CPR tcsr setting\n"); -+ return rc; -+ } -+ -+ rc = cpr3_allocate_threads(ctrl, 0, 0); -+ if (rc) { -+ cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ if (ctrl->thread_count != 1) { -+ cpr3_err(ctrl, "expected 1 thread but found %d\n", -+ ctrl->thread_count); -+ return -EINVAL; -+ } -+ -+ rc = cpr4_apss_init_controller(ctrl); -+ if (rc) { -+ if (rc != -EPROBE_DEFER) -+ cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n", -+ rc); -+ return rc; -+ } -+ -+ rc = cpr4_apss_init_thread(&ctrl->thread[0]); -+ if (rc) { -+ cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc); -+ return rc; -+ } -+ -+ for (i = 0; i < ctrl->thread[0].vreg_count; i++) { -+ ctrl->thread[0].vreg[i].cpr4_regulator_data = cpr_data; -+ rc = cpr4_apss_init_regulator(&ctrl->thread[0].vreg[i]); -+ if (rc) { -+ cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n", -+ rc); -+ return rc; -+ } -+ } -+ -+ platform_set_drvdata(pdev, ctrl); -+ -+ return cpr3_regulator_register(pdev, ctrl); -+} -+ -+static void cpr4_apss_regulator_remove(struct platform_device *pdev) -+{ -+ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); -+ -+ cpr3_regulator_unregister(ctrl); -+} -+ -+static struct platform_driver cpr4_apss_regulator_driver = { -+ .driver = { -+ .name = "qcom,cpr4-apss-regulator", -+ .of_match_table = cpr4_regulator_match_table, -+ }, -+ .probe = cpr4_apss_regulator_probe, -+ .remove = cpr4_apss_regulator_remove, -+ .suspend = cpr4_apss_regulator_suspend, -+ .resume = cpr4_apss_regulator_resume, -+}; -+ -+static int cpr4_regulator_init(void) -+{ -+ return platform_driver_register(&cpr4_apss_regulator_driver); -+} -+ -+static void cpr4_regulator_exit(void) -+{ -+ platform_driver_unregister(&cpr4_apss_regulator_driver); -+} -+ -+MODULE_DESCRIPTION("CPR4 APSS regulator driver"); -+MODULE_LICENSE("GPL v2"); -+ -+arch_initcall(cpr4_regulator_init); -+module_exit(cpr4_regulator_exit); diff --git a/target/linux/qualcommax/patches-6.12/0902-arm64-dts-ipq8074-add-label-to-clocks.patch b/target/linux/qualcommax/patches-6.12/0902-arm64-dts-ipq8074-add-label-to-clocks.patch deleted file mode 100644 index 9b8b4df12b2..00000000000 --- a/target/linux/qualcommax/patches-6.12/0902-arm64-dts-ipq8074-add-label-to-clocks.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 6baf7e4abcea6f7ac21eccf072a20078b39d064c Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 9 Feb 2022 23:13:26 +0100 -Subject: [PATCH] arm64: dts: ipq8074: add label to clocks - -Add label to clocks node as that makes it easy to add the NSS fixed -clocks that are required in their DTSI. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -15,7 +15,7 @@ - compatible = "qcom,ipq8074"; - interrupt-parent = <&intc>; - -- clocks { -+ clocks: clocks { - sleep_clk: sleep_clk { - compatible = "fixed-clock"; - clock-frequency = <32768>; diff --git a/target/linux/qualcommax/patches-6.12/0903-psci-dont-advertise-OSI-support-for-IPQ6018.patch b/target/linux/qualcommax/patches-6.12/0903-psci-dont-advertise-OSI-support-for-IPQ6018.patch deleted file mode 100644 index 5fcb90098f3..00000000000 --- a/target/linux/qualcommax/patches-6.12/0903-psci-dont-advertise-OSI-support-for-IPQ6018.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 563db68137475d011b355bfe674d1b7a24778091 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sat, 8 Oct 2022 22:26:31 +0200 -Subject: [PATCH] psci: dont advertise OSI support for IPQ6018 - -Some older IPQ60xx SoC series boards ship with TrustZone/QSEE firmware -older than TZ.WNS.5.1-00084 which will advertise OSI[1] but are broken -and trying to use OSI will cause the board to hang until WDT kicks in. - -So workaround it by checking for SoC compatible and returning false so -OSI is not used. - -[1] https://www.spinics.net/lists/linux-arm-msm/msg79916.html - -Signed-off-by: Robert Marko ---- - drivers/firmware/psci/psci.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - ---- a/drivers/firmware/psci/psci.c -+++ b/drivers/firmware/psci/psci.c -@@ -87,6 +87,18 @@ static inline bool psci_has_ext_power_st - - bool psci_has_osi_support(void) - { -+ /* -+ * Some older IPQ60xx SoC series boards ship with -+ * TrustZone/QSEE firmware older than TZ.WNS.5.1-00084 -+ * which will advertise OSI but is broken and trying -+ * to use OSI will cause the board to hang until WDT -+ * kicks in. -+ * So workaround it by checking for SoC compatible -+ * and returning false so OSI is not used. -+ */ -+ if (of_machine_is_compatible("qcom,ipq6018")) -+ return false; -+ - return psci_cpu_suspend_feature & PSCI_1_0_OS_INITIATED; - } - diff --git a/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch b/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch deleted file mode 100644 index 30c6eced96c..00000000000 --- a/target/linux/qualcommax/patches-6.12/0904-clk-qcom-ipq6018-workaround-networking-clock-parenti.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 0c5b5243ad55ae744e790ba90c5ad37a93bd1377 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Tue, 11 Oct 2022 23:38:45 +0200 -Subject: [PATCH] clk: qcom: ipq6018: workaround networking clock parenting - -Currently, networking clocks are only looked up by fw_name however, -these are registered and setup by SSDK and are not available to the -GCC driver at all, so work around that by providing a global name -fallback. - -While we are here, provide global fallback for bias_pll_cc_clk and -bias_pll_nss_noc_clk as well as these are fixed clocks also not available -to the driver. - -Signed-off-by: Robert Marko ---- - drivers/clk/qcom/gcc-ipq6018.c | 39 +++++++++++++++++----------------- - 1 file changed, 19 insertions(+), 20 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq6018.c -+++ b/drivers/clk/qcom/gcc-ipq6018.c -@@ -360,7 +360,7 @@ static const struct freq_tbl ftbl_nss_pp - - static const struct clk_parent_data gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { - { .fw_name = "xo" }, -- { .fw_name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - { .hw = &gpll0.clkr.hw }, - { .hw = &gpll4.clkr.hw }, - { .hw = &nss_crypto_pll.clkr.hw }, -@@ -534,12 +534,12 @@ static const struct freq_multi_tbl ftbl_ - static const struct clk_parent_data - gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { - { .fw_name = "xo" }, -- { .fw_name = "uniphy0_gcc_rx_clk" }, -- { .fw_name = "uniphy0_gcc_tx_clk" }, -- { .fw_name = "uniphy1_gcc_rx_clk" }, -- { .fw_name = "uniphy1_gcc_tx_clk" }, -+ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, -+ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, -+ { .fw_name = "uniphy1_gcc_rx_clk", .name = "uniphy1_gcc_rx_clk" }, -+ { .fw_name = "uniphy1_gcc_tx_clk", .name = "uniphy1_gcc_tx_clk" }, - { .hw = &ubi32_pll.clkr.hw }, -- { .fw_name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map -@@ -589,12 +589,12 @@ static const struct freq_multi_tbl ftbl_ - static const struct clk_parent_data - gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { - { .fw_name = "xo" }, -- { .fw_name = "uniphy0_gcc_tx_clk" }, -- { .fw_name = "uniphy0_gcc_rx_clk" }, -- { .fw_name = "uniphy1_gcc_tx_clk" }, -- { .fw_name = "uniphy1_gcc_rx_clk" }, -+ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, -+ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, -+ { .fw_name = "uniphy1_gcc_tx_clk", .name = "uniphy1_gcc_tx_clk" }, -+ { .fw_name = "uniphy1_gcc_rx_clk", .name = "uniphy1_gcc_rx_clk" }, - { .hw = &ubi32_pll.clkr.hw }, -- { .fw_name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map -@@ -730,10 +730,10 @@ static const struct freq_tbl ftbl_nss_po - - static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { - { .fw_name = "xo" }, -- { .fw_name = "uniphy0_gcc_rx_clk" }, -- { .fw_name = "uniphy0_gcc_tx_clk" }, -+ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, -+ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, - { .hw = &ubi32_pll.clkr.hw }, -- { .fw_name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { -@@ -766,10 +766,10 @@ static const struct freq_tbl ftbl_nss_po - - static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { - { .fw_name = "xo" }, -- { .fw_name = "uniphy0_gcc_tx_clk" }, -- { .fw_name = "uniphy0_gcc_rx_clk" }, -+ { .fw_name = "uniphy0_gcc_tx_clk", .name = "uniphy0_gcc_tx_clk" }, -+ { .fw_name = "uniphy0_gcc_rx_clk", .name = "uniphy0_gcc_rx_clk" }, - { .hw = &ubi32_pll.clkr.hw }, -- { .fw_name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { -@@ -1915,12 +1915,11 @@ static const struct freq_tbl ftbl_ubi32_ - { } - }; - --static const struct clk_parent_data -- gcc_xo_gpll0_gpll2_bias_pll_nss_noc_clk[] = { -+static const struct clk_parent_data gcc_xo_gpll0_gpll2_bias_pll_nss_noc_clk[] = { - { .fw_name = "xo" }, - { .hw = &gpll0.clkr.hw }, - { .hw = &gpll2.clkr.hw }, -- { .fw_name = "bias_pll_nss_noc_clk" }, -+ { .fw_name = "bias_pll_nss_noc_clk", .name = "bias_pll_nss_noc_clk" }, - }; - - static const struct parent_map gcc_xo_gpll0_gpll2_bias_pll_nss_noc_clk_map[] = { diff --git a/target/linux/qualcommax/patches-6.12/0905-remoteproc-q6v5_wcss-change-ssr-name-for-ipq6018-wif.patch b/target/linux/qualcommax/patches-6.12/0905-remoteproc-q6v5_wcss-change-ssr-name-for-ipq6018-wif.patch deleted file mode 100644 index af7f45cc763..00000000000 --- a/target/linux/qualcommax/patches-6.12/0905-remoteproc-q6v5_wcss-change-ssr-name-for-ipq6018-wif.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 505f9c8653fc218ca47a153ec58ebc16bef5502f Mon Sep 17 00:00:00 2001 -From: Mantas Pucka -Date: Tue, 16 Jan 2024 10:42:40 +0200 -Subject: [PATCH 16/19] remoteproc: q6v5_wcss: change ssr name for ipq6018 wifi - subsystem - -On IPQ6018 this string ends up being sent to RPM when remoteproc stops -(on crash or rmmod ath11k). "q6wcss" is not a valid name (not found by -`strings` in rpm.mbn), so this causes RPM do 'something' (presumably crash) -causing a system reboot followed by hang in XBL, with no WDT running. -Let's change ssr_name to a more sensible 'wcnss', that does not cause such -issues. - -Signed-off-by: Mantas Pucka ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -1143,9 +1143,9 @@ static int q6v5_wcss_probe(struct platfo - if (ret) - return ret; - -- qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss"); -+ qcom_add_glink_subdev(rproc, &wcss->glink_subdev, desc->ssr_name); - qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev); -- qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); -+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, desc->ssr_name); - - if (desc->ssctl_id) - wcss->sysmon = qcom_add_sysmon_subdev(rproc, -@@ -1195,7 +1195,7 @@ static const struct wcss_data wcss_ipq60 - .aon_reset_required = true, - .wcss_q6_reset_required = true, - .bcr_reset_required = false, -- .ssr_name = "q6wcss", -+ .ssr_name = "wcnss", - .ops = &q6v5_wcss_ipq8074_ops, - .requires_force_stop = true, - .need_mem_protection = true, diff --git a/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch b/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch deleted file mode 100644 index aac8cd53dc2..00000000000 --- a/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch +++ /dev/null @@ -1,120 +0,0 @@ -From 153c74fc80b9f33ed1a50d7790bf6979fdceb370 Mon Sep 17 00:00:00 2001 -From: Mantas Pucka -Date: Tue, 16 Jan 2024 11:41:06 +0200 -Subject: [PATCH 19/19] arm64: dts: qcom: ipq6018: add wifi node - -IPQ6018 has a AHB based Q6v5 802.11ax radios that are supported -by the ath11k. - -Add the required DT node to enable the built-in radios. - -Signed-off-by: Mantas Pucka ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 96 +++++++++++++++++++++++++++++++++++ - 1 file changed, 96 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -831,6 +831,102 @@ - }; - }; - -+ wifi: wifi@c000000 { -+ compatible = "qcom,ipq6018-wifi"; -+ reg = <0x0 0xc000000 0x0 0x1000000>; -+ qcom,rproc = <&q6v5_wcss>; -+ interrupts = , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ , -+ ; -+ interrupt-names = "misc-pulse1", "misc-latch", "sw-exception", -+ "watchdog", "ce0", "ce1", "ce2", "ce3", "ce4", -+ "ce5", "ce6", "ce7", "ce8", "ce9", "ce10", -+ "ce11", "host2wbm-desc-feed", -+ "host2reo-re-injection", "host2reo-command", -+ "host2rxdma-monitor-ring3", -+ "host2rxdma-monitor-ring2", -+ "host2rxdma-monitor-ring1", -+ "reo2ost-exception", "wbm2host-rx-release", -+ "reo2host-status", -+ "reo2host-destination-ring4", -+ "reo2host-destination-ring3", -+ "reo2host-destination-ring2", -+ "reo2host-destination-ring1", -+ "rxdma2host-monitor-destination-mac3", -+ "rxdma2host-monitor-destination-mac2", -+ "rxdma2host-monitor-destination-mac1", -+ "ppdu-end-interrupts-mac3", -+ "ppdu-end-interrupts-mac2", -+ "ppdu-end-interrupts-mac1", -+ "rxdma2host-monitor-status-ring-mac3", -+ "rxdma2host-monitor-status-ring-mac2", -+ "rxdma2host-monitor-status-ring-mac1", -+ "host2rxdma-host-buf-ring-mac3", -+ "host2rxdma-host-buf-ring-mac2", -+ "host2rxdma-host-buf-ring-mac1", -+ "rxdma2host-destination-ring-mac3", -+ "rxdma2host-destination-ring-mac2", -+ "rxdma2host-destination-ring-mac1", -+ "host2tcl-input-ring4", -+ "host2tcl-input-ring3", -+ "host2tcl-input-ring2", -+ "host2tcl-input-ring1", -+ "wbm2host-tx-completions-ring3", -+ "wbm2host-tx-completions-ring2", -+ "wbm2host-tx-completions-ring1", -+ "tcl2host-status-ring"; -+ status = "disabled"; -+ }; -+ - q6v5_wcss: remoteproc@cd00000 { - compatible = "qcom,ipq6018-wcss-pil"; - reg = <0x0 0x0cd00000 0x0 0x4040>, diff --git a/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch b/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch deleted file mode 100644 index 9dc8cd4d086..00000000000 --- a/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch +++ /dev/null @@ -1,53 +0,0 @@ -From d93936f175bd914067df8f63f5fbe6e3b77bb4d2 Mon Sep 17 00:00:00 2001 -From: Mantas Pucka -Date: Tue, 23 May 2023 14:46:28 +0300 -Subject: [PATCH 11/19] soc: qcom: fix smp2p ack on ipq6018 - -IPQ6018 seem to need different ack mechanism for smp2p messaging. This -fixes q6v5_wcss remoteproc firmware reloading. Without this first load -is OK, but subsequent loads would hang and fail to complete. - -Signed-off-by: Mantas Pucka ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 1 + - drivers/soc/qcom/smp2p.c | 6 +++++- - 2 files changed, 6 insertions(+), 1 deletion(-) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -1183,6 +1183,7 @@ - - wcss_smp2p_out: master-kernel { - qcom,entry-name = "master-kernel"; -+ qcom,smp2p-feature-ssr-ack; - #qcom,smem-state-cells = <1>; - }; - ---- a/drivers/soc/qcom/smp2p.c -+++ b/drivers/soc/qcom/smp2p.c -@@ -159,6 +159,8 @@ struct qcom_smp2p { - - struct list_head inbound; - struct list_head outbound; -+ -+ bool need_ssr_ack; - }; - - #define CREATE_TRACE_POINTS -@@ -316,7 +318,7 @@ static irqreturn_t qcom_smp2p_intr(int i - ack_restart = qcom_smp2p_check_ssr(smp2p); - qcom_smp2p_notify_in(smp2p); - -- if (ack_restart) -+ if (ack_restart || smp2p->need_ssr_ack) - qcom_smp2p_do_ssr_ack(smp2p); - } - -@@ -447,6 +449,7 @@ static int qcom_smp2p_outbound_entry(str - - /* Make the logical entry reference the physical value */ - entry->value = &out->entries[out->valid_entries].value; -+ smp2p->need_ssr_ack = of_property_read_bool(node, "qcom,smp2p-feature-ssr-ack"); - - out->valid_entries++; - diff --git a/target/linux/qualcommax/patches-6.12/0908-remoteproc-qcom_q6v5_wcss-add-optional-qdss_at-clock.patch b/target/linux/qualcommax/patches-6.12/0908-remoteproc-qcom_q6v5_wcss-add-optional-qdss_at-clock.patch deleted file mode 100644 index ae2e500125b..00000000000 --- a/target/linux/qualcommax/patches-6.12/0908-remoteproc-qcom_q6v5_wcss-add-optional-qdss_at-clock.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 87dbcc69a7e3fe6ccddf4fe9bdbf51330f5e4a77 Mon Sep 17 00:00:00 2001 -From: Mantas Pucka -Date: Tue, 23 Jan 2024 11:04:04 +0200 -Subject: [PATCH] remoteproc: qcom_q6v5_wcss: add optional qdss_at clock - -IPQ6018 needs QDSS_AT clock enabled when loading wifi. Optionally enable it -when provided by DT. - -Signed-off-by: Mantas Pucka ---- - drivers/remoteproc/qcom_q6v5_wcss.c | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - ---- a/drivers/remoteproc/qcom_q6v5_wcss.c -+++ b/drivers/remoteproc/qcom_q6v5_wcss.c -@@ -120,6 +120,7 @@ struct q6v5_wcss { - struct clk *qdsp6ss_core_gfmux; - struct clk *lcc_bcr_sleep; - struct clk *prng_clk; -+ struct clk *qdss_clk; - struct regulator *cx_supply; - struct qcom_sysmon *sysmon; - -@@ -260,6 +261,9 @@ static int q6v5_wcss_start(struct rproc - return ret; - } - -+ if (wcss->qdss_clk) -+ clk_prepare_enable(wcss->qdss_clk); -+ - qcom_q6v5_prepare(&wcss->q6v5); - - if (wcss->need_mem_protection) { -@@ -773,6 +777,8 @@ static int q6v5_wcss_stop(struct rproc * - } - - pas_done: -+ if (wcss->qdss_clk) -+ clk_disable_unprepare(wcss->qdss_clk); - clk_disable_unprepare(wcss->prng_clk); - qcom_q6v5_unprepare(&wcss->q6v5); - -@@ -981,6 +987,12 @@ static int ipq_init_clock(struct q6v5_wc - dev_err(wcss->dev, "Failed to get prng clock\n"); - return ret; - } -+ -+ wcss->qdss_clk = devm_clk_get(wcss->dev, "qdss"); -+ if (IS_ERR(wcss->qdss_clk)) { -+ wcss->qdss_clk = NULL; -+ } -+ - return 0; - } - diff --git a/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch b/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch deleted file mode 100644 index 2a2fa5ffc91..00000000000 --- a/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 71f30e25d21ae4981ecef6653a4ba7dfeb80db7b Mon Sep 17 00:00:00 2001 -From: Mantas Pucka -Date: Tue, 23 Jan 2024 11:04:57 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq6018: assign QDSS_AT clock to wifi remoteproc - -IPQ6018 needs to enable QDSS_AT clock when loading wifi firmware, -add it to wifi remoteproc clock list. - -Signed-off-by: Mantas Pucka ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 15 ++++++++------- - 1 file changed, 9 insertions(+), 8 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -952,8 +952,8 @@ - "wcss_reset", - "wcss_q6_reset"; - -- clocks = <&gcc GCC_PRNG_AHB_CLK>; -- clock-names = "prng"; -+ clocks = <&gcc GCC_PRNG_AHB_CLK>, <&gcc GCC_QDSS_AT_CLK>; -+ clock-names = "prng", "qdss" ; - - qcom,halt-regs = <&tcsr 0x18000 0x1b000 0xe000>; - diff --git a/target/linux/qualcommax/patches-6.12/0911-arm64-cmdline-replacement.patch b/target/linux/qualcommax/patches-6.12/0911-arm64-cmdline-replacement.patch deleted file mode 100644 index 127677d2200..00000000000 --- a/target/linux/qualcommax/patches-6.12/0911-arm64-cmdline-replacement.patch +++ /dev/null @@ -1,105 +0,0 @@ -Subject: qualcommax: add kernel cmdline replacement hack - -Add kernel command line replacement hack to qualcommax. Now we can -find and replace arguments in the kernel command line by setting -bootargs-find-1, bootargs-replace-1, bootargs-exact-match-1 -and bootargs-find-2, bootargs-replace-2, bootargs-exact-match-2 -under the chosen node in the device tree. - -This hack replaces the first occurence of bootargs-find-X with -bootargs-replace-X. If bootargs-exact-match-X is set to "y", -then the replacement happens only if the kernel command line is -identical to bootargs-find-X. - -Signed-off-by: Qiyuan Zhang ---- - drivers/of/fdt.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 71 insertions(+) - ---- a/drivers/of/fdt.c -+++ b/drivers/of/fdt.c -@@ -1021,6 +1021,14 @@ int __init early_init_dt_scan_chosen(cha - const void *rng_seed; - const void *fdt = initial_boot_params; - -+ int i, cmd_len, f_len, r_len, offset, step; -+ char *s_ptr, *l_end, *r_end, *cur_ptr, *end_ptr; -+ const char *exact_match; -+ const int bootargs_replace_num = 2; -+ const char *bootargs_replace_props[2][3] = -+ { {"bootargs-find-1", "bootargs-replace-1", "bootargs-exact-match-1"}, -+ {"bootargs-find-2", "bootargs-replace-2", "bootargs-exact-match-2"} }; -+ - node = fdt_path_offset(fdt, "/chosen"); - if (node < 0) - node = fdt_path_offset(fdt, "/chosen@0"); -@@ -1049,6 +1057,69 @@ int __init early_init_dt_scan_chosen(cha - p = of_get_flat_dt_prop(node, "bootargs", &l); - if (p != NULL && l > 0) - strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE)); -+ -+ for(i = 0; i < bootargs_replace_num; i++) { -+ p = of_get_flat_dt_prop(node, bootargs_replace_props[i][0], &f_len); -+ -+ if (p == NULL || f_len == 0 ) -+ continue; -+ -+ exact_match = of_get_flat_dt_prop(node, bootargs_replace_props[i][2], &l); -+ -+ if (exact_match != NULL && l > 0 && exact_match[0] == 'y') { -+ if(strncmp(cmdline, p, r_len) == 0) -+ s_ptr = cmdline; -+ else -+ s_ptr = NULL; -+ } else { -+ s_ptr = strstr(cmdline, p); -+ } -+ -+ if(!s_ptr) -+ continue; -+ -+ p = of_get_flat_dt_prop(node, bootargs_replace_props[i][1], &r_len); -+ -+ if (p == NULL || r_len == 0) -+ continue; -+ -+ pr_info("Input kernel commad line: %s\n", cmdline); -+ -+ cmd_len = strlen(cmdline); -+ -+ if (cmd_len - f_len + r_len < COMMAND_LINE_SIZE) { -+ -+ pr_info("Replace kernel command line with %s\n", bootargs_replace_props[i][1]); -+ -+ offset = r_len - f_len; -+ -+ if (offset != 0) { -+ l_end = s_ptr + f_len - 1; -+ r_end = cmdline + cmd_len; -+ -+ if (offset > 0) { -+ step = -1; -+ cur_ptr = r_end; -+ end_ptr = l_end + step; -+ } else { -+ step = 1; -+ cur_ptr = l_end; -+ end_ptr = r_end + step; -+ } -+ -+ for (; cur_ptr != end_ptr; cur_ptr += step) -+ *(cur_ptr + offset) = *cur_ptr; -+ } -+ -+ memcpy(s_ptr, p, r_len - 1); -+ -+ pr_info("Kernel command line after replacement: %s\n", cmdline); -+ } else { -+ pr_err("Replace kernel command line with %s failed\n", bootargs_replace_props[i][1]); -+ } -+ -+ } -+ - p = of_get_flat_dt_prop(node, "bootargs-append", &l); - if (p != NULL && l > 0) - strlcat(cmdline, p, COMMAND_LINE_SIZE); diff --git a/target/linux/qualcommax/patches-6.12/0912-arm64-dts-qcom-ipq5018-remove-cpufreq-scaling.patch b/target/linux/qualcommax/patches-6.12/0912-arm64-dts-qcom-ipq5018-remove-cpufreq-scaling.patch deleted file mode 100644 index 8432679f66e..00000000000 --- a/target/linux/qualcommax/patches-6.12/0912-arm64-dts-qcom-ipq5018-remove-cpufreq-scaling.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 08910d6f438475bafec40654ff1c3e722d34d809 Mon Sep 17 00:00:00 2001 -From: Robert Senderek -Date: Thu, 25 Dec 2025 12:14:46 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq5018: remove cpufreq scaling - -ipq5018 do not support freq scaling and therefore stuck on 800Mhz -This patch allows CPU to run with 1.008 Ghz as designed - -Signed-off-by: Robert Senderek ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 6 ------ - 1 file changed, 6 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -95,12 +95,6 @@ - compatible = "operating-points-v2"; - opp-shared; - -- opp-800000000 { -- opp-hz = /bits/ 64 <800000000>; -- opp-microvolt = <1100000>; -- clock-latency-ns = <200000>; -- }; -- - opp-1008000000 { - opp-hz = /bits/ 64 <1008000000>; - opp-microvolt = <1100000>; diff --git a/target/linux/qualcommax/patches-6.12/0917-clk-qcom-gcc-ipq6018-mark-gcc_xo_clk_src-as-critical.patch b/target/linux/qualcommax/patches-6.12/0917-clk-qcom-gcc-ipq6018-mark-gcc_xo_clk_src-as-critical.patch deleted file mode 100644 index 1844803aa00..00000000000 --- a/target/linux/qualcommax/patches-6.12/0917-clk-qcom-gcc-ipq6018-mark-gcc_xo_clk_src-as-critical.patch +++ /dev/null @@ -1,28 +0,0 @@ -From: John Crispin -Date: Wed, 05 Mar 2026 12:00:00 +0100 -Subject: clk: qcom: gcc-ipq6018: mark gcc_xo_clk_src as critical - -The XO clock source is always-on in hardware and cannot be gated. -Without CLK_IS_CRITICAL, runtime PM of downstream consumers (such as -the CMN PLL driver) cascades a disable up to gcc_xo_clk_src, causing -a branch status timeout warning. - -The IPQ8074 GCC driver already marks this clock as CLK_IS_CRITICAL. -Apply the same fix to IPQ6018. - -Signed-off-by: John Crispin ---- - drivers/clk/qcom/gcc-ipq6018.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/clk/qcom/gcc-ipq6018.c -+++ b/drivers/clk/qcom/gcc-ipq6018.c -@@ -400,7 +400,7 @@ static struct clk_branch gcc_xo_clk_src - .fw_name = "xo", - }, - .num_parents = 1, -- .flags = CLK_SET_RATE_PARENT, -+ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, - .ops = &clk_branch2_ops, - }, - }, diff --git a/target/linux/qualcommax/patches-6.12/0920-clk-add-clk_hw_recalc_rate-to-trigger-HW-clk-rate-re.patch b/target/linux/qualcommax/patches-6.12/0920-clk-add-clk_hw_recalc_rate-to-trigger-HW-clk-rate-re.patch deleted file mode 100644 index 73444396f80..00000000000 --- a/target/linux/qualcommax/patches-6.12/0920-clk-add-clk_hw_recalc_rate-to-trigger-HW-clk-rate-re.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 49ae1f081626f1a8c91b283d06868a178a0a2e5a Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Tue, 17 Mar 2026 17:41:45 +0100 -Subject: [PATCH] clk: add clk_hw_recalc_rate() to trigger HW clk rate - recalculation - -There is currently a latent problem with HW clk that only expose a -.recalc_rate OP and doesn't have a .set_rate() and also have the -CLK_GET_RATE_NOCACHE flag set. In such case the rate in clk core is -parsed only (and set) only at init and when the full clk_set_rate() -is called. In every other case .recalc_rate() is never called. - -It's also possible that an HW clk of this type, initially report 0 as rate -as the register are still not configured and the HW clk effectively doesn't -return any rate. - -This gets especially problematic when a clock provider use such clk as a -parent and require the rate for parent selection for the final rate -calculation. - -In such case, since the HW clk rate is never updated after init, it's still -0 and cause problems with any other HW clk that use .determine_rate() or -.round_rate() and search for the closest rate using clk_hw_get_rate() on -the parents. - -This doesn't happen if the full clk_get_rate() is used instead as it will -check if CLK_GET_RATE_NOCACHE is set and recalculate the rate accordingly. - -Updating the clk_hw_get_rate() to align to what clk_get_rate() does is not -possible as it should be lockless and might cause problems in any user of -clk_hw_get_rate(). - -A more safe approach is the introduction of a direct function that triggers -the HW clk rate recalculation, clk_hw_recalc_rate(). - -Any driver that implement an HW clk that entirely depends on some register -to configure the rate (that are externally configured) and have only -.recalc_rate() and set CLK_GET_RATE_NOCACHE (aka the case where the HW clk -rate actually change and depends on the external register configuration) -will have to call clk_hw_recalc_rate() on the HW clk after changing the -register configuration to sync the CCF with the new rate for the HW clk. - -Example: - - - All register zero -> HW clk rate = 0 - - PCS configure USXGMII mode -> HW clk rate = 0 - - PCS call clk_hw_recalc_rate() -> HW clk rate = 312MHz - - Port goes UP - - PCS/MAC scale the PHY port clock correctly by having - the correct reference clock as parent (instead of 0) - -Signed-off-by: Christian Marangi ---- - drivers/clk/clk.c | 23 +++++++++++++++++++++++ - include/linux/clk-provider.h | 1 + - 2 files changed, 24 insertions(+) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -1990,6 +1990,29 @@ static unsigned long clk_core_get_rate_r - } - - /** -+ * clk_hw_recalc_rate - trigger rate recalculation for clk_hw -+ * @hw: clk_hw associated with the clk to recalculate for -+ * -+ * Use clk_hw_recalc_rate() for the hw clk where the rate -+ * entirely depend on register configuration and doesn't have -+ * a .set_rate() OP. In such case, after modifying the register -+ * that would change the rate for the hw clk, call -+ * clk_hw_recalc_rate() to sync the CCF with the new clk rate. -+ */ -+void clk_hw_recalc_rate(const struct clk_hw *hw) -+{ -+ struct clk_core *core = hw->core; -+ -+ if (!core || !(core->flags & CLK_GET_RATE_NOCACHE)) -+ return; -+ -+ clk_prepare_lock(); -+ __clk_recalc_rates(core, false, 0); -+ clk_prepare_unlock(); -+} -+EXPORT_SYMBOL_GPL(clk_hw_recalc_rate); -+ -+/** - * clk_get_rate - return the rate of clk - * @clk: the clk whose rate is being returned - * ---- a/include/linux/clk-provider.h -+++ b/include/linux/clk-provider.h -@@ -1355,6 +1355,7 @@ int clk_hw_get_parent_index(struct clk_h - int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *new_parent); - unsigned int __clk_get_enable_count(struct clk *clk); - unsigned long clk_hw_get_rate(const struct clk_hw *hw); -+void clk_hw_recalc_rate(const struct clk_hw *hw); - unsigned long clk_hw_get_flags(const struct clk_hw *hw); - #define clk_hw_can_set_rate_parent(hw) \ - (clk_hw_get_flags((hw)) & CLK_SET_RATE_PARENT) diff --git a/target/linux/qualcommax/patches-6.12/0950-net-dsa-add-out-of-band-tagging-protocol.patch b/target/linux/qualcommax/patches-6.12/0950-net-dsa-add-out-of-band-tagging-protocol.patch deleted file mode 100644 index 142c8bb698b..00000000000 --- a/target/linux/qualcommax/patches-6.12/0950-net-dsa-add-out-of-band-tagging-protocol.patch +++ /dev/null @@ -1,220 +0,0 @@ -From a32e16b3c2fc1954ad6e09737439f60e5890278e Mon Sep 17 00:00:00 2001 -From: Maxime Chevallier -Date: Fri, 4 Nov 2022 18:41:49 +0100 -Subject: [PATCH] net: dsa: add out-of-band tagging protocol - -This tagging protocol is designed for the situation where the link -between the MAC and the Switch is designed such that the Destination -Port, which is usually embedded in some part of the Ethernet Header, is -sent out-of-band, and isn't present at all in the Ethernet frame. - -This can happen when the MAC and Switch are tightly integrated on an -SoC, as is the case with the Qualcomm IPQ4019 for example, where the DSA -tag is inserted directly into the DMA descriptors. In that case, -the MAC driver is responsible for sending the tag to the switch using -the out-of-band medium. To do so, the MAC driver needs to have the -information of the destination port for that skb. - -Add a new tagging protocol based on SKB extensions to convey the -information about the destination port to the MAC driver - -Signed-off-by: Maxime Chevallier ---- - Documentation/networking/dsa/dsa.rst | 13 +++++++- - MAINTAINERS | 1 + - include/linux/dsa/oob.h | 16 +++++++++ - include/linux/skbuff.h | 3 ++ - include/net/dsa.h | 2 ++ - net/core/skbuff.c | 10 ++++++ - net/dsa/Kconfig | 9 +++++ - net/dsa/Makefile | 1 + - net/dsa/tag_oob.c | 49 ++++++++++++++++++++++++++++ - 9 files changed, 103 insertions(+), 1 deletion(-) - create mode 100644 include/linux/dsa/oob.h - create mode 100644 net/dsa/tag_oob.c - ---- a/Documentation/networking/dsa/dsa.rst -+++ b/Documentation/networking/dsa/dsa.rst -@@ -70,7 +70,8 @@ Switch tagging protocols - ------------------------ - - DSA supports many vendor-specific tagging protocols, one software-defined --tagging protocol, and a tag-less mode as well (``DSA_TAG_PROTO_NONE``). -+tagging protocol, a tag-less mode as well (``DSA_TAG_PROTO_NONE``) and an -+out-of-band tagging protocol (``DSA_TAG_PROTO_OOB``). - - The exact format of the tag protocol is vendor specific, but in general, they - all contain something which: -@@ -221,6 +222,16 @@ receive all frames regardless of the val - setting the ``promisc_on_conduit`` property of the ``struct dsa_device_ops``. - Note that this assumes a DSA-unaware conduit driver, which is the norm. - -+Some SoCs have a tight integration between the conduit network interface and the -+embedded switch, such that the DSA tag isn't transmitted in the packet data, -+but through another media, using so-called out-of-band tagging. In that case, -+the host MAC driver is in charge of transmitting the tag to the switch. -+An example is the IPQ4019 SoC, that transmits the tag between the ipqess -+ethernet controller and the qca8k switch using the DMA descriptor. In that -+configuration, tag-chaining is permitted, but the OOB tag will always be the -+top-most switch in the tree. The tagger (``DSA_TAG_PROTO_OOB``) uses skb -+extensions to transmit the tag to and from the MAC driver. -+ - Conduit network devices - ----------------------- - ---- /dev/null -+++ b/include/linux/dsa/oob.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only -+ * Copyright (C) 2022 Maxime Chevallier -+ */ -+ -+#ifndef _NET_DSA_OOB_H -+#define _NET_DSA_OOB_H -+ -+#include -+ -+struct dsa_oob_tag_info { -+ u16 port; -+}; -+ -+int dsa_oob_tag_push(struct sk_buff *skb, struct dsa_oob_tag_info *ti); -+int dsa_oob_tag_pop(struct sk_buff *skb, struct dsa_oob_tag_info *ti); -+#endif ---- a/include/linux/skbuff.h -+++ b/include/linux/skbuff.h -@@ -4852,6 +4852,9 @@ enum skb_ext_id { - #if IS_ENABLED(CONFIG_MCTP_FLOWS) - SKB_EXT_MCTP, - #endif -+#if IS_ENABLED(CONFIG_NET_DSA_TAG_OOB) -+ SKB_EXT_DSA_OOB, -+#endif - SKB_EXT_NUM, /* must be last */ - }; - ---- a/include/net/dsa.h -+++ b/include/net/dsa.h -@@ -57,6 +57,7 @@ struct tc_action; - #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 - #define DSA_TAG_PROTO_MXL862_VALUE 30 - #define DSA_TAG_PROTO_MXL862_8021Q_VALUE 31 -+#define DSA_TAG_PROTO_OOB_VALUE 32 - - enum dsa_tag_protocol { - DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, -@@ -91,6 +92,7 @@ enum dsa_tag_protocol { - DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, - DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, - DSA_TAG_PROTO_MXL862_8021Q = DSA_TAG_PROTO_MXL862_8021Q_VALUE, -+ DSA_TAG_PROTO_OOB = DSA_TAG_PROTO_OOB_VALUE, - }; - - struct dsa_switch; ---- a/net/core/skbuff.c -+++ b/net/core/skbuff.c -@@ -65,8 +65,12 @@ - #include - #include - #include -+#ifdef CONFIG_NET_DSA_TAG_OOB -+#include -+#endif - - #include -+#include - #include - #include - #include -@@ -5097,6 +5101,9 @@ static const u8 skb_ext_type_len[] = { - #if IS_ENABLED(CONFIG_MCTP_FLOWS) - [SKB_EXT_MCTP] = SKB_EXT_CHUNKSIZEOF(struct mctp_flow), - #endif -+#if IS_ENABLED(CONFIG_NET_DSA_TAG_OOB) -+ [SKB_EXT_DSA_OOB] = SKB_EXT_CHUNKSIZEOF(struct dsa_oob_tag_info), -+#endif - }; - - static __always_inline unsigned int skb_ext_total_length(void) ---- a/net/dsa/Kconfig -+++ b/net/dsa/Kconfig -@@ -145,6 +145,15 @@ config NET_DSA_TAG_OCELOT_8021Q - this mode, less TCAM resources (VCAP IS1, IS2, ES0) are available for - use with tc-flower. - -+config NET_DSA_TAG_OOB -+ select SKB_EXTENSIONS -+ tristate "Tag driver for Out-of-band tagging drivers" -+ help -+ Say Y or M if you want to enable support for pairs of embedded -+ switches and host MAC drivers which perform demultiplexing and -+ packet steering to ports using out of band metadata processed -+ by the DSA master, rather than tags present in the packets. -+ - config NET_DSA_TAG_QCA - tristate "Tag driver for Qualcomm Atheros QCA8K switches" - help ---- a/net/dsa/Makefile -+++ b/net/dsa/Makefile -@@ -33,6 +33,7 @@ obj-$(CONFIG_NET_DSA_TAG_MXL_862XX_8021Q - obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o - obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o - obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o -+obj-$(CONFIG_NET_DSA_TAG_OOB) += tag_oob.o - obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o - obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o - obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o ---- /dev/null -+++ b/net/dsa/tag_oob.c -@@ -0,0 +1,51 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+/* Copyright (c) 2022, Maxime Chevallier */ -+ -+#include -+#include -+#include -+ -+#include "tag.h" -+ -+#define OOB_NAME "oob" -+ -+static struct sk_buff *oob_tag_xmit(struct sk_buff *skb, -+ struct net_device *dev) -+{ -+ struct dsa_oob_tag_info *tag_info = skb_ext_add(skb, SKB_EXT_DSA_OOB); -+ struct dsa_port *dp = dsa_user_to_port(dev); -+ -+ tag_info->port = dp->index; -+ -+ return skb; -+} -+ -+static struct sk_buff *oob_tag_rcv(struct sk_buff *skb, -+ struct net_device *dev) -+{ -+ struct dsa_oob_tag_info *tag_info = skb_ext_find(skb, SKB_EXT_DSA_OOB); -+ -+ if (!tag_info) -+ return NULL; -+ -+ skb->dev = dsa_conduit_find_user(dev, 0, tag_info->port); -+ if (!skb->dev) -+ return NULL; -+ -+ return skb; -+} -+ -+static const struct dsa_device_ops oob_tag_dsa_ops = { -+ .name = OOB_NAME, -+ .proto = DSA_TAG_PROTO_OOB, -+ .xmit = oob_tag_xmit, -+ .rcv = oob_tag_rcv, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("DSA tag driver for out-of-band tagging"); -+MODULE_AUTHOR("Maxime Chevallier "); -+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OOB, OOB_NAME); -+ -+module_dsa_tag_driver(oob_tag_dsa_ops); diff --git a/target/linux/qualcommax/patches-6.12/0951-net-pcs-add-uniphy-pcs.patch b/target/linux/qualcommax/patches-6.12/0951-net-pcs-add-uniphy-pcs.patch deleted file mode 100644 index 05ab18902e5..00000000000 --- a/target/linux/qualcommax/patches-6.12/0951-net-pcs-add-uniphy-pcs.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- a/drivers/net/pcs/Kconfig -+++ b/drivers/net/pcs/Kconfig -@@ -51,4 +51,13 @@ 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. - -+config PCS_QCA_UNIPHY -+ tristate "Qualcomm 802.11ax UNIPHY PCS driver" -+ select PHYLINK -+ select FWNODE_PCS -+ help -+ PCS driver for the UNIPHY SerDes blocks in Qualcomm 802.11ax SoCs. -+ Provides PSGMII and SGMII interface modes and registers the UNIPHY -+ PLL clock providers needed for port clock muxing. -+ - endmenu ---- a/drivers/net/pcs/Makefile -+++ b/drivers/net/pcs/Makefile -@@ -10,3 +10,4 @@ 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_QCA_UNIPHY) += pcs-qca-uniphy.o diff --git a/target/linux/qualcommax/patches-6.12/0952-net-ethernet-qca-add-ppe.patch b/target/linux/qualcommax/patches-6.12/0952-net-ethernet-qca-add-ppe.patch deleted file mode 100644 index bec14518d67..00000000000 --- a/target/linux/qualcommax/patches-6.12/0952-net-ethernet-qca-add-ppe.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- a/drivers/net/ethernet/qualcomm/Kconfig -+++ b/drivers/net/ethernet/qualcomm/Kconfig -@@ -61,6 +61,13 @@ config QCOM_EMAC - low power, Receive-Side Scaling (RSS), and IEEE 1588-2008 - Precision Clock Synchronization Protocol. - -+config QCOM_80211AX_PPE -+ tristate "Qualcomm 802.11ax PPE switch driver" -+ select PHYLINK -+ help -+ Driver for Qualcomm 802.11ax PPE (Packet Processing Engine) switches. -+ -+ - source "drivers/net/ethernet/qualcomm/rmnet/Kconfig" - - endif # NET_VENDOR_QUALCOMM ---- a/drivers/net/ethernet/qualcomm/Makefile -+++ b/drivers/net/ethernet/qualcomm/Makefile -@@ -8,6 +8,8 @@ obj-$(CONFIG_QCA7000_SPI) += qcaspi.o - qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o - obj-$(CONFIG_QCA7000_UART) += qcauart.o - qcauart-objs := qca_uart.o -+obj-$(CONFIG_QCOM_80211AX_PPE) += qca_ppe.o -+qca_ppe-objs := qca_ppe_main.o qca_ppe_vlan.o qca_ppe_scheduler.o - - obj-y += emac/ - diff --git a/target/linux/qualcommax/patches-6.12/0953-net-ethernet-qca-add-EDMA.patch b/target/linux/qualcommax/patches-6.12/0953-net-ethernet-qca-add-EDMA.patch deleted file mode 100644 index 6244773c4fa..00000000000 --- a/target/linux/qualcommax/patches-6.12/0953-net-ethernet-qca-add-EDMA.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- a/drivers/net/ethernet/qualcomm/Kconfig -+++ b/drivers/net/ethernet/qualcomm/Kconfig -@@ -67,6 +67,11 @@ config QCOM_80211AX_PPE - help - Driver for Qualcomm 802.11ax PPE (Packet Processing Engine) switches. - -+config QCOM_EDMA -+ tristate "EDMA dataplane ethernet driver for Qualcomm IPQ platforms" -+ help -+ EDMA dataplane ethernet driver for Qualcomm IPQ platforms. -+ - - source "drivers/net/ethernet/qualcomm/rmnet/Kconfig" - ---- a/drivers/net/ethernet/qualcomm/Makefile -+++ b/drivers/net/ethernet/qualcomm/Makefile -@@ -10,6 +10,7 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o - qcauart-objs := qca_uart.o - obj-$(CONFIG_QCOM_80211AX_PPE) += qca_ppe.o - qca_ppe-objs := qca_ppe_main.o qca_ppe_vlan.o qca_ppe_scheduler.o -+obj-$(CONFIG_QCOM_EDMA) += qca_edma.o - - obj-y += emac/ - diff --git a/target/linux/qualcommax/patches-6.12/0954-net-stmmac-Introduce-Qualcomm-IPQ5018-DWMAC-driver.patch b/target/linux/qualcommax/patches-6.12/0954-net-stmmac-Introduce-Qualcomm-IPQ5018-DWMAC-driver.patch deleted file mode 100644 index f0cf29a1b01..00000000000 --- a/target/linux/qualcommax/patches-6.12/0954-net-stmmac-Introduce-Qualcomm-IPQ5018-DWMAC-driver.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- a/drivers/net/ethernet/stmicro/stmmac/Kconfig -+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig -@@ -78,6 +78,14 @@ config DWMAC_INGENIC - device driver. This driver is used on for the Ingenic SoCs - MAC ethernet controller. - -+config DWMAC_IPQ5018 -+ tristate "Qualcomm IPQ5018 DWMAC driver" -+ depends on OF && (ARCH_QCOM || COMPILE_TEST) -+ help -+ Support for ethernet controller on Qualcomm IPQ5018 SoCs. -+ -+ This selects the IPQ5018 SoC glue layer support for the stmmac driver. -+ - config DWMAC_IPQ806X - tristate "QCA IPQ806x DWMAC support" - default ARCH_QCOM ---- a/drivers/net/ethernet/stmicro/stmmac/Makefile -+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile -@@ -15,6 +15,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stm - obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o - obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o - obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o -+obj-$(CONFIG_DWMAC_IPQ5018) += dwmac-ipq5018.o - obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o - obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o - obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o