From: Robert Marko Date: Sun, 11 Jun 2023 17:32:52 +0000 (+0200) Subject: ipq807x: rename target to qualcommax X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=f02f6aaa8d4e1025ab4aa9f569123e57f689f4e5;p=openwrt%2Fstaging%2Fblocktrron.git ipq807x: rename target to qualcommax Currently, ipq807x only covers Qualcomm IPQ807x SoC-s. However, Qualcomm also has IPQ60xx and IPQ50xx SoC-s under the AX WiSoC-s and they share a lot of stuff with IPQ807x, especially IPQ60xx so to avoid duplicating kernel patches and everything lets make a common target with per SoC subtargets. Start doing that by renaming ipq807x to qualcommax so that dependencies on ipq807x target can be updated. Signed-off-by: Robert Marko --- diff --git a/.github/labeler.yml b/.github/labeler.yml index 6d53d58106..e22f074604 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -41,8 +41,8 @@ - "target/linux/ipq40xx/**" "target/ipq806x": - "target/linux/ipq806x/**" -"target/ipq807x": - - "target/linux/ipq807x/**" +"target/qualcommax": + - "target/linux/qualcommax/**" "target/kirkwood": - "target/linux/kirkwood/**" - "package/boot/uboot-kirkwood/**" diff --git a/config/Config-images.in b/config/Config-images.in index ea7b3d37c3..c37537b1a7 100644 --- a/config/Config-images.in +++ b/config/Config-images.in @@ -17,7 +17,7 @@ menu "Target Images" default TARGET_INITRAMFS_COMPRESSION_LZMA if TARGET_lantiq default TARGET_INITRAMFS_COMPRESSION_LZMA if TARGET_mpc85xx default TARGET_INITRAMFS_COMPRESSION_LZMA if TARGET_ramips - default TARGET_INITRAMFS_COMPRESSION_ZSTD if TARGET_ipq807x + default TARGET_INITRAMFS_COMPRESSION_ZSTD if TARGET_qualcommax default TARGET_INITRAMFS_COMPRESSION_XZ if USES_SEPARATE_INITRAMFS default TARGET_INITRAMFS_COMPRESSION_NONE depends on TARGET_ROOTFS_INITRAMFS diff --git a/package/boot/uboot-envtools/files/ipq807x b/package/boot/uboot-envtools/files/ipq807x deleted file mode 100644 index 345cdadebe..0000000000 --- a/package/boot/uboot-envtools/files/ipq807x +++ /dev/null @@ -1,54 +0,0 @@ -[ -e /etc/config/ubootenv ] && exit 0 - -touch /etc/config/ubootenv - -. /lib/uboot-envtools.sh -. /lib/functions.sh - -board=$(board_name) - -case "$board" in -dynalink,dl-wrx36|\ -netgear,wax218) - idx="$(find_mtd_index 0:appsblenv)" - [ -n "$idx" ] && \ - ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x40000" "0x20000" "2" - ;; -compex,wpq873|\ -edgecore,eap102|\ -zyxel,nbg7815) - idx="$(find_mtd_index 0:appsblenv)" - [ -n "$idx" ] && \ - ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x10000" "1" - ;; -edimax,cax1800) - idx="$(find_mtd_index 0:appsblenv)" - [ -n "$idx" ] && \ - ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x20000" - ;; -redmi,ax6|\ -xiaomi,ax3600|\ -xiaomi,ax9000) - idx="$(find_mtd_index 0:appsblenv)" - [ -n "$idx" ] && \ - ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x20000" - idx2="$(find_mtd_index bdata)" - [ -n "$idx2" ] && \ - ubootenv_add_uci_sys_config "/dev/mtd$idx2" "0x0" "0x10000" "0x20000" - ;; -prpl,haze) - mmcpart="$(find_mmc_part 0:APPSBLENV)" - [ -n "$mmcpart" ] && \ - ubootenv_add_uci_config "$mmcpart" "0x0" "0x40000" "0x400" "0x100" - ;; -qnap,301w) - idx="$(find_mtd_index 0:appsblenv)" - [ -n "$idx" ] && \ - ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x20000" "0x20000" "1" - ;; -esac - -config_load ubootenv -config_foreach ubootenv_add_app_config - -exit 0 diff --git a/package/boot/uboot-envtools/files/qualcommax b/package/boot/uboot-envtools/files/qualcommax new file mode 100644 index 0000000000..345cdadebe --- /dev/null +++ b/package/boot/uboot-envtools/files/qualcommax @@ -0,0 +1,54 @@ +[ -e /etc/config/ubootenv ] && exit 0 + +touch /etc/config/ubootenv + +. /lib/uboot-envtools.sh +. /lib/functions.sh + +board=$(board_name) + +case "$board" in +dynalink,dl-wrx36|\ +netgear,wax218) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x40000" "0x20000" "2" + ;; +compex,wpq873|\ +edgecore,eap102|\ +zyxel,nbg7815) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x10000" "1" + ;; +edimax,cax1800) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x20000" + ;; +redmi,ax6|\ +xiaomi,ax3600|\ +xiaomi,ax9000) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x20000" + idx2="$(find_mtd_index bdata)" + [ -n "$idx2" ] && \ + ubootenv_add_uci_sys_config "/dev/mtd$idx2" "0x0" "0x10000" "0x20000" + ;; +prpl,haze) + mmcpart="$(find_mmc_part 0:APPSBLENV)" + [ -n "$mmcpart" ] && \ + ubootenv_add_uci_config "$mmcpart" "0x0" "0x40000" "0x400" "0x100" + ;; +qnap,301w) + idx="$(find_mtd_index 0:appsblenv)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x20000" "0x20000" "1" + ;; +esac + +config_load ubootenv +config_foreach ubootenv_add_app_config + +exit 0 diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index 0c6941050b..2bffd2a1b7 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -48,7 +48,7 @@ define Package/ipq-wifi-default SUBMENU:=ath10k Board-Specific Overrides SECTION:=firmware CATEGORY:=Firmware - DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x||TARGET_ipq807x) + DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x||TARGET_qualcommax) TITLE:=Custom Board endef diff --git a/package/kernel/linux/modules/netsupport.mk b/package/kernel/linux/modules/netsupport.mk index 0c5d2da950..cb85c46b78 100644 --- a/package/kernel/linux/modules/netsupport.mk +++ b/package/kernel/linux/modules/netsupport.mk @@ -1511,7 +1511,7 @@ $(eval $(call KernelPackage,qrtr-tun)) define KernelPackage/qrtr-smd SUBMENU:=$(NETWORK_SUPPORT_MENU) TITLE:=SMD IPC Router channels - DEPENDS:=+kmod-qrtr @TARGET_ipq807x + DEPENDS:=+kmod-qrtr @TARGET_qualcommax KCONFIG:=CONFIG_QRTR_SMD FILES:= $(LINUX_DIR)/net/qrtr/qrtr-smd.ko AUTOLOAD:=$(call AutoProbe,qrtr-smd) diff --git a/package/kernel/linux/modules/usb.mk b/package/kernel/linux/modules/usb.mk index 644365ed19..45efd7449f 100644 --- a/package/kernel/linux/modules/usb.mk +++ b/package/kernel/linux/modules/usb.mk @@ -483,7 +483,7 @@ $(eval $(call KernelPackage,usb-dwc3)) define KernelPackage/usb-dwc3-qcom TITLE:=DWC3 Qualcomm USB driver - DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x||TARGET_ipq807x) +kmod-usb-dwc3 + DEPENDS:=@(TARGET_ipq40xx||TARGET_ipq806x||TARGET_qualcommax) +kmod-usb-dwc3 KCONFIG:= CONFIG_USB_DWC3_QCOM FILES:= $(LINUX_DIR)/drivers/usb/dwc3/dwc3-qcom.ko AUTOLOAD:=$(call AutoLoad,53,dwc3-qcom,1) diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk index dc08df4f6f..c75630a8bb 100644 --- a/package/kernel/mac80211/ath.mk +++ b/package/kernel/mac80211/ath.mk @@ -317,7 +317,7 @@ define KernelPackage/ath11k/config config ATH11K_THERMAL bool "Enable thermal sensors and throttling support" depends on PACKAGE_kmod-ath11k - default y if TARGET_ipq807x + default y if TARGET_qualcommax endef @@ -325,7 +325,7 @@ define KernelPackage/ath11k-ahb $(call KernelPackage/mac80211/Default) TITLE:=Qualcomm 802.11ax AHB wireless chipset support URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k - DEPENDS+= @TARGET_ipq807x +kmod-ath11k +kmod-qrtr-smd + DEPENDS+= @TARGET_qualcommax +kmod-ath11k +kmod-qrtr-smd FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k_ahb.ko AUTOLOAD:=$(call AutoProbe,ath11k_ahb) endef diff --git a/package/kernel/qca-nss-dp/Makefile b/package/kernel/qca-nss-dp/Makefile index e9d9706141..aad49b8055 100644 --- a/package/kernel/qca-nss-dp/Makefile +++ b/package/kernel/qca-nss-dp/Makefile @@ -19,7 +19,7 @@ define KernelPackage/qca-nss-dp SECTION:=kernel CATEGORY:=Kernel modules SUBMENU:=Network Devices - DEPENDS:=@TARGET_ipq807x +kmod-qca-ssdk + DEPENDS:=@TARGET_qualcommax +kmod-qca-ssdk TITLE:=Qualcom NSS dataplane ethernet driver FILES:=$(PKG_BUILD_DIR)/qca-nss-dp.ko AUTOLOAD:=$(call AutoLoad,31,qca-nss-dp,1) diff --git a/package/kernel/qca-ssdk/Makefile b/package/kernel/qca-ssdk/Makefile index 92f7b66490..169252663b 100644 --- a/package/kernel/qca-ssdk/Makefile +++ b/package/kernel/qca-ssdk/Makefile @@ -20,7 +20,7 @@ define KernelPackage/qca-ssdk CATEGORY:=Kernel modules SUBMENU:=Network Devices TITLE:=Qualcom SSDK switch driver - DEPENDS:=@(TARGET_ipq807x) + DEPENDS:=@(TARGET_qualcommax) FILES:=$(PKG_BUILD_DIR)/build/bin/qca-ssdk.ko AUTOLOAD:=$(call AutoLoad,30,qca-ssdk) endef diff --git a/target/linux/ipq807x/Makefile b/target/linux/ipq807x/Makefile deleted file mode 100644 index e434288395..0000000000 --- a/target/linux/ipq807x/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -include $(TOPDIR)/rules.mk - -ARCH:=aarch64 -BOARD:=ipq807x -BOARDNAME:=Qualcomm Atheros IPQ807x -FEATURES:=squashfs ramdisk fpu nand rtc emmc -KERNELNAME:=Image dtbs -CPU_TYPE:=cortex-a53 -SUBTARGETS:=generic - -KERNEL_PATCHVER:=6.1 - -include $(INCLUDE_DIR)/target.mk -DEFAULT_PACKAGES += \ - kmod-usb3 kmod-usb-dwc3 kmod-usb-dwc3-qcom \ - kmod-leds-gpio kmod-gpio-button-hotplug \ - kmod-phy-aquantia kmod-qca-nss-dp \ - ath11k-firmware-ipq8074 kmod-ath11k-ahb \ - wpad-basic-mbedtls uboot-envtools \ - e2fsprogs kmod-fs-ext4 losetup - -$(eval $(call BuildTarget)) diff --git a/target/linux/ipq807x/base-files/etc/board.d/01_leds b/target/linux/ipq807x/base-files/etc/board.d/01_leds deleted file mode 100644 index a89b4c1564..0000000000 --- a/target/linux/ipq807x/base-files/etc/board.d/01_leds +++ /dev/null @@ -1,33 +0,0 @@ - -. /lib/functions/uci-defaults.sh - -board_config_update - -board=$(board_name) - -case "$board" in -edgecore,eap102) - ucidef_set_led_netdev "wan" "WAN" "green:wanpoe" "wan" - ;; -netgear,wax218) - ucidef_set_led_netdev "lan" "LAN" "blue:lan" "lan" - ucidef_set_led_wlan "wlan5g" "WIFI 5GHz" "blue:wlan5g" "phy0radio" - ucidef_set_led_wlan "wlan2g" "WIFI 2.4GHz" "blue:wlan2g" "phy1radio" - ;; -redmi,ax6|\ -xiaomi,ax3600) - ucidef_set_led_netdev "wan" "WAN" "blue:network" "wan" - ;; -qnap,301w) - ucidef_set_led_netdev "lan1" "LAN1" "green:lan1" "lan1" - ucidef_set_led_netdev "lan2" "LAN2" "green:lan2" "lan2" - ucidef_set_led_netdev "lan3" "LAN3" "green:lan3" "lan3" - ucidef_set_led_netdev "lan4" "LAN4" "green:lan4" "lan4" - ucidef_set_led_netdev "10G_1" "10G_1" "green:10g_1" "10g-1" - ucidef_set_led_netdev "10G_2" "10G_2" "green:10g_2" "10g-2" - ;; -esac - -board_config_flush - -exit 0 diff --git a/target/linux/ipq807x/base-files/etc/board.d/02_network b/target/linux/ipq807x/base-files/etc/board.d/02_network deleted file mode 100644 index 8175a99f97..0000000000 --- a/target/linux/ipq807x/base-files/etc/board.d/02_network +++ /dev/null @@ -1,53 +0,0 @@ -# -# Copyright (c) 2015 The Linux Foundation. All rights reserved. -# Copyright (c) 2011-2015 OpenWrt.org -# - -. /lib/functions/uci-defaults.sh -. /lib/functions/system.sh - -ipq807x_setup_interfaces() -{ - local board="$1" - - case "$board" in - buffalo,wxr-5950ax12|\ - dynalink,dl-wrx36|\ - xiaomi,ax9000) - ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "wan" - ;; - edgecore,eap102) - ucidef_set_interfaces_lan_wan "lan" "wan" - ;; - edimax,cax1800) - ucidef_set_interfaces_lan_wan "lan" - ;; - netgear,wax218) - ucidef_set_interface_lan "lan" "dhcp" - ;; - prpl,haze) - ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" - ;; - qnap,301w) - ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 10g-2" "10g-1" - ;; - compex,wpq873|\ - redmi,ax6|\ - xiaomi,ax3600) - ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" - ;; - zyxel,nbg7815) - ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 10g" "wan" - ;; - *) - echo "Unsupported hardware. Network interfaces not initialized" - ;; - esac -} - -board_config_update -board=$(board_name) -ipq807x_setup_interfaces $board -board_config_flush - -exit 0 diff --git a/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata b/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata deleted file mode 100644 index f9315573ec..0000000000 --- a/target/linux/ipq807x/base-files/etc/hotplug.d/firmware/11-ath11k-caldata +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -[ -e /lib/firmware/$FIRMWARE ] && exit 0 - -. /lib/functions/caldata.sh - -board=$(board_name) - -case "$FIRMWARE" in -"ath11k/IPQ8074/hw2.0/cal-ahb-c000000.wifi.bin") - case "$board" in - buffalo,wxr-5950ax12|\ - compex,wpq873|\ - edgecore,eap102|\ - edimax,cax1800|\ - dynalink,dl-wrx36|\ - netgear,wax218|\ - qnap,301w|\ - redmi,ax6|\ - xiaomi,ax3600|\ - xiaomi,ax9000|\ - zyxel,nbg7815) - caldata_extract "0:art" 0x1000 0x20000 - ;; - prpl,haze) - caldata_extract_mmc "0:ART" 0x1000 0x20000 - ;; - esac - ;; -"ath11k/QCN9074/hw1.0/cal-pci-0000:01:00.0.bin"|\ -"ath11k/QCN9074/hw1.0/cal-pci-0001:01:00.0.bin") - case "$board" in - prpl,haze) - caldata_extract_mmc "0:ART" 0x26800 0x20000 - ;; - xiaomi,ax9000) - caldata_extract "0:art" 0x26800 0x20000 - ;; - esac - ;; -*) - exit 1 - ;; -esac diff --git a/target/linux/ipq807x/base-files/etc/init.d/bootcount b/target/linux/ipq807x/base-files/etc/init.d/bootcount deleted file mode 100755 index 6917446a9b..0000000000 --- a/target/linux/ipq807x/base-files/etc/init.d/bootcount +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=99 - -boot() { - case $(board_name) in - edgecore,eap102) - fw_setenv upgrade_available 0 - # Unset changed flag after sysupgrade complete - fw_setenv changed - ;; - esac -} diff --git a/target/linux/ipq807x/base-files/lib/upgrade/buffalo.sh b/target/linux/ipq807x/base-files/lib/upgrade/buffalo.sh deleted file mode 100644 index d0ed258890..0000000000 --- a/target/linux/ipq807x/base-files/lib/upgrade/buffalo.sh +++ /dev/null @@ -1,55 +0,0 @@ -. /lib/functions.sh - -# Prepare UBI devices for OpenWrt installation -# - rootfs (mtd22) -# - remove "ubi_rootfs" volume (rootfs on stock) -# - remove "fw_hash" volume (firmware hash) -# - user_property (mtd24) -# - remove "user_property_ubi" volume (user configuration) -# - remove "extra_property" volume (gzipped syslog) -buffalo_upgrade_prepare() { - local ubi_rootdev ubi_propdev - - if ! ubi_rootdev="$(nand_attach_ubi rootfs)" || \ - ! ubi_propdev="$(nand_attach_ubi user_property)"; then - echo "failed to attach UBI volume \"rootfs\" or \"user_property\", rebooting..." - reboot -f - fi - - ubirmvol /dev/$ubi_rootdev -N ubi_rootfs &> /dev/null || true - ubirmvol /dev/$ubi_rootdev -N fw_hash &> /dev/null || true - - ubirmvol /dev/$ubi_propdev -N user_property_ubi &> /dev/null || true - ubirmvol /dev/$ubi_propdev -N extra_property &> /dev/null || true -} - -# Re-create small dummy ubi_rootfs volume and update -# fw_hash volume to pass the checking by U-Boot -# - rootfs (mtd22) -# - re-create "ubi_rootfs" volume -# - re-create and update "fw_hash" volume -# - rootfs_recover (mtd23) -# - update "fw_hash" volume -buffalo_upgrade_optvol() { - local ubi_rootdev ubi_rcvrdev - local hashvol_root hashvol_rcvr - - if ! ubi_rootdev="$(nand_attach_ubi rootfs)" || \ - ! ubi_rcvrdev="$(nand_attach_ubi rootfs_recover)"; then - echo "failed to attach UBI volume \"rootfs\" or \"rootfs_recover\", rebooting..." - reboot -f - fi - - ubimkvol /dev/$ubi_rootdev -N ubi_rootfs -S 1 - ubimkvol /dev/$ubi_rootdev -N fw_hash -S 1 -t static - - if ! hashvol_root="$(nand_find_volume $ubi_rootdev fw_hash)" || \ - ! hashvol_rcvr="$(nand_find_volume $ubi_rcvrdev fw_hash)"; then - echo "\"fw_hash\" volume in \"rootfs\" or \"rootfs_recover\" not found, rebooting..." - reboot -f - fi - - echo -n "00000000000000000000000000000000" > /tmp/dummyhash.txt - ubiupdatevol /dev/$hashvol_root /tmp/dummyhash.txt - ubiupdatevol /dev/$hashvol_rcvr /tmp/dummyhash.txt -} diff --git a/target/linux/ipq807x/base-files/lib/upgrade/mmc.sh b/target/linux/ipq807x/base-files/lib/upgrade/mmc.sh deleted file mode 100644 index dac9ddd568..0000000000 --- a/target/linux/ipq807x/base-files/lib/upgrade/mmc.sh +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (C) 2016 lede-project.org -# - -# this can be used as a generic mmc upgrade script -# just add a device entry in platform.sh, -# define "kernelname" and "rootfsname" and call mmc_do_upgrade -# after the kernel and rootfs flash a loopdev (as overlay) is -# setup on top of the rootfs partition -# for the proper function a padded rootfs image is needed, basically -# append "pad-to 64k" to the image definition -# this is based on the ipq806x zyxel.sh mmc upgrade - -. /lib/functions.sh - -mmc_do_upgrade() { - local tar_file="$1" - local rootfs= - local kernel= - - [ -z "$kernel" ] && kernel=$(find_mmc_part ${kernelname}) - [ -z "$rootfs" ] && rootfs=$(find_mmc_part ${rootfsname}) - - [ -z "$kernel" ] && echo "Upgrade failed: kernel partition not found! Rebooting..." && reboot -f - [ -z "$rootfs" ] && echo "Upgrade failed: rootfs partition not found! Rebooting..." && reboot -f - - mmc_do_flash $tar_file $kernel $rootfs - - return 0 -} - -mmc_do_flash() { - local tar_file=$1 - local kernel=$2 - local rootfs=$3 - - # keep sure its unbound - losetup --detach-all || { - echo Failed to detach all loop devices. Skip this try. - reboot -f - } - - # use the first found directory in the tar archive - local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') - board_dir=${board_dir%/} - - echo "flashing kernel to $kernel" - tar xf $tar_file ${board_dir}/kernel -O >$kernel - - echo "flashing rootfs to ${rootfs}" - tar xf $tar_file ${board_dir}/root -O >"${rootfs}" - - # a padded rootfs is needed for overlay fs creation - local offset=$(tar xf $tar_file ${board_dir}/root -O | wc -c) - [ $offset -lt 65536 ] && { - echo Wrong size for rootfs: $offset - sleep 10 - reboot -f - } - - # Mount loop for rootfs_data - local loopdev="$(losetup -f)" - losetup -o $offset $loopdev $rootfs || { - echo "Failed to mount looped rootfs_data." - sleep 10 - reboot -f - } - - echo "Format new rootfs_data at position ${offset}." - mkfs.ext4 -F -L rootfs_data $loopdev - mkdir /tmp/new_root - mount -t ext4 $loopdev /tmp/new_root && { - echo "Saving config to rootfs_data at position ${offset}." - cp -v "$UPGRADE_BACKUP" "/tmp/new_root/$BACKUP_FILE" - umount /tmp/new_root - } - - # Cleanup - losetup -d $loopdev >/dev/null 2>&1 - sync - umount -a - reboot -f -} diff --git a/target/linux/ipq807x/base-files/lib/upgrade/platform.sh b/target/linux/ipq807x/base-files/lib/upgrade/platform.sh deleted file mode 100644 index 458eb16b79..0000000000 --- a/target/linux/ipq807x/base-files/lib/upgrade/platform.sh +++ /dev/null @@ -1,117 +0,0 @@ -PART_NAME=firmware -REQUIRE_IMAGE_METADATA=1 - -RAMFS_COPY_BIN='fw_printenv fw_setenv head' -RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' - -xiaomi_initramfs_prepare() { - # Wipe UBI if running initramfs - [ "$(rootfs_type)" = "tmpfs" ] || return 0 - - local rootfs_mtdnum="$( find_mtd_index rootfs )" - if [ ! "$rootfs_mtdnum" ]; then - echo "unable to find mtd partition rootfs" - return 1 - fi - - local kern_mtdnum="$( find_mtd_index ubi_kernel )" - if [ ! "$kern_mtdnum" ]; then - echo "unable to find mtd partition ubi_kernel" - return 1 - fi - - ubidetach -m "$rootfs_mtdnum" - ubiformat /dev/mtd$rootfs_mtdnum -y - - ubidetach -m "$kern_mtdnum" - ubiformat /dev/mtd$kern_mtdnum -y -} - -platform_check_image() { - return 0; -} - -platform_pre_upgrade() { - case "$(board_name)" in - redmi,ax6|\ - xiaomi,ax3600|\ - xiaomi,ax9000) - xiaomi_initramfs_prepare - ;; - esac -} - -platform_do_upgrade() { - case "$(board_name)" in - buffalo,wxr-5950ax12) - CI_KERN_UBIPART="rootfs" - CI_ROOT_UBIPART="user_property" - buffalo_upgrade_prepare - nand_do_flash_file "$1" || nand_do_upgrade_failed - nand_do_restore_config || nand_do_upgrade_failed - buffalo_upgrade_optvol - ;; - dynalink,dl-wrx36) - nand_do_upgrade "$1" - ;; - edgecore,eap102) - active="$(fw_printenv -n active)" - if [ "$active" -eq "1" ]; then - CI_UBIPART="rootfs2" - else - CI_UBIPART="rootfs1" - fi - # force altbootcmd which handles partition change in u-boot - fw_setenv bootcount 3 - fw_setenv upgrade_available 1 - nand_do_upgrade "$1" - ;; - compex,wpq873|\ - edimax,cax1800|\ - netgear,wax218) - nand_do_upgrade "$1" - ;; - prpl,haze|\ - qnap,301w) - kernelname="0:HLOS" - rootfsname="rootfs" - mmc_do_upgrade "$1" - ;; - zyxel,nbg7815) - local config_mtdnum="$(find_mtd_index 0:bootconfig)" - [ -z "$config_mtdnum" ] && reboot - part_num="$(hexdump -e '1/1 "%01x|"' -n 1 -s 168 -C /dev/mtd$config_mtdnum | cut -f 1 -d "|" | head -n1)" - if [ "$part_num" -eq "0" ]; then - kernelname="0:HLOS" - rootfsname="rootfs" - mmc_do_upgrade "$1" - else - kernelname="0:HLOS_1" - rootfsname="rootfs_1" - mmc_do_upgrade "$1" - fi - ;; - redmi,ax6|\ - xiaomi,ax3600|\ - xiaomi,ax9000) - # Make sure that UART is enabled - fw_setenv boot_wait on - fw_setenv uart_en 1 - - # Enforce single partition. - fw_setenv flag_boot_rootfs 0 - fw_setenv flag_last_success 0 - fw_setenv flag_boot_success 1 - fw_setenv flag_try_sys1_failed 8 - fw_setenv flag_try_sys2_failed 8 - - # Kernel and rootfs are placed in 2 different UBI - CI_KERN_UBIPART="ubi_kernel" - CI_ROOT_UBIPART="rootfs" - nand_do_upgrade "$1" - ;; - *) - default_do_upgrade "$1" - ;; - esac -} diff --git a/target/linux/ipq807x/config-6.1 b/target/linux/ipq807x/config-6.1 deleted file mode 100644 index 2fa4aa30bb..0000000000 --- a/target/linux/ipq807x/config-6.1 +++ /dev/null @@ -1,560 +0,0 @@ -CONFIG_64BIT=y -# CONFIG_APQ_GCC_8084 is not set -# CONFIG_APQ_MMCC_8084 is not set -CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y -CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_ARCH_FORCE_MAX_ORDER=11 -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_NR_GPIO=0 -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_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_PAGE_SHIFT=12 -CONFIG_ARM64_PA_BITS=48 -CONFIG_ARM64_PA_BITS_48=y -CONFIG_ARM64_PTR_AUTH=y -CONFIG_ARM64_PTR_AUTH_KERNEL=y -CONFIG_ARM64_SME=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_GIC_V3_ITS_PCI=y -# CONFIG_ARM_MHU_V2 is not set -CONFIG_ARM_PSCI_CPUIDLE=y -CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y -CONFIG_ARM_PSCI_FW=y -# CONFIG_ARM_QCOM_CPUFREQ_HW is not set -CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y -CONFIG_AUDIT_ARCH_COMPAT_GENERIC=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_CAVIUM_TX2_ERRATUM_219=y -CONFIG_CC_HAVE_SHADOW_CALL_STACK=y -CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y -CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" -CONFIG_CC_NO_ARRAY_BOUNDS=y -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_PM=y -CONFIG_CPU_RMAP=y -CONFIG_CPU_THERMAL=y -CONFIG_CRC16=y -CONFIG_CRC8=y -CONFIG_CRYPTO_AUTHENC=y -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_QCE=y -CONFIG_CRYPTO_DEV_QCE_AEAD=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set -CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set -CONFIG_CRYPTO_DEV_QCE_SHA=y -CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y -CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 -CONFIG_CRYPTO_DEV_QCOM_RNG=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_HASH_INFO=y -CONFIG_CRYPTO_HW=y -CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y -CONFIG_CRYPTO_LIB_DES=y -CONFIG_CRYPTO_LIB_SHA1=y -CONFIG_CRYPTO_LIB_SHA256=y -CONFIG_CRYPTO_LIB_UTILS=y -CONFIG_CRYPTO_LZO=y -# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set -CONFIG_CRYPTO_RNG=y -CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=y -# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set -# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set -CONFIG_CRYPTO_XTS=y -CONFIG_CRYPTO_ZSTD=y -CONFIG_DCACHE_WORD_ACCESS=y -CONFIG_DEBUG_INFO=y -CONFIG_DEV_COREDUMP=y -CONFIG_DMADEVICES=y -CONFIG_DMA_DIRECT_REMAP=y -CONFIG_DMA_ENGINE=y -CONFIG_DMA_OF=y -CONFIG_DMA_VIRTUAL_CHANNELS=y -CONFIG_DTC=y -CONFIG_DT_IDLE_GENPD=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_FUJITSU_ERRATUM_010001=y -CONFIG_FWNODE_MDIO=y -CONFIG_FW_LOADER_PAGED_BUF=y -CONFIG_FW_LOADER_SYSFS=y -CONFIG_GCC11_NO_ARRAY_BOUNDS=y -CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=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_VULNERABILITIES=y -CONFIG_GENERIC_CSUM=y -CONFIG_GENERIC_EARLY_IOREMAP=y -CONFIG_GENERIC_GETTIMEOFDAY=y -CONFIG_GENERIC_IDLE_POLL_SETUP=y -CONFIG_GENERIC_IOREMAP=y -CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y -CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_IRQ_SHOW_LEVEL=y -CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y -CONFIG_GENERIC_MSI_IRQ=y -CONFIG_GENERIC_MSI_IRQ_DOMAIN=y -CONFIG_GENERIC_PCI_IOMAP=y -CONFIG_GENERIC_PHY=y -CONFIG_GENERIC_PINCONF=y -CONFIG_GENERIC_PINCTRL_GROUPS=y -CONFIG_GENERIC_PINMUX_FUNCTIONS=y -CONFIG_GENERIC_SCHED_CLOCK=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_GENERIC_STRNCPY_FROM_USER=y -CONFIG_GENERIC_STRNLEN_USER=y -CONFIG_GENERIC_TIME_VSYSCALL=y -CONFIG_GLOB=y -CONFIG_GPIOLIB_IRQCHIP=y -CONFIG_GPIO_CDEV=y -CONFIG_HARDIRQS_SW_RESEND=y -CONFIG_HAS_DMA=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT_MAP=y -CONFIG_HWSPINLOCK=y -CONFIG_HWSPINLOCK_QCOM=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_IPQ_APSS_6018=y -CONFIG_IPQ_APSS_PLL=y -# CONFIG_IPQ_GCC_4019 is not set -# CONFIG_IPQ_GCC_6018 is not set -# CONFIG_IPQ_GCC_806X is not set -CONFIG_IPQ_GCC_8074=y -# CONFIG_IPQ_LCC_806X 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_WORK=y -# CONFIG_KPSS_XCC is not set -CONFIG_LIBFDT=y -CONFIG_LOCK_DEBUGGING_SUPPORT=y -CONFIG_LOCK_SPIN_ON_OWNER=y -CONFIG_LZO_COMPRESS=y -CONFIG_LZO_DECOMPRESS=y -CONFIG_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_MDM_GCC_9615 is not set -# CONFIG_MDM_LCC_9615 is not set -CONFIG_MEMFD_CREATE=y -# CONFIG_MFD_HI6421_SPMI is not set -# CONFIG_MFD_QCOM_RPM is not set -CONFIG_MFD_SPMI_PMIC=y -CONFIG_MFD_SYSCON=y -CONFIG_MIGRATION=y -CONFIG_MMC=y -CONFIG_MMC_BLOCK=y -CONFIG_MMC_BLOCK_MINORS=32 -CONFIG_MMC_CQHCI=y -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_IO_ACCESSORS=y -CONFIG_MMC_SDHCI_MSM=y -# CONFIG_MMC_SDHCI_PCI is not set -CONFIG_MMC_SDHCI_PLTFM=y -CONFIG_MODULES_USE_ELF_RELA=y -# CONFIG_MSM_GCC_8660 is not set -# CONFIG_MSM_GCC_8909 is not set -# CONFIG_MSM_GCC_8916 is not set -# CONFIG_MSM_GCC_8939 is not set -# CONFIG_MSM_GCC_8960 is not set -# CONFIG_MSM_GCC_8974 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_LCC_8960 is not set -# CONFIG_MSM_MMCC_8960 is not set -# CONFIG_MSM_MMCC_8974 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_FLOW_LIMIT=y -CONFIG_NET_SELFTESTS=y -CONFIG_NET_SWITCHDEV=y -CONFIG_NLS=y -CONFIG_NO_HZ_COMMON=y -CONFIG_NO_HZ_IDLE=y -CONFIG_NR_CPUS=4 -CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y -CONFIG_NVMEM=y -CONFIG_NVMEM_QCOM_QFPROM=y -# CONFIG_NVMEM_SPMI_SDAM is not set -CONFIG_NVMEM_SYSFS=y -CONFIG_NVMEM_U_BOOT_ENV=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_PCI_DOMAINS=y -CONFIG_PCI_DOMAINS_GENERIC=y -CONFIG_PCI_MSI=y -CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PGTABLE_LEVELS=3 -CONFIG_PHYLIB=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_IPQ4019_USB is not set -# CONFIG_PHY_QCOM_IPQ806X_SATA is not set -# CONFIG_PHY_QCOM_IPQ806X_USB is not set -# CONFIG_PHY_QCOM_PCIE2 is not set -CONFIG_PHY_QCOM_QMP=y -CONFIG_PHY_QCOM_QUSB2=y -# 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_IPQ6018 is not set -CONFIG_PINCTRL_IPQ8074=y -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_SPMI_PMIC=y -# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set -# CONFIG_PINCTRL_QCS404 is not set -# CONFIG_PINCTRL_SC7180 is not set -# CONFIG_PINCTRL_SC8280XP is not set -# CONFIG_PINCTRL_SDM660 is not set -# CONFIG_PINCTRL_SDM845 is not set -# CONFIG_PINCTRL_SM6350 is not set -# CONFIG_PINCTRL_SM6375 is not set -# CONFIG_PINCTRL_SM8150 is not set -# CONFIG_PINCTRL_SM8250 is not set -# CONFIG_PINCTRL_SM8450 is not set -CONFIG_PM=y -# CONFIG_PM8916_WATCHDOG is not set -CONFIG_PM_CLK=y -CONFIG_PM_GENERIC_DOMAINS=y -CONFIG_PM_GENERIC_DOMAINS_OF=y -CONFIG_PM_OPP=y -CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y -CONFIG_POWER_RESET=y -# CONFIG_POWER_RESET_MSM is not set -# CONFIG_POWER_RESET_QCOM_PON is not set -CONFIG_POWER_SUPPLY=y -CONFIG_PREEMPT_NONE_BUILD=y -CONFIG_PRINTK_TIME=y -CONFIG_PTP_1588_CLOCK_OPTIONAL=y -# CONFIG_QCM_DISPCC_2290 is not set -# CONFIG_QCM_GCC_2290 is not set -# CONFIG_QCOM_A53PLL is not set -# CONFIG_QCOM_AOSS_QMP is not set -CONFIG_QCOM_APCS_IPC=y -CONFIG_QCOM_APM=y -# CONFIG_QCOM_APR is not set -CONFIG_QCOM_BAM_DMA=y -# CONFIG_QCOM_CLK_APCC_MSM8996 is not set -# CONFIG_QCOM_CLK_APCS_MSM8916 is not set -# CONFIG_QCOM_CLK_APCS_SDX55 is not set -# CONFIG_QCOM_COINCELL is not set -# CONFIG_QCOM_COMMAND_DB is not set -# CONFIG_QCOM_CPR is not set -# CONFIG_QCOM_EBI2 is not set -# CONFIG_QCOM_FASTRPC is not set -CONFIG_QCOM_GDSC=y -# 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_IPCC is not set -# CONFIG_QCOM_LLCC is not set -CONFIG_QCOM_MDT_LOADER=y -# CONFIG_QCOM_MPM is not set -# 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_MSS is not set -# CONFIG_QCOM_Q6V5_PAS is not set -CONFIG_QCOM_Q6V5_WCSS=y -# CONFIG_QCOM_RMTFS_MEM is not set -# CONFIG_QCOM_RPMH is not set -CONFIG_QCOM_RPROC_COMMON=y -CONFIG_QCOM_SCM=y -# CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT is not set -# 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_SPMI_ADC5=y -# CONFIG_QCOM_SPMI_RRADC is not set -# CONFIG_QCOM_STATS is not set -# CONFIG_QCOM_SYSMON is not set -CONFIG_QCOM_TSENS=y -CONFIG_QCOM_VADC_COMMON=y -# 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_QUEUED_RWLOCKS=y -CONFIG_QUEUED_SPINLOCKS=y -CONFIG_RANDSTRUCT_NONE=y -CONFIG_RAS=y -CONFIG_RATIONAL=y -CONFIG_REGMAP=y -CONFIG_REGMAP_MMIO=y -CONFIG_REGMAP_SPMI=y -CONFIG_REGULATOR=y -CONFIG_REGULATOR_CPR3=y -# CONFIG_REGULATOR_CPR3_NPU is not set -CONFIG_REGULATOR_CPR4_APSS=y -CONFIG_REGULATOR_FIXED_VOLTAGE=y -# CONFIG_REGULATOR_QCOM_LABIBB is not set -CONFIG_REGULATOR_QCOM_SPMI=y -# CONFIG_REGULATOR_QCOM_USB_VBUS is not set -# 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_DRV_PM8XXX=y -CONFIG_RTC_I2C_AND_SPI=y -CONFIG_RWSEM_SPIN_ON_OWNER=y -# CONFIG_SCHED_CORE is not set -CONFIG_SCHED_MC=y -CONFIG_SCHED_SMT=y -CONFIG_SCHED_THERMAL_PRESSURE=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_DISPCC_7180 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_LPASS_CORECC_7180 is not set -# CONFIG_SC_LPASS_CORECC_7280 is not set -# CONFIG_SC_MSS_7180 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_65 is not set -CONFIG_SERIAL_8250_FSL=y -CONFIG_SERIAL_MCTRL_GPIO=y -CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y -CONFIG_SGL_ALLOC=y -CONFIG_SG_POOL=y -CONFIG_SMP=y -# CONFIG_SM_CAMCC_8450 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_GPUCC_6350 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_VIDEOCC_8150 is not set -# CONFIG_SM_VIDEOCC_8250 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_QUP=y -CONFIG_SPMI=y -# CONFIG_SPMI_HISI3670 is not set -CONFIG_SPMI_MSM_PMIC_ARB=y -# CONFIG_SPMI_PMIC_CLKDIV is not set -CONFIG_SRCU=y -CONFIG_SWIOTLB=y -CONFIG_SWPHY=y -CONFIG_SYSCTL_EXCEPTION_TRACE=y -CONFIG_THERMAL=y -CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 -CONFIG_THERMAL_GOV_STEP_WISE=y -CONFIG_THERMAL_OF=y -CONFIG_THREAD_INFO_IN_TASK=y -CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TIMER_OF=y -CONFIG_TIMER_PROBE=y -CONFIG_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_VIRTIO=y -CONFIG_VIRTIO_ANCHOR=y -# CONFIG_VIRTIO_BLK 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/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts deleted file mode 100644 index 2c9cbd5b3c..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts +++ /dev/null @@ -1,322 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2021, Dirk Buchwalder */ - -/dts-v1/; - -#include "ipq8074-512m.dtsi" -#include "ipq8074-ac-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include - -/ { - model = "Edimax CAX1800"; - compatible = "edimax,cax1800", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - led-boot = &led_system_red; - led-failsafe = &led_system_red; - led-running = &led_system_green; - led-upgrade = &led_system_red; - /* Aliases as required by u-boot to patch MAC addresses */ - ethernet0 = &dp5; - label-mac-device = &dp5; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " root=/dev/ubiblock0_1"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - leds { - compatible = "gpio-leds"; - - led_system_red: system-red { - label = "red:system"; - gpios = <&tlmm 25 GPIO_ACTIVE_HIGH>; - }; - - led_system_green: system-green { - label = "green:system"; - gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; - }; - - led_system_blue: system-blue { - label = "blue:system"; - gpios = <&tlmm 27 GPIO_ACTIVE_HIGH>; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "rootfs"; - reg = <0x0000000 0x3400000>; - }; - }; - }; -}; - -&blsp1_spi1 { - pinctrl-0 = <&spi_0_pins>; - pinctrl-names = "default"; - cs-select = <0>; - status = "ok"; - - m25p80@0 { - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - compatible = "jedec,spi-nor"; - spi-max-frequency = <50000000>; - use-default-sizes; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "0:sbl1"; - reg = <0x0 0x50000>; - read-only; - }; - - partition@50000 { - label = "0:mibib"; - reg = <0x50000 0x10000>; - read-only; - }; - - partition@60000 { - label = "0:bootconfig"; - reg = <0x60000 0x20000>; - read-only; - }; - - partition@80000 { - label = "0:bootconfig1"; - reg = <0x80000 0x20000>; - read-only; - }; - - partition@a0000 { - label = "0:qsee"; - reg = <0xa0000 0x180000>; - read-only; - }; - - partition@220000 { - label = "0:qsee_1"; - reg = <0x220000 0x180000>; - read-only; - }; - - partition@3a0000 { - label = "0:devcfg"; - reg = <0x3a0000 0x10000>; - read-only; - }; - - partition@3b0000 { - label = "0:devcfg_1"; - reg = <0x3b0000 0x10000>; - read-only; - }; - - partition@3c0000 { - label = "0:apdp"; - reg = <0x3c0000 0x10000>; - read-only; - }; - - partition@3d0000 { - label = "0:apdp_1"; - reg = <0x3d0000 0x10000>; - read-only; - }; - - partition@3e0000 { - label = "0:rpm"; - reg = <0x3e0000 0x40000>; - read-only; - }; - - partition@420000 { - label = "0:rpm_1"; - reg = <0x420000 0x40000>; - read-only; - }; - - partition@460000 { - label = "0:cdt"; - reg = <0x460000 0x10000>; - read-only; - }; - - partition@470000 { - label = "0:cdt_1"; - reg = <0x470000 0x10000>; - read-only; - }; - - partition@480000 { - label = "0:appsblenv"; - reg = <0x480000 0x10000>; - }; - - partition@490000 { - label = "0:appsbl"; - reg = <0x490000 0xa0000>; - read-only; - }; - - partition@530000 { - label = "0:appsbl_1"; - reg = <0x530000 0xa0000>; - read-only; - }; - - partition@5d0000 { - label = "0:art"; - reg = <0x5d0000 0x40000>; - read-only; - }; - - partition@610000 { - label = "0:ethphyfw"; - reg = <0x610000 0x80000>; - read-only; - }; - }; - }; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - qca8075: ethernet-phy@4 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <4>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x1e>; /* lan port bitmap */ - switch_wan_bmp = <0x20>; /* wan port bitmap */ - switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0>; - }; - port@1 { - port_id = <2>; - phy_address = <1>; - }; - port@2 { - port_id = <3>; - phy_address = <2>; - }; - port@3 { - port_id = <4>; - phy_address = <3>; - }; - port@4 { - port_id = <5>; - phy_address = <4>; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp5 { - status = "okay"; - phy-handle = <&qca8075>; - label = "lan"; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Edimax-CAX1800"; - qcom,ath11k-fw-memory-mode = <1>; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts deleted file mode 100644 index f3e82e2251..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2021, Robert Marko */ - -/dts-v1/; - -#include "ipq8071-ax3600.dtsi" - -/ { - model = "Xiaomi AX3600"; - compatible = "xiaomi,ax3600", "qcom,ipq8074"; - - leds { - compatible = "gpio-leds"; - - led_system_blue: system-blue { - label = "blue:system"; - gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; - }; - - led_system_yellow: system-yellow { - label = "yellow:system"; - gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; - }; - - network-yellow { - label = "yellow:network"; - gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; - }; - - network-blue { - label = "blue:network"; - gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; - }; - - aiot { - label = "blue:aiot"; - gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "phy0tpt"; - }; - }; -}; - -&pcie_qmp0 { - status = "okay"; -}; - -&pcie0 { - status = "okay"; - - perst-gpio = <&tlmm 52 GPIO_ACTIVE_HIGH>; - - bridge@0,0 { - reg = <0x00000000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - - wifi0: wifi@1,0 { - status = "okay"; - - compatible = "qcom,ath10k"; - reg = <0x00010000 0 0 0 0>; - - qcom,ath10k-calibration-variant = "Xiaomi-AX3600"; - nvmem-cell-names = "calibration"; - nvmem-cells = <&caldata_qca9889>; - }; - }; -}; - -&wifi { - qcom,ath11k-calibration-variant = "Xiaomi-AX3600"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi deleted file mode 100644 index c18cef52f3..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2021, Robert Marko */ - -#include "ipq8074-512m.dtsi" -#include "ipq8074-ac-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include - -/ { - aliases { - serial0 = &blsp1_uart5; - led-boot = &led_system_yellow; - led-failsafe = &led_system_yellow; - led-running = &led_system_blue; - led-upgrade = &led_system_yellow; - label-mac-device = &dp2; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " root=/dev/ubiblock0_0"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 34 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - /* - * Bootloader will find the NAND DT node by the compatible and - * then "fixup" it by adding the partitions from the SMEM table - * using the legacy bindings thus making it impossible for us - * to change the partition table or utilize NVMEM for calibration. - * So add a dummy partitions node that bootloader will populate - * and set it as disabled so the kernel ignores it instead of - * printing warnings due to the broken way bootloader adds the - * partitions. - */ - partitions { - status = "disabled"; - }; - - nand@0 { - reg = <0>; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "0:sbl1"; - reg = <0x0 0x100000>; - read-only; - }; - - partition@100000 { - label = "0:mibib"; - reg = <0x100000 0x100000>; - read-only; - }; - - partition@200000 { - label = "0:qsee"; - reg = <0x200000 0x300000>; - read-only; - }; - - partition@500000 { - label = "0:devcfg"; - reg = <0x500000 0x80000>; - read-only; - }; - - partition@580000 { - label = "0:rpm"; - reg = <0x580000 0x80000>; - read-only; - }; - - partition@600000 { - label = "0:cdt"; - reg = <0x600000 0x80000>; - read-only; - }; - - partition@680000 { - label = "0:appsblenv"; - reg = <0x680000 0x80000>; - }; - - partition@700000 { - label = "0:appsbl"; - reg = <0x700000 0x100000>; - read-only; - }; - - partition@800000 { - label = "0:art"; - reg = <0x800000 0x80000>; - read-only; - - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_dp2: macaddr@6 { - reg = <0x6 0x6>; - }; - - macaddr_dp3: macaddr@c { - reg = <0xc 0x6>; - }; - - macaddr_dp4: macaddr@12 { - reg = <0x12 0x6>; - }; - - macaddr_dp5: macaddr@18 { - reg = <0x18 0x6>; - }; - - caldata_qca9889: caldata@4d000 { - reg = <0x33000 0x844>; - }; - }; - - partition@880000 { - label = "bdata"; - reg = <0x880000 0x80000>; - }; - - partition@900000 { - /* This is crash + crash_syslog parts combined */ - label = "pstore"; - reg = <0x900000 0x100000>; - }; - - /* Make the first rootfs a dedicated ubi partition for kernel */ - partition@a00000 { - label = "ubi_kernel"; - reg = <0xa00000 0x23c0000>; - }; - - /* Place the real rootfs in the original second rootfs and - * expand it to the end of the nand - */ - rootfs: partition@2dc0000 { - label = "rootfs"; - reg = <0x2dc0000 0xd240000>; - }; - }; - }; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - qca8075_1: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - - qca8075_2: ethernet-phy@2 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <2>; - }; - - qca8075_3: ethernet-phy@3 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <3>; - }; - - qca8075_4: ethernet-phy@4 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <4>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x1e>; /* lan port bitmap */ - switch_wan_bmp = <0x20>; /* wan port bitmap */ - switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0>; - }; - port@1 { - port_id = <2>; - phy_address = <1>; - }; - port@2 { - port_id = <3>; - phy_address = <2>; - }; - port@3 { - port_id = <4>; - phy_address = <3>; - }; - port@4 { - port_id = <5>; - phy_address = <4>; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_1>; - label = "wan"; - nvmem-cells = <&macaddr_dp2>; - nvmem-cell-names = "mac-address"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan1"; - nvmem-cells = <&macaddr_dp3>; - nvmem-cell-names = "mac-address"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan2"; - nvmem-cells = <&macaddr_dp4>; - nvmem-cell-names = "mac-address"; -}; - -&dp5 { - status = "okay"; - phy-handle = <&qca8075_4>; - label = "lan3"; - nvmem-cells = <&macaddr_dp5>; - nvmem-cell-names = "mac-address"; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-fw-memory-mode = <1>; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts deleted file mode 100644 index 6611a8fe27..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2021, Zhijun You */ - -/dts-v1/; - -#include "ipq8071-ax3600.dtsi" - -/ { - model = "Redmi AX6"; - compatible = "redmi,ax6", "qcom,ipq8074"; - - leds { - compatible = "gpio-leds"; - - led_system_blue: system-blue { - label = "blue:system"; - gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; - }; - - led_system_yellow: system-yellow { - label = "yellow:system"; - gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; - }; - - network-blue { - label = "blue:network"; - gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; - }; - - network-yellow { - label = "yellow:network"; - gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; - }; - }; -}; - -/* AX6 can both have NAND of 256MiB or 128MiB. - * To be on the safe side, assume 128MiB of NAND. - */ -&rootfs { - reg = <0x2dc0000 0x5220000>; -}; - -&wifi { - qcom,ath11k-calibration-variant = "Redmi-AX6"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts deleted file mode 100644 index 357b6368d9..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts +++ /dev/null @@ -1,389 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2022, Matthew Hagan */ - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-ac-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include - -/ { - model = "Edgecore EAP102"; - compatible = "edgecore,eap102", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - serial1 = &blsp1_uart3; - led-boot = &led_system_green; - led-failsafe = &led_system_green; - led-running = &led_system_green; - led-upgrade = &led_system_green; - /* Aliases as required by u-boot to patch MAC addresses */ - ethernet0 = &dp5; - ethernet1 = &dp6; - label-mac-device = &dp5; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " root=/dev/ubiblock0_1"; - }; - - keys { - compatible = "gpio-keys"; - pinctrl-0 = <&button_pins>; - pinctrl-names = "default"; - - reset { - label = "reset"; - gpios = <&tlmm 66 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - leds { - compatible = "gpio-leds"; - - led_wanpoe { - label = "green:wanpoe"; - gpios = <&tlmm 46 GPIO_ACTIVE_HIGH>; - }; - - led_wlan2g { - label = "green:wlan2g"; - gpio = <&tlmm 47 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "phy1radio"; - }; - - led_wlan5g { - label = "green:wlan5g"; - gpio = <&tlmm 48 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "phy0radio"; - }; - - led_system_green: led_system { - label = "green:power"; - gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - button_pins: button_pins { - reset_button { - pins = "gpio66"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_spi1 { - status = "okay"; - - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - compatible = "jedec,spi-nor"; - spi-max-frequency = <50000000>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "0:sbl1"; - reg = <0x0 0x50000>; - read-only; - }; - - partition@50000 { - label = "0:mibib"; - reg = <0x50000 0x10000>; - read-only; - }; - - partition@60000 { - label = "0:bootconfig"; - reg = <0x60000 0x20000>; - read-only; - }; - - partition@80000 { - label = "0:bootconfig1"; - reg = <0x80000 0x20000>; - read-only; - }; - - partition@a0000 { - label = "0:qsee"; - reg = <0xa0000 0x180000>; - read-only; - }; - - partition@220000 { - label = "0:qsee_1"; - reg = <0x220000 0x180000>; - read-only; - }; - - partition@3a0000 { - label = "0:devcfg"; - reg = <0x3a0000 0x10000>; - read-only; - }; - - partition@3b0000 { - label = "0:devcfg_1"; - reg = <0x3b0000 0x10000>; - read-only; - }; - - partition@3c0000 { - label = "0:apdp"; - reg = <0x3c0000 0x10000>; - read-only; - }; - - partition@3d0000 { - label = "0:apdp_1"; - reg = <0x3d0000 0x10000>; - read-only; - }; - - partition@3e0000 { - label = "0:rpm"; - reg = <0x3e0000 0x40000>; - read-only; - }; - - partition@420000 { - label = "0:rpm_1"; - reg = <0x420000 0x40000>; - read-only; - }; - - partition@460000 { - label = "0:cdt"; - reg = <0x460000 0x10000>; - read-only; - }; - - partition@470000 { - label = "0:cdt_1"; - reg = <0x470000 0x10000>; - read-only; - }; - - partition@480000 { - label = "0:appsblenv"; - reg = <0x480000 0x10000>; - }; - - partition@490000 { - label = "0:appsbl"; - reg = <0x490000 0xc0000>; - read-only; - }; - - partition@550000 { - label = "0:appsbl_1"; - reg = <0x530000 0xc0000>; - read-only; - }; - - partition@610000 { - label = "0:art"; - reg = <0x610000 0x40000>; - read-only; - }; - - partition@650000 { - label = "0:ethphyfw"; - reg = <0x650000 0x80000>; - read-only; - }; - - partition@6d0000 { - label = "0:product_info"; - reg = <0x6d0000 0x80000>; - read-only; - }; - - partition@750000 { - label = "priv_data1"; - reg = <0x750000 0x10000>; - read-only; - }; - - partition@760000 { - label = "priv_data2"; - reg = <0x760000 0x10000>; - read-only; - }; - }; - }; -}; - -&blsp1_uart3 { - status = "okay"; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-strength = <8>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "rootfs1"; - reg = <0x0000000 0x3400000>; - }; - - partition@3400000 { - label = "0:wififw"; - reg = <0x3400000 0x800000>; - read-only; - }; - - partition@3c00000 { - label = "rootfs2"; - reg = <0x3c00000 0x3400000>; - }; - - partition@7000000 { - label = "0:wififw_1"; - reg = <0x7000000 0x800000>; - read-only; - }; - }; - }; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - - qca8081_24: ethernet-phy@24 { - compatible = "ethernet-phy-id004d.d101"; - reg = <24>; - reset-gpios = <&tlmm 33 GPIO_ACTIVE_LOW>; - }; - - qca8081_28: ethernet-phy@28 { - compatible = "ethernet-phy-id004d.d101"; - reg = <28>; - reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x3e>; /* lan port bitmap */ - switch_wan_bmp = <0x40>; /* wan port bitmap */ - switch_mac_mode = <0xff>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xf>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xf>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@4 { - port_id = <5>; - phy_address = <24>; - port_mac_sel = "QGMAC_PORT"; - }; - port@5 { - port_id = <6>; - phy_address = <28>; - port_mac_sel = "QGMAC_PORT"; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp5 { - status = "okay"; - phy-handle = <&qca8081_28>; - label = "wan"; -}; - -&dp6 { - status = "okay"; - phy-handle = <&qca8081_24>; - label = "lan"; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Edgecore-EAP102"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts deleted file mode 100644 index 5521a480b8..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts +++ /dev/null @@ -1,410 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2021, Dirk Buchwalder */ - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - -/ { - model = "QNAP 301w"; - compatible = "qnap,301w", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - /* - * Aliases as required by u-boot - * to patch MAC addresses - */ - led-boot = &led_system_red; - led-failsafe = &led_system_red; - led-running = &led_pwr_green; - led-upgrade = &led_system_red; - ethernet0 = &dp1; - ethernet1 = &dp2; - ethernet2 = &dp3; - ethernet3 = &dp4; - ethernet4 = &dp5; - ethernet5 = &dp6_syn; - label-mac-device = &dp1; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - keys { - compatible = "gpio-keys"; - pinctrl-0 = <&button_pins>; - pinctrl-names = "default"; - - wps-button { - label = "wps"; - gpios = <&tlmm 57 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - reset-button { - label = "reset"; - gpios = <&tlmm 67 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - leds { - compatible = "gpio-leds"; - pinctrl-0 = <&leds_pins>; - pinctrl-names = "default"; - - led_system_green: led-system-green { - label = "green:system"; - gpios = <&tlmm 1 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_system_red: led-system-red { - label = "red:system"; - gpios = <&tlmm 3 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_pwr_green: led-pwr-green { - label = "green:pwr"; - gpios = <&tlmm 4 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-wifi-green { - label = "green:wifi"; - gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan4-green { - label = "green:lan4"; - gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan4-amber { - label = "amber:lan4"; - gpios = <&tlmm 7 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan3-green { - label = "green:lan3"; - gpios = <&tlmm 8 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan3-amber { - label = "amber:lan3"; - gpios = <&tlmm 11 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan2-green { - label = "green:lan2"; - gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan2-amber { - label = "amber:lan2"; - gpios = <&tlmm 13 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan1-green { - label = "green:lan1"; - gpios = <&tlmm 14 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-lan1-amber { - label = "amber:lan1"; - gpios = <&tlmm 15 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-10g-1-green { - label = "green:10g_1"; - gpios = <&tlmm 54 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-10g-1-amber { - label = "amber:10g_1"; - gpios = <&tlmm 56 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-10g-2-green { - label = "green:10g_2"; - gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-10g-2-amber { - label = "amber:10g_2"; - gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; - color = ; - }; - }; -}; - -&tlmm { - - mdio_pins: mdio-state { - mdc-pins { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio-pins { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - button_pins: button-state { - wps-pins { - pins = "gpio57"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; - - rst-pins { - pins = "gpio67"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - leds_pins: leds-state { - pins = "gpio1", "gpio3", "gpio4", "gpio6", "gpio7", "gpio8", - "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", "gpio42", - "gpio51", "gpio52", "gpio54", "gpio56"; - function = "gpio"; - drive-strength = <8>; - bias-pull-down; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&ssphy_1 { - status = "okay"; -}; - -&qusb_phy_1 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&usb_1 { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&blsp1_spi1 { /* BLSP1 QUP1 */ - pinctrl-0 = <&spi_0_pins>; - pinctrl-names = "default"; - cs-gpios = <0>; - status = "okay"; - - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - compatible = "jedec,spi-nor"; - spi-max-frequency = <50000000>; - - partitions { - compatible = "qcom,smem-part"; - }; - }; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - aqr113c_0: ethernet-phy@0 { - compatible ="ethernet-phy-ieee802.3-c45"; - reg = <0>; - reset-gpios = <&tlmm 59 GPIO_ACTIVE_LOW>; - }; - - aqr113c_8: ethernet-phy@8 { - compatible ="ethernet-phy-ieee802.3-c45"; - reg = <8>; - reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - }; - - qca8075_16: ethernet-phy@16 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <16>; - }; - - qca8075_17: ethernet-phy@17 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <17>; - }; - - qca8075_18: ethernet-phy@18 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <18>; - }; - - qca8075_19: ethernet-phy@19 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <19>; - }; -}; - -&sdhc_1 { - status = "okay"; - - /* According to the stock dts from the QNAP gpl drop - * the emmc has a problem with the hs400 > hs200 speed switch. - * Therefore remove the mmc-hs400-1_8v property - */ - /delete-property/ mmc-hs400-1_8v; - mmc-hs200-1_8v; - mmc-ddr-1_8v; - vqmmc-supply = <&l11>; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x3e>; /* lan port bitmap */ - switch_wan_bmp = <0xc0>; /* wan port bitmap */ - switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xd>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xd>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <16>; - }; - port@1 { - port_id = <2>; - phy_address = <17>; - }; - port@2 { - port_id = <3>; - phy_address = <18>; - }; - port@3 { - port_id = <4>; - phy_address = <19>; - }; - port@4 { - port_id = <5>; - phy_address = <8>; - compatible = "ethernet-phy-ieee802.3-c45"; - ethernet-phy-ieee802.3-c45; - }; - port@5 { - port_id = <6>; - phy_address = <0>; - compatible = "ethernet-phy-ieee802.3-c45"; - ethernet-phy-ieee802.3-c45; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp1 { - status = "okay"; - phy-handle = <&qca8075_16>; - label = "lan4"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_17>; - label = "lan3"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_18>; - label = "lan2"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_19>; - label = "lan1"; -}; - -&dp5 { - status = "okay"; - qcom,mactype = <1>; - phy-handle = <&aqr113c_8>; - label = "10g-1"; -}; - -&dp6_syn { - status = "okay"; - phy-handle = <&aqr113c_0>; - label = "10g-2"; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "QNAP-301w"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts deleted file mode 100644 index 801aa05604..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts +++ /dev/null @@ -1,522 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2021, Robert Marko */ - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - -/ { - model = "Xiaomi AX9000"; - compatible = "xiaomi,ax9000", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - led-boot = &led_system_yellow; - led-failsafe = &led_system_yellow; - led-running = &led_system_blue; - led-upgrade = &led_system_yellow; - label-mac-device = &dp5; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " root=/dev/ubiblock0_0"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 47 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - wps { - label = "wps"; /* Labeled Mesh on the device */ - gpios = <&tlmm 46 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - leds { - compatible = "gpio-leds"; - - led_system_blue: system-blue { - label = "blue:system"; - gpios = <&tlmm 48 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_system_yellow: system-yellow { - label = "yellow:system"; - gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; - color = ; - }; - - network-yellow { - label = "yellow:network"; - gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; - color = ; - }; - - network-blue { - label = "blue:network"; - gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; - color = ; - }; - - top-red { - label = "red:top"; - gpios = <&tlmm 63 GPIO_ACTIVE_HIGH>; - color = ; - default-state = "keep"; - }; - - top-green { - label = "green:top"; - gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>; - color = ; - default-state = "keep"; - }; - - top-blue { - label = "blue:top"; - gpios = <&tlmm 66 GPIO_ACTIVE_HIGH>; - color = ; - default-state = "keep"; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - i2c_pins: i2c-pins { - pins = "gpio0", "gpio2"; - function = "blsp5_i2c"; - drive-strength = <8>; - bias-disable; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&blsp1_i2c6 { - status = "okay"; - - pinctrl-0 = <&i2c_pins>; - pinctrl-names = "default"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - /* - * Bootloader will find the NAND DT node by the compatible and - * then "fixup" it by adding the partitions from the SMEM table - * using the legacy bindings thus making it impossible for us - * to change the partition table or utilize NVMEM for calibration. - * So add a dummy partitions node that bootloader will populate - * and set it as disabled so the kernel ignores it instead of - * printing warnings due to the broken way bootloader adds the - * partitions. - */ - partitions { - status = "disabled"; - }; - - nand@0 { - reg = <0>; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "0:sbl1"; - reg = <0x0 0x100000>; - read-only; - }; - - partition@100000 { - label = "0:mibib"; - reg = <0x100000 0x100000>; - read-only; - }; - - partition@200000 { - label = "0:bootconfig"; - reg = <0x200000 0x80000>; - read-only; - }; - - partition@280000 { - label = "0:bootconfig1"; - reg = <0x280000 0x80000>; - read-only; - }; - - partition@300000 { - label = "0:qsee"; - reg = <0x300000 0x300000>; - read-only; - }; - - partition@600000 { - label = "0:qsee_1"; - reg = <0x600000 0x300000>; - read-only; - }; - - partition@900000 { - label = "0:devcfg"; - reg = <0x900000 0x80000>; - read-only; - }; - - partition@980000 { - label = "0:devcfg_1"; - reg = <0x980000 0x80000>; - read-only; - }; - - partition@a00000 { - label = "0:apdp"; - reg = <0xa00000 0x80000>; - read-only; - }; - - partition@a80000 { - label = "0:apdp_1"; - reg = <0xa80000 0x80000>; - read-only; - }; - - partition@b00000 { - label = "0:rpm"; - reg = <0xb00000 0x80000>; - read-only; - }; - - partition@b80000 { - label = "0:rpm_1"; - reg = <0xb80000 0x80000>; - read-only; - }; - - partition@c00000 { - label = "0:cdt"; - reg = <0xc00000 0x80000>; - read-only; - }; - - partition@c80000 { - label = "0:cdt_1"; - reg = <0xc80000 0x80000>; - read-only; - }; - - partition@d00000 { - label = "0:appsblenv"; - reg = <0xd00000 0x80000>; - }; - - partition@d80000 { - label = "0:appsbl"; - reg = <0xd80000 0x100000>; - read-only; - }; - - partition@e80000 { - label = "0:appsbl_1"; - reg = <0xe80000 0x100000>; - read-only; - }; - - partition@f80000 { - label = "0:art"; - reg = <0xf80000 0x80000>; - read-only; - - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_dp1: macaddr@0 { - reg = <0x0 0x6>; - }; - - macaddr_dp2: macaddr@6 { - reg = <0x6 0x6>; - }; - - macaddr_dp3: macaddr@c { - reg = <0xc 0x6>; - }; - - macaddr_dp4: macaddr@12 { - reg = <0x12 0x6>; - }; - - macaddr_dp5: macaddr@18 { - reg = <0x18 0x6>; - }; - - caldata_qca9889: caldata@4d000 { - reg = <0x4d000 0x844>; - }; - }; - - partition@1000000 { - label = "bdata"; - reg = <0x1000000 0x80000>; - }; - - partition@1080000 { - /* This is crash + crash_syslog parts combined */ - label = "pstore"; - reg = <0x1080000 0x100000>; - }; - - partition@1180000 { - label = "ubi_kernel"; - reg = <0x1180000 0x3800000>; - }; - - partition@4980000 { - label = "rootfs"; - reg = <0x4980000 0xb680000>; - }; - }; - }; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - qca8075_0: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0>; - }; - - qca8075_1: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - - qca8075_2: ethernet-phy@2 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <2>; - }; - - qca8075_3: ethernet-phy@3 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <3>; - }; - - qca8081: ethernet-phy@24 { - compatible = "ethernet-phy-id004d.d101"; - reg = <24>; - reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x1e>; /* lan port bitmap */ - switch_wan_bmp = <0x20>; /* wan port bitmap */ - switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xc>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0>; - }; - port@1 { - port_id = <2>; - phy_address = <1>; - }; - port@2 { - port_id = <3>; - phy_address = <2>; - }; - port@3 { - port_id = <4>; - phy_address = <3>; - }; - port@4 { - port_id = <5>; - phy_address = <24>; - port_mac_sel = "QGMAC_PORT"; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp1 { - status = "okay"; - phy-handle = <&qca8075_0>; - label = "lan4"; - nvmem-cells = <&macaddr_dp1>; - nvmem-cell-names = "mac-address"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_1>; - label = "lan3"; - nvmem-cells = <&macaddr_dp2>; - nvmem-cell-names = "mac-address"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan2"; - nvmem-cells = <&macaddr_dp3>; - nvmem-cell-names = "mac-address"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan1"; - nvmem-cells = <&macaddr_dp4>; - nvmem-cell-names = "mac-address"; -}; - -&dp5 { - status = "okay"; - phy-handle = <&qca8081>; - label = "wan"; - nvmem-cells = <&macaddr_dp5>; - nvmem-cell-names = "mac-address"; -}; - -&pcie_qmp0 { - status = "okay"; -}; - -&pcie0 { - status = "okay"; - - perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; - - bridge@0,0 { - reg = <0x00000000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - - wifi@1,0 { - status = "okay"; - - /* ath11k has no DT compatible for PCI cards */ - compatible = "pci17cb,1104"; - reg = <0x00010000 0 0 0 0>; - - qcom,ath11k-calibration-variant = "Xiaomi-AX9000"; - }; - }; -}; - -&pcie_qmp1 { - status = "okay"; -}; - -&pcie1 { - status = "okay"; - - perst-gpio = <&tlmm 62 GPIO_ACTIVE_HIGH>; - - bridge@1,0 { - reg = <0x00010000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - - wifi@1,0 { - status = "okay"; - - compatible = "qcom,ath10k"; - reg = <0x00010000 0 0 0 0>; - - qcom,ath10k-calibration-variant = "Xiaomi-AX9000"; - nvmem-cell-names = "calibration"; - nvmem-cells = <&caldata_qca9889>; - }; - }; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Xiaomi-AX9000"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts deleted file mode 100644 index 5468e9e1fb..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts +++ /dev/null @@ -1,243 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright (c) 2022, Robert Marko */ - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - -/ { - model = "Dynalink DL-WRX36"; - compatible = "dynalink,dl-wrx36", "qcom,ipq8074"; - - aliases { - led-boot = &led_system_red; - led-failsafe = &led_system_red; - led-running = &led_system_blue; - led-upgrade = &led_system_red; - serial0 = &blsp1_uart5; - /* Aliases as required by u-boot to patch MAC addresses */ - ethernet0 = &dp6_syn; - ethernet1 = &dp4; - ethernet2 = &dp3; - ethernet3 = &dp2; - ethernet4 = &dp1; - label-mac-device = &dp6_syn; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " root=/dev/ubiblock0_1"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 34 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - wps { - label = "wps"; - gpios = <&tlmm 63 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - leds { - compatible = "gpio-leds"; - - led_system_blue: system-blue { - label = "blue:system"; - gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_system_red: system-red { - label = "red:system"; - gpios = <&tlmm 25 GPIO_ACTIVE_HIGH>; - color = ; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-strength = <8>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "qcom,smem-part"; - }; - }; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - qca8075_0: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0>; - }; - - qca8075_1: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - - qca8075_2: ethernet-phy@2 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <2>; - }; - - qca8075_3: ethernet-phy@3 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <3>; - }; - - qca8081: ethernet-phy@28 { - compatible = "ethernet-phy-id004d.d101"; - reg = <28>; - reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x3e>; /* lan port bitmap */ - switch_wan_bmp = <0x40>; /* wan port bitmap */ - switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xc>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0>; - }; - port@1 { - port_id = <2>; - phy_address = <1>; - }; - port@2 { - port_id = <3>; - phy_address = <2>; - }; - port@3 { - port_id = <4>; - phy_address = <3>; - }; - port@5 { - port_id = <6>; - phy_address = <28>; - port_mac_sel = "QGMAC_PORT"; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp1 { - status = "okay"; - phy-handle = <&qca8075_0>; - label = "lan4"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_1>; - label = "lan3"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan2"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan1"; -}; - -&dp6_syn { - status = "okay"; - phy-handle = <&qca8081>; - label = "wan"; -}; - -&wifi { - status = "okay"; - qcom,ath11k-calibration-variant = "Dynalink-DL-WRX36"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts deleted file mode 100644 index 8a5200b4eb..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts +++ /dev/null @@ -1,308 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - -/ { - model = "prpl Foundation Haze"; - compatible = "prpl,haze", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - /* Aliases are required by U-Boot to patch MAC addresses */ - ethernet0 = &dp6_syn; - ethernet1 = &dp4; - ethernet2 = &dp3; - ethernet3 = &dp2; - label-mac-device = &dp6_syn; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - keys { - compatible = "gpio-keys"; - pinctrl-0 = <&button_pins>; - pinctrl-names = "default"; - - wps-button { - label = "wps"; - gpios = <&tlmm 42 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - reset-button { - label = "reset"; - gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-state { - mdc-pins { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio-pins { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - button_pins: button-state { - wps-pins { - pins = "gpio42"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; - - rst-pins { - pins = "gpio44"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&ssphy_1 { - status = "okay"; -}; - -&qusb_phy_1 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&usb_1 { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&blsp1_spi1 { /* BLSP1 QUP1 */ - pinctrl-0 = <&spi_0_pins>; - pinctrl-names = "default"; - cs-gpios = <0>; - status = "okay"; - - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - compatible = "jedec,spi-nor"; - spi-max-frequency = <50000000>; - - partitions { - compatible = "qcom,smem-part"; - }; - }; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 22 GPIO_ACTIVE_LOW>; - - qca8075_1: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0>; - }; - - qca8075_2: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - - qca8075_3: ethernet-phy@2 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <2>; - }; - - qca8075_4: ethernet-phy@3 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <3>; - }; - - aqr113c: ethernet-phy@5 { - compatible ="ethernet-phy-ieee802.3-c45"; - reg = <8>; - reset-gpios = <&tlmm 43 GPIO_ACTIVE_LOW>; - }; -}; - -&sdhc_1 { - status = "okay"; - - vqmmc-supply = <&l11>; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x1e>; /* lan port bitmap */ - switch_wan_bmp = <0x60>; /* wan port bitmap */ - switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0xe>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0xd>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0>; - }; - port@1 { - port_id = <2>; - phy_address = <1>; - }; - port@2 { - port_id = <3>; - phy_address = <2>; - }; - port@3 { - port_id = <4>; - phy_address = <3>; - }; - port@4 { - port_id = <6>; - phy_address = <8>; - compatible = "ethernet-phy-ieee802.3-c45"; - ethernet-phy-ieee802.3-c45; - }; - }; -}; - -&edma { - status = "okay"; -}; - -/* Dummy LAN port */ -&dp1 { - status = "disabled"; - phy-handle = <&qca8075_1>; - label = "lan4"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan3"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan2"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_4>; - label = "lan1"; -}; - -&dp6_syn { - status = "okay"; - qcom,mactype = <1>; - phy-handle = <&aqr113c>; - label = "wan"; -}; - -&pcie_qmp0 { - status = "okay"; -}; - -&pcie0 { - status = "okay"; - - perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; - - bridge@0,0 { - reg = <0x00020000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - }; -}; - -&pcie_qmp1 { - status = "okay"; -}; - -&pcie1 { - status = "okay"; - - perst-gpio = <&tlmm 61 GPIO_ACTIVE_LOW>; - - bridge@1,0 { - reg = <0x00010000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - - wifi@1,0 { - status = "okay"; - - /* ath11k has no DT compatible for PCI cards */ - compatible = "pci17cb,1104"; - reg = <0x00010000 0 0 0 0>; - - qcom,ath11k-calibration-variant = "prpl-Haze"; - }; - }; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "prpl-Haze"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts deleted file mode 100644 index 5bfcdcc8ca..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts +++ /dev/null @@ -1,191 +0,0 @@ -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" - -#include -#include - -/ { - model = "Netgear WAX218"; - compatible = "netgear,wax218", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - led-boot = &led_power_amber; - led-failsafe = &led_power_amber; - led-running = &led_power_amber; - led-upgrade = &led_power_amber; - }; - - chosen { - stdout-path = "serial0:115200n8"; - /* - * Netgear's U-Boot adds "ubi.mtd=rootfs root=mtd:ubi_rootfs" - * That fails to create a UBI block device, so add it here. - */ - bootargs-append = " ubi.block=0,rootfs root=/dev/ubiblock0_1"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 52 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - led_spi { - compatible = "spi-gpio"; - #address-cells = <1>; - #size-cells = <0>; - - sck-gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>; - mosi-gpios = <&tlmm 19 GPIO_ACTIVE_HIGH>; - - led_gpio: led_gpio@0 { - compatible = "fairchild,74hc595"; - reg = <0>; - gpio-controller; - #gpio-cells = <2>; - registers-number = <1>; - enable-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; - spi-max-frequency = <1000000>; - }; - }; - - leds { - compatible = "gpio-leds"; - - led_power_amber: led_power { - label = "amber:power"; - gpios = <&led_gpio 1 GPIO_ACTIVE_HIGH>; - }; - - led_lan { - label = "blue:lan"; - gpios = <&led_gpio 2 GPIO_ACTIVE_HIGH>; - }; - - led_wlan_2g { - label = "blue:wlan2g"; - gpios = <&led_gpio 3 GPIO_ACTIVE_HIGH>; - }; - - led_wlan_5g { - label = "blue:wlan5g"; - gpios = <&led_gpio 4 GPIO_ACTIVE_HIGH>; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; - switch_lan_bmp = <0x3e>; - switch_wan_bmp = <0x40>; - switch_mac_mode = <0x00>; - switch_mac_mode1 = <0xff>; - switch_mac_mode2 = <0x0f>; - bm_tick_mode = <0>; - tm_tick_mode = <0>; - - qcom,port_phyinfo { - port@5 { - port_id = <6>; - phy_address = <28>; - port_mac_sel = "QGMAC_PORT"; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&mdio { - status = "okay"; - - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - - qca8081_28: ethernet-phy@28 { - reg = <28>; - }; -}; - -&dp6_syn { - status = "okay"; - phy-handle = <&qca8081_28>; - label = "lan"; - nvmem-cells = <&macaddr_ubootenv_ethaddr>; - nvmem-cell-names = "mac-address"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "qcom,smem-part"; - - partition-0-appsblenv { - compatible = "fixed-partitions"; - label = "0:appsblenv"; - read-only; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - compatible = "u-boot,env"; - label = "env-data"; - reg = <0x0 0x40000>; - - macaddr_ubootenv_ethaddr: ethaddr {}; - }; - }; - }; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Netgear-WAX218"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts deleted file mode 100644 index 9779070725..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts +++ /dev/null @@ -1,487 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* Copyright 2023 Nokia */ - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - -/ { - model = "Compex WPQ873"; - compatible = "compex,wpq873", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - led-boot = &led_power_blue; - led-failsafe = &led_power_red; - led-running = &led_system_green; - led-upgrade = &led_system_blue; - /* Aliases as required by u-boot to patch MAC addresses */ - ethernet0 = &dp6; - ethernet1 = &dp2; - ethernet2 = &dp3; - ethernet3 = &dp4; - label-mac-device = &dp6; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " root=/dev/ubiblock0_1"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&tlmm 21 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - leds { - compatible = "gpio-leds"; - - led_power_red: power-red { - label = "red:power"; - gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_power_blue: power-blue { - label = "blue:power"; - gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_system_red: system-red { - label = "red:system"; - gpios = <&tlmm 28 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_system_green: system-green { - label = "green:system"; - gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_system_blue: system-blue { - label = "blue:system"; - gpios = <&tlmm 19 GPIO_ACTIVE_HIGH>; - color = ; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - i2c_pins: i2c-pins { - pins = "gpio0", "gpio2"; - function = "blsp5_i2c"; - drive-strength = <8>; - bias-disable; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&blsp1_i2c6 { - status = "okay"; - - pinctrl-0 = <&i2c_pins>; - pinctrl-names = "default"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&blsp1_spi1 { - status = "okay"; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; - - button_pins: button_pins { - reset_button { - pins = "gpio66"; - function = "gpio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_spi1 { - status = "okay"; - - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - compatible = "jedec,spi-nor"; - spi-max-frequency = <50000000>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "0:sbl1"; - reg = <0x0 0x50000>; - read-only; - }; - - partition@50000 { - label = "0:mibib"; - reg = <0x50000 0x10000>; - read-only; - }; - - partition@60000 { - label = "0:bootconfig"; - reg = <0x60000 0x20000>; - read-only; - }; - - partition@80000 { - label = "0:bootconfig1"; - reg = <0x80000 0x20000>; - read-only; - }; - - partition@a0000 { - label = "0:qsee"; - reg = <0xa0000 0x180000>; - read-only; - }; - - partition@220000 { - label = "0:qsee_1"; - reg = <0x220000 0x180000>; - read-only; - }; - - partition@3a0000 { - label = "0:devcfg"; - reg = <0x3a0000 0x10000>; - read-only; - }; - - partition@3b0000 { - label = "0:devcfg_1"; - reg = <0x3b0000 0x10000>; - read-only; - }; - - partition@3c0000 { - label = "0:apdp"; - reg = <0x3c0000 0x10000>; - read-only; - }; - - partition@3d0000 { - label = "0:apdp_1"; - reg = <0x3d0000 0x10000>; - read-only; - }; - - partition@3e0000 { - label = "0:rpm"; - reg = <0x3e0000 0x40000>; - read-only; - }; - - partition@420000 { - label = "0:rpm_1"; - reg = <0x420000 0x40000>; - read-only; - }; - - partition@460000 { - label = "0:cdt"; - reg = <0x460000 0x10000>; - read-only; - }; - - partition@470000 { - label = "0:cdt_1"; - reg = <0x470000 0x10000>; - read-only; - }; - - partition@480000 { - label = "0:appsblenv"; - reg = <0x480000 0x10000>; - }; - - partition@490000 { - label = "0:appsbl"; - reg = <0x490000 0xa0000>; - read-only; - }; - - partition@550000 { - label = "0:appsbl_1"; - reg = <0x530000 0xa0000>; - read-only; - }; - - partition@610000 { - label = "0:art"; - reg = <0x5d0000 0x40000>; - read-only; - }; - - partition@650000 { - label = "0:ethphyfw"; - reg = <0x610000 0x80000>; - read-only; - }; - }; - }; -}; - -&qpic_nand { - status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-strength = <8>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "rootfs"; - reg = <0x0000000 0x3400000>; - }; - - partition@3400000 { - label = "0:wififw"; - reg = <0x3400000 0x800000>; - read-only; - }; - - partition@3c00000 { - label = "rootfs_1"; - reg = <0x3c00000 0x3400000>; - }; - - partition@7000000 { - label = "0:wififw_1"; - reg = <0x7000000 0x800000>; - read-only; - }; - }; - }; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&qusb_phy_1 { - status = "okay"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&ssphy_1 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&usb_1 { - status = "okay"; -}; - - -&mdio { - status = "okay"; - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - qca8075_1: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - - qca8075_2: ethernet-phy@2 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <2>; - }; - - qca8075_3: ethernet-phy@3 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <3>; - }; - - qca8081: ethernet-phy@28 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <28>; - reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; - }; - -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_lan_bmp = <0x3e>; /* lan port bitmap */ - switch_wan_bmp = <0x40>; /* wan port bitmap */ - switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ - switch_mac_mode1 = <0x0f>; /* mac mode for uniphy instance1*/ - switch_mac_mode2 = <0x0f>; /* mac mode for uniphy instance2*/ - bm_tick_mode = <0>; /* bm tick mode */ - tm_tick_mode = <0>; /* tm tick mode */ - - qcom,port_phyinfo { - port@2 { - port_id = <2>; - phy_address = <1>; - }; - port@3 { - port_id = <3>; - phy_address = <2>; - }; - port@4 { - port_id = <4>; - phy_address = <3>; - }; - port@5 { - port_id = <6>; - phy_address = <28>; - port_mac_sel = "QGMAC_PORT"; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_1>; - label = "lan1"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan2"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan3"; -}; - -&dp6 { - status = "okay"; - phy-handle = <&qca8081>; - label = "wan"; -}; - -&pcie_qmp0 { - status = "okay"; -}; - -&pcie0 { - status = "okay"; - - perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; - - bridge@0,0 { - reg = <0x00020000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - }; -}; - -&pcie_qmp1 { - status = "okay"; -}; - -&pcie1 { - status = "okay"; - - perst-gpio = <&tlmm 62 GPIO_ACTIVE_HIGH>; - - bridge@1,0 { - reg = <0x00010000 0 0 0 0>; - #address-cells = <3>; - #size-cells = <2>; - ranges; - }; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Compex-WPQ873"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi deleted file mode 100644 index dace4008b3..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include "ipq8074.dtsi" - -&tzapp_region { - reg = <0x0 0x4a400000 0x0 0x100000>; -}; - -&q6_region { - reg = <0x0 0x4b000000 0x0 0x3700000>; -}; - -&q6_etr_region { - reg = <0x0 0x4e700000 0x0 0x100000>; -}; - -&m3_dump_region { - reg = <0x0 0x4e800000 0x0 0x100000>; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi deleted file mode 100644 index 057d592031..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include "ipq8074-cpr-regulator.dtsi" - -&CPU0 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; - -&CPU1 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; - -&CPU2 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; - -&CPU3 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; -&cpu0_thermal { - trips { - cpu0_passive: cpu-passive { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu0_passive>; - 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>; - }; - }; -}; - -&cpu1_thermal { - trips { - cpu1_passive: cpu-passive { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu1_passive>; - 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>; - }; - }; -}; - -&cpu2_thermal { - trips { - cpu2_passive: cpu-passive { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu2_passive>; - 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>; - }; - }; -}; - -&cpu3_thermal { - trips { - cpu3_passive: cpu-passive { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu3_passive>; - 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>; - }; - }; -}; - -&cluster_thermal { - trips { - cluster_passive: cluster-passive { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cluster_passive>; - 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/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi deleted file mode 100644 index e351a2e759..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi +++ /dev/null @@ -1,228 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include "pmp8074.dtsi" - -&soc { - apc_apm: apm@b111000 { - compatible = "qcom,ipq807x-apm"; - reg = <0xb111000 0x1000>; - reg-names = "pm-apcc-glb"; - qcom,apm-post-halt-delay = <0x2>; - qcom,apm-halt-clk-delay = <0x11>; - qcom,apm-resume-clk-delay = <0x10>; - qcom,apm-sel-switch-delay = <0x01>; - }; - - apc_cpr: cpr4-ctrl@b018000 { - compatible = "qcom,cpr4-ipq807x-apss-regulator"; - reg = <0xb018000 0x4000>, <0xa4000 0x1000>, <0x0193d008 0x4>; - reg-names = "cpr_ctrl", "fuse_base", "cpr_tcsr_reg"; - interrupts = ; - interrupt-names = "cpr"; - qcom,cpr-ctrl-name = "apc"; - qcom,cpr-sensor-time = <1000>; - qcom,cpr-loop-time = <5000000>; - qcom,cpr-idle-cycles = <15>; - qcom,cpr-step-quot-init-min = <12>; - qcom,cpr-step-quot-init-max = <14>; - qcom,cpr-count-mode = <0>; /* All-at-once */ - qcom,cpr-count-repeat = <14>; - qcom,cpr-down-error-step-limit = <1>; - qcom,cpr-up-error-step-limit = <1>; - qcom,apm-ctrl = <&apc_apm>; - qcom,apm-threshold-voltage = <848000>; - vdd-supply = <&s3>; - qcom,voltage-step = <8000>; - - thread@0 { - qcom,cpr-thread-id = <0>; - qcom,cpr-consecutive-up = <0>; - qcom,cpr-consecutive-down = <0>; - qcom,cpr-up-threshold = <4>; - qcom,cpr-down-threshold = <1>; - - apc_vreg: regulator { - regulator-name = "apc_corner"; - regulator-min-microvolt = <1>; - regulator-max-microvolt = <6>; - qcom,cpr-part-types = <2>; - qcom,cpr-parts-voltage = <1048000>; - qcom,cpr-parts-voltage-v2 = <992000>; - qcom,cpr-fuse-corners = <4>; - qcom,cpr-fuse-combos = <8>; - qcom,cpr-corners = <6>; - qcom,cpr-speed-bins = <1>; - qcom,cpr-speed-bin-corners = <6>; - qcom,cpr-corner-fmax-map = <1 3 5 6>; - qcom,allow-voltage-interpolation; - qcom,allow-quotient-interpolation; - qcom,cpr-scaled-open-loop-voltage-as-ceiling; - qcom,cpr-voltage-ceiling = - <840000 904000 944000 - 984000 992000 1064000>; - qcom,cpr-voltage-floor = - <592000 648000 712000 - 744000 784000 848000>; - qcom,corner-frequencies = - <1017600000 1382400000 1651200000 - 1843200000 1920000000 2208000000>; - - /* TT/FF parts i.e. turbo L1 OL voltage < 1048 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-0 = - /* Speed bin 0; CPR rev 0..7 */ - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 12000>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; - - /* SS parts i.e turbo L1 OL voltage >= 1048 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-1 = - /* Speed bin 0; CPR rev 0..7 */ - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 20000 26000 0 20000>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; - - /* v2 - FF parts i.e. turbo L1 OL voltage < 992 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-v2-0 = - /* Speed bin 0; CPR rev 0..7 */ - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; - - /* v2 - SS/TT parts i.e turbo L1 OL voltage >= 992 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-v2-1 = - /* Speed bin 0; CPR rev 0..7 */ - < 0 0 0 0>, - < 0 7000 36000 4000>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; - - /* v2 - FF parts i.e. turbo L1 OL voltage < 992 mV */ - qcom,cpr-closed-loop-voltage-adjustment-v2-0 = - /* Speed bin 0; CPR rev 0..7 */ - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; - - /* v2 - SS/TT parts i.e turbo L1 OL voltage >= 992 mV */ - qcom,cpr-closed-loop-voltage-adjustment-v2-1 = - /* Speed bin 0; CPR rev 0..7 */ - < 0 0 0 0>, - < 0 0 19000 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>, - < 0 0 0 0>; - - qcom,cpr-ro-scaling-factor = - < 3970 4150 0 2280 2520 2470 2250 2280 - 2390 2330 2530 2500 850 2900 2510 2170 >, - < 3970 4150 0 2280 2520 2470 2250 2280 - 2390 2330 2530 2500 850 2900 2510 2170 >, - < 3970 4150 0 2280 2520 2470 2250 2280 - 2390 2330 2530 2500 850 2900 2510 2170 >, - < 3970 4150 0 2280 2520 2470 2250 2280 - 2390 2330 2530 2500 850 2900 2510 2170 >; - - qcom,cpr-floor-to-ceiling-max-range = - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>, - < 40000 40000 40000 40000 40000 40000>; - regulator-always-on; - }; - }; - }; - - npu_cpr: npu-cpr { - compatible = "qcom,cpr3-ipq807x-npu-regulator"; - reg = <0xa4000 0x1000>, <0x0193d008 0x4>; - reg-names = "fuse_base", "cpr_tcsr_reg"; - qcom,cpr-ctrl-name = "npu"; - vdd-supply = <&s4>; - qcom,voltage-step = <8000>; - thread@0 { - qcom,cpr-thread-id = <0>; - qcom,cpr-consecutive-up = <0>; - qcom,cpr-consecutive-down = <2>; - qcom,cpr-up-threshold = <2>; - qcom,cpr-down-threshold = <1>; - - npu_vreg: regulator { - regulator-name = "npu_corner"; - regulator-min-microvolt = <1>; - regulator-max-microvolt = <3>; - qcom,cpr-part-types = <2>; - qcom,cpr-parts-voltage = <968000>; - qcom,cpr-parts-voltage-v2 = <832001>; - qcom,cpr-cold-temp-threshold-v2 = <30>; - qcom,cpr-fuse-corners = <2>; - qcom,cpr-fuse-combos = <1>; - qcom,cpr-corners = <2>; - qcom,cpr-speed-bins = <1>; - qcom,cpr-speed-bin-corners = <2>; - qcom,allow-voltage-interpolation; - qcom,cpr-corner-fmax-map = <1 2>; - qcom,cpr-voltage-ceiling = - <912000 992000>; - qcom,cpr-voltage-floor = - <752000 792000>; - qcom,corner-frequencies = - <1497600000 1689600000>; - - /* TT/FF parts i.e. turbo OL voltage < 968 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-0 = - < 40000 40000>; - - /* SS parts i.e turbo OL voltage >= 968 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-1 = - < 24000 24000>; - - /* FF parts i.e. turbo OL voltage <= 832 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-v2-0= - <40000 40000>; - - /* TT/SS parts i.e turbo OL voltage > 832 mV */ - qcom,cpr-open-loop-voltage-fuse-adjustment-v2-1= - <40000 40000>; - - /* FF parts i.e. turbo OL voltage <= 832 mV */ - qcom,cpr-cold-temp-voltage-adjustment-v2-0 = - <0 0>; - - /* TT/SS parts i.e turbo OL voltage > 832 mV */ - qcom,cpr-cold-temp-voltage-adjustment-v2-1 = - <35000 27000>; - }; - }; - }; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi deleted file mode 100644 index 129266c50d..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi +++ /dev/null @@ -1,531 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -&clocks { - bias_pll_cc_clk { - compatible = "fixed-clock"; - clock-frequency = <300000000>; - #clock-cells = <0>; - }; - - bias_pll_nss_noc_clk { - compatible = "fixed-clock"; - clock-frequency = <416500000>; - #clock-cells = <0>; - }; -}; - -&soc { - switch: ess-switch@3a000000 { - compatible = "qcom,ess-switch-ipq807x"; - reg = <0x3a000000 0x1000000>; - switch_access_mode = "local bus"; - switch_cpu_bmp = <0x1>; /* cpu port bitmap */ - switch_inner_bmp = <0x80>; /*inner port bitmap*/ - clocks = <&gcc GCC_CMN_12GPLL_AHB_CLK>, - <&gcc GCC_CMN_12GPLL_SYS_CLK>, - <&gcc GCC_UNIPHY0_AHB_CLK>, - <&gcc GCC_UNIPHY0_SYS_CLK>, - <&gcc GCC_UNIPHY1_AHB_CLK>, - <&gcc GCC_UNIPHY1_SYS_CLK>, - <&gcc GCC_UNIPHY2_AHB_CLK>, - <&gcc GCC_UNIPHY2_SYS_CLK>, - <&gcc GCC_PORT1_MAC_CLK>, - <&gcc GCC_PORT2_MAC_CLK>, - <&gcc GCC_PORT3_MAC_CLK>, - <&gcc GCC_PORT4_MAC_CLK>, - <&gcc GCC_PORT5_MAC_CLK>, - <&gcc GCC_PORT6_MAC_CLK>, - <&gcc GCC_NSS_PPE_CLK>, - <&gcc GCC_NSS_PPE_CFG_CLK>, - <&gcc GCC_NSSNOC_PPE_CLK>, - <&gcc GCC_NSSNOC_PPE_CFG_CLK>, - <&gcc GCC_NSS_EDMA_CLK>, - <&gcc GCC_NSS_EDMA_CFG_CLK>, - <&gcc GCC_NSS_PPE_IPE_CLK>, - <&gcc GCC_NSS_PPE_BTQ_CLK>, - <&gcc GCC_MDIO_AHB_CLK>, - <&gcc GCC_NSS_NOC_CLK>, - <&gcc GCC_NSSNOC_SNOC_CLK>, - <&gcc GCC_MEM_NOC_NSS_AXI_CLK>, - <&gcc GCC_NSS_CRYPTO_CLK>, - <&gcc GCC_NSS_IMEM_CLK>, - <&gcc GCC_NSS_PTP_REF_CLK>, - <&gcc GCC_NSS_PORT1_RX_CLK>, - <&gcc GCC_NSS_PORT1_TX_CLK>, - <&gcc GCC_NSS_PORT2_RX_CLK>, - <&gcc GCC_NSS_PORT2_TX_CLK>, - <&gcc GCC_NSS_PORT3_RX_CLK>, - <&gcc GCC_NSS_PORT3_TX_CLK>, - <&gcc GCC_NSS_PORT4_RX_CLK>, - <&gcc GCC_NSS_PORT4_TX_CLK>, - <&gcc GCC_NSS_PORT5_RX_CLK>, - <&gcc GCC_NSS_PORT5_TX_CLK>, - <&gcc GCC_NSS_PORT6_RX_CLK>, - <&gcc GCC_NSS_PORT6_TX_CLK>, - <&gcc GCC_UNIPHY0_PORT1_RX_CLK>, - <&gcc GCC_UNIPHY0_PORT1_TX_CLK>, - <&gcc GCC_UNIPHY0_PORT2_RX_CLK>, - <&gcc GCC_UNIPHY0_PORT2_TX_CLK>, - <&gcc GCC_UNIPHY0_PORT3_RX_CLK>, - <&gcc GCC_UNIPHY0_PORT3_TX_CLK>, - <&gcc GCC_UNIPHY0_PORT4_RX_CLK>, - <&gcc GCC_UNIPHY0_PORT4_TX_CLK>, - <&gcc GCC_UNIPHY0_PORT5_RX_CLK>, - <&gcc GCC_UNIPHY0_PORT5_TX_CLK>, - <&gcc GCC_UNIPHY1_PORT5_RX_CLK>, - <&gcc GCC_UNIPHY1_PORT5_TX_CLK>, - <&gcc GCC_UNIPHY2_PORT6_RX_CLK>, - <&gcc GCC_UNIPHY2_PORT6_TX_CLK>, - <&gcc NSS_PORT5_RX_CLK_SRC>, - <&gcc NSS_PORT5_TX_CLK_SRC>; - clock-names = "cmn_ahb_clk", "cmn_sys_clk", - "uniphy0_ahb_clk", "uniphy0_sys_clk", - "uniphy1_ahb_clk", "uniphy1_sys_clk", - "uniphy2_ahb_clk", "uniphy2_sys_clk", - "port1_mac_clk", "port2_mac_clk", - "port3_mac_clk", "port4_mac_clk", - "port5_mac_clk", "port6_mac_clk", - "nss_ppe_clk", "nss_ppe_cfg_clk", - "nssnoc_ppe_clk", "nssnoc_ppe_cfg_clk", - "nss_edma_clk", "nss_edma_cfg_clk", - "nss_ppe_ipe_clk", "nss_ppe_btq_clk", - "gcc_mdio_ahb_clk", "gcc_nss_noc_clk", - "gcc_nssnoc_snoc_clk", - "gcc_mem_noc_nss_axi_clk", - "gcc_nss_crypto_clk", - "gcc_nss_imem_clk", - "gcc_nss_ptp_ref_clk", - "nss_port1_rx_clk", "nss_port1_tx_clk", - "nss_port2_rx_clk", "nss_port2_tx_clk", - "nss_port3_rx_clk", "nss_port3_tx_clk", - "nss_port4_rx_clk", "nss_port4_tx_clk", - "nss_port5_rx_clk", "nss_port5_tx_clk", - "nss_port6_rx_clk", "nss_port6_tx_clk", - "uniphy0_port1_rx_clk", - "uniphy0_port1_tx_clk", - "uniphy0_port2_rx_clk", - "uniphy0_port2_tx_clk", - "uniphy0_port3_rx_clk", - "uniphy0_port3_tx_clk", - "uniphy0_port4_rx_clk", - "uniphy0_port4_tx_clk", - "uniphy0_port5_rx_clk", - "uniphy0_port5_tx_clk", - "uniphy1_port5_rx_clk", - "uniphy1_port5_tx_clk", - "uniphy2_port6_rx_clk", - "uniphy2_port6_tx_clk", - "nss_port5_rx_clk_src", - "nss_port5_tx_clk_src"; - resets = <&gcc GCC_PPE_FULL_RESET>, - <&gcc GCC_UNIPHY0_SOFT_RESET>, - <&gcc GCC_UNIPHY0_XPCS_RESET>, - <&gcc GCC_UNIPHY1_SOFT_RESET>, - <&gcc GCC_UNIPHY1_XPCS_RESET>, - <&gcc GCC_UNIPHY2_SOFT_RESET>, - <&gcc GCC_UNIPHY2_XPCS_RESET>, - <&gcc GCC_NSSPORT1_RESET>, - <&gcc GCC_NSSPORT2_RESET>, - <&gcc GCC_NSSPORT3_RESET>, - <&gcc GCC_NSSPORT4_RESET>, - <&gcc GCC_NSSPORT5_RESET>, - <&gcc GCC_NSSPORT6_RESET>; - reset-names = "ppe_rst", "uniphy0_soft_rst", - "uniphy0_xpcs_rst", "uniphy1_soft_rst", - "uniphy1_xpcs_rst", "uniphy2_soft_rst", - "uniphy2_xpcs_rst", "nss_port1_rst", - "nss_port2_rst", "nss_port3_rst", - "nss_port4_rst", "nss_port5_rst", - "nss_port6_rst"; - mdio-bus = <&mdio>; - status = "disabled"; - - port_scheduler_resource { - port@0 { - port_id = <0>; - ucast_queue = <0 143>; - mcast_queue = <256 271>; - l0sp = <0 35>; - l0cdrr = <0 47>; - l0edrr = <0 47>; - l1cdrr = <0 7>; - l1edrr = <0 7>; - }; - port@1 { - port_id = <1>; - ucast_queue = <144 159>; - mcast_queue = <272 275>; - l0sp = <36 39>; - l0cdrr = <48 63>; - l0edrr = <48 63>; - l1cdrr = <8 11>; - l1edrr = <8 11>; - }; - port@2 { - port_id = <2>; - ucast_queue = <160 175>; - mcast_queue = <276 279>; - l0sp = <40 43>; - l0cdrr = <64 79>; - l0edrr = <64 79>; - l1cdrr = <12 15>; - l1edrr = <12 15>; - }; - port@3 { - port_id = <3>; - ucast_queue = <176 191>; - mcast_queue = <280 283>; - l0sp = <44 47>; - l0cdrr = <80 95>; - l0edrr = <80 95>; - l1cdrr = <16 19>; - l1edrr = <16 19>; - }; - port@4 { - port_id = <4>; - ucast_queue = <192 207>; - mcast_queue = <284 287>; - l0sp = <48 51>; - l0cdrr = <96 111>; - l0edrr = <96 111>; - l1cdrr = <20 23>; - l1edrr = <20 23>; - }; - port@5 { - port_id = <5>; - ucast_queue = <208 223>; - mcast_queue = <288 291>; - l0sp = <52 55>; - l0cdrr = <112 127>; - l0edrr = <112 127>; - l1cdrr = <24 27>; - l1edrr = <24 27>; - }; - port@6 { - port_id = <6>; - ucast_queue = <224 239>; - mcast_queue = <292 295>; - l0sp = <56 59>; - l0cdrr = <128 143>; - l0edrr = <128 143>; - l1cdrr = <28 31>; - l1edrr = <28 31>; - }; - port@7 { - port_id = <7>; - ucast_queue = <240 255>; - mcast_queue = <296 299>; - l0sp = <60 63>; - l0cdrr = <144 159>; - l0edrr = <144 159>; - l1cdrr = <32 35>; - l1edrr = <32 35>; - }; - }; - port_scheduler_config { - port@0 { - port_id = <0>; - l1scheduler { - group@0 { - sp = <0 1>; /*L0 SPs*/ - /*cpri cdrr epri edrr*/ - cfg = <0 0 0 0>; - }; - }; - l0scheduler { - group@0 { - /*unicast queues*/ - ucast_queue = <0 4 8>; - /*multicast queues*/ - mcast_queue = <256 260>; - /*sp cpri cdrr epri edrr*/ - cfg = <0 0 0 0 0>; - }; - group@1 { - ucast_queue = <1 5 9>; - mcast_queue = <257 261>; - cfg = <0 1 1 1 1>; - }; - group@2 { - ucast_queue = <2 6 10>; - mcast_queue = <258 262>; - cfg = <0 2 2 2 2>; - }; - group@3 { - ucast_queue = <3 7 11>; - mcast_queue = <259 263>; - cfg = <0 3 3 3 3>; - }; - }; - }; - port@1 { - port_id = <1>; - l1scheduler { - group@0 { - sp = <36>; - cfg = <0 8 0 8>; - }; - group@1 { - sp = <37>; - cfg = <1 9 1 9>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <144>; - ucast_loop_pri = <16>; - mcast_queue = <272>; - mcast_loop_pri = <4>; - cfg = <36 0 48 0 48>; - }; - }; - }; - port@2 { - port_id = <2>; - l1scheduler { - group@0 { - sp = <40>; - cfg = <0 12 0 12>; - }; - group@1 { - sp = <41>; - cfg = <1 13 1 13>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <160>; - ucast_loop_pri = <16>; - mcast_queue = <276>; - mcast_loop_pri = <4>; - cfg = <40 0 64 0 64>; - }; - }; - }; - port@3 { - port_id = <3>; - l1scheduler { - group@0 { - sp = <44>; - cfg = <0 16 0 16>; - }; - group@1 { - sp = <45>; - cfg = <1 17 1 17>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <176>; - ucast_loop_pri = <16>; - mcast_queue = <280>; - mcast_loop_pri = <4>; - cfg = <44 0 80 0 80>; - }; - }; - }; - port@4 { - port_id = <4>; - l1scheduler { - group@0 { - sp = <48>; - cfg = <0 20 0 20>; - }; - group@1 { - sp = <49>; - cfg = <1 21 1 21>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <192>; - ucast_loop_pri = <16>; - mcast_queue = <284>; - mcast_loop_pri = <4>; - cfg = <48 0 96 0 96>; - }; - }; - }; - port@5 { - port_id = <5>; - l1scheduler { - group@0 { - sp = <52>; - cfg = <0 24 0 24>; - }; - group@1 { - sp = <53>; - cfg = <1 25 1 25>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <208>; - ucast_loop_pri = <16>; - mcast_queue = <288>; - mcast_loop_pri = <4>; - cfg = <52 0 112 0 112>; - }; - }; - }; - port@6 { - port_id = <6>; - l1scheduler { - group@0 { - sp = <56>; - cfg = <0 28 0 28>; - }; - group@1 { - sp = <57>; - cfg = <1 29 1 29>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <224>; - ucast_loop_pri = <16>; - mcast_queue = <292>; - mcast_loop_pri = <4>; - cfg = <56 0 128 0 128>; - }; - }; - }; - port@7 { - port_id = <7>; - l1scheduler { - group@0 { - sp = <60>; - cfg = <0 32 0 32>; - }; - group@1 { - sp = <61>; - cfg = <1 33 1 33>; - }; - }; - l0scheduler { - group@0 { - ucast_queue = <240>; - ucast_loop_pri = <16>; - mcast_queue = <296>; - cfg = <60 0 144 0 144>; - }; - }; - }; - }; - }; - - ess-uniphy@7a00000 { - compatible = "qcom,ess-uniphy"; - reg = <0x7a00000 0x30000>; - uniphy_access_mode = "local bus"; - }; - - edma: edma@3ab00000 { - compatible = "qcom,edma"; - reg = <0x3ab00000 0x76900>; - reg-names = "edma-reg-base"; - qcom,txdesc-ring-start = <23>; - qcom,txdesc-rings = <1>; - qcom,txcmpl-ring-start = <7>; - qcom,txcmpl-rings = <1>; - qcom,rxfill-ring-start = <7>; - qcom,rxfill-rings = <1>; - qcom,rxdesc-ring-start = <15>; - qcom,rxdesc-rings = <1>; - interrupts = , - , - , - ; - resets = <&gcc GCC_EDMA_HW_RESET>; - reset-names = "edma_rst"; - status = "disabled"; - }; - - dp1: dp1 { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <1>; - reg = <0x3a001000 0x200>; - qcom,mactype = <0>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp2: dp2 { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <2>; - reg = <0x3a001200 0x200>; - qcom,mactype = <0>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp3: dp3 { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <3>; - reg = <0x3a001400 0x200>; - qcom,mactype = <0>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp4: dp4 { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <4>; - reg = <0x3a001600 0x200>; - qcom,mactype = <0>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp5: dp5 { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <5>; - reg = <0x3a001800 0x200>; - qcom,mactype = <0>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp6: dp6 { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <6>; - reg = <0x3a001a00 0x200>; - qcom,mactype = <0>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp5_syn: dp5-syn { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <5>; - reg = <0x3a003000 0x3fff>; - qcom,mactype = <1>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; - - dp6_syn: dp6-syn { - device_type = "network"; - compatible = "qcom,nss-dp"; - qcom,id = <6>; - reg = <0x3a007000 0x3fff>; - qcom,mactype = <1>; - local-mac-address = [000000000000]; - phy-mode = "sgmii"; - status = "disabled"; - }; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi deleted file mode 100644 index 37af7bbc1f..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include "ipq8074-cpr-regulator.dtsi" - -&CPU0 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; - -&CPU1 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; - -&CPU2 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; - -&CPU3 { - cpu-supply = <&apc_vreg>; - voltage-tolerance = <1>; -}; -&cpu0_thermal { - trips { - cpu0_passive_low: cpu-passive-low { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu0_passive_high: cpu-passive-high { - temperature = <100000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu0_passive_low>; - 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>; - }; - map1 { - trip = <&cpu0_passive_high>; - 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>; - }; - }; -}; - -&cpu1_thermal { - trips { - cpu1_passive_low: cpu-passive-low { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu1_passive_high: cpu-passive-high { - temperature = <100000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu1_passive_low>; - 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>; - }; - map1 { - trip = <&cpu1_passive_high>; - 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>; - }; - }; -}; - -&cpu2_thermal { - trips { - cpu2_passive_low: cpu-passive-low { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu2_passive_high: cpu-passive-high { - temperature = <100000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu2_passive_low>; - 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>; - }; - map1 { - trip = <&cpu2_passive_high>; - 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>; - }; - }; -}; - -&cpu3_thermal { - trips { - cpu3_passive_low: cpu-passive-low { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - - cpu3_passive_high: cpu-passive-high { - temperature = <100000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cpu3_passive_low>; - 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>; - }; - map1 { - trip = <&cpu3_passive_high>; - 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>; - }; - }; -}; - -&cluster_thermal { - trips { - cluster_passive_low: cluster-passive { - temperature = <95000>; - hysteresis = <2000>; - type = "passive"; - }; - - cluster_passive_high: cluster-passive-high { - temperature = <100000>; - hysteresis = <2000>; - type = "passive"; - }; - }; - - cooling-maps { - map0 { - trip = <&cluster_passive_low>; - 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>; - }; - map1 { - trip = <&cluster_passive_high>; - 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/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts deleted file mode 100644 index d113b233ec..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts +++ /dev/null @@ -1,445 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT -/* - * Copyright (c) 2022, Karol Przybylski - * Copyright (c) 2023, Andre Valentin - */ - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - - -/ { - model = "Zyxel NBG7815"; - compatible = "zyxel,nbg7815", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - serial1 = &blsp1_uart3; - /* Alias as required by u-boot to patch MAC addresses */ - ethernet0 = &dp1; - label-mac-device = &dp1; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - linux,code = ; - gpios = <&tlmm 54 GPIO_ACTIVE_LOW>; - }; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - - -&blsp1_uart3 { - status = "okay"; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - - -&blsp1_spi1 { - pinctrl-0 = <&spi_0_pins>; - pinctrl-names = "default"; - cs-gpios = <0>; - status = "okay"; - - /* - * Bootloader will find the NAND DT node by the compatible and - * then "fixup" it by adding the partitions from the SMEM table - * using the legacy bindings thus making it impossible for us - * to change the partition table or utilize NVMEM for calibration. - * So add a dummy partitions node that bootloader will populate - * and set it as disabled so the kernel ignores it instead of - * printing warnings due to the broken way bootloader adds the - * partitions. - */ - partitions { - status = "disabled"; - }; - - - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - compatible = "jedec,spi-nor"; - spi-max-frequency = <50000000>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "0:sbl1"; - reg = <0x0 0x50000>; - read-only; - }; - - partition@50000 { - label = "0:mibib"; - reg = <0x50000 0x10000>; - read-only; - }; - - partition@60000 { - label = "0:bootconfig"; - reg = <0x60000 0x20000>; - read-only; - }; - - partition@80000 { - label = "0:bootconfig1"; - reg = <0x80000 0x20000>; - read-only; - }; - - partition@a0000 { - label = "0:qsee"; - reg = <0xa0000 0x180000>; - read-only; - }; - - partition@220000 { - label = "0:qsee_1"; - reg = <0x220000 0x180000>; - read-only; - }; - - partition@3a0000 { - label = "0:devcfg"; - reg = <0x3a0000 0x10000>; - read-only; - }; - - partition@3b0000 { - label = "0:devcfg_1"; - reg = <0x3b0000 0x10000>; - read-only; - }; - - partition@3c0000 { - label = "0:apdp"; - reg = <0x3c0000 0x10000>; - read-only; - }; - - partition@3d0000 { - label = "0:apdp_1"; - reg = <0x3d0000 0x10000>; - read-only; - }; - - partition@3e0000 { - label = "0:rpm"; - reg = <0x3e0000 0x40000>; - read-only; - }; - - partition@420000 { - label = "0:rpm_1"; - reg = <0x420000 0x40000>; - read-only; - }; - - partition@460000 { - label = "0:cdt"; - reg = <0x460000 0x10000>; - read-only; - }; - - partition@470000 { - label = "0:cdt_1"; - reg = <0x470000 0x10000>; - read-only; - }; - - partition@480000 { - label = "0:appsbl"; - reg = <0x480000 0xc0000>; - read-only; - }; - - partition@540000 { - label = "0:appsbl_1"; - reg = <0x540000 0xc0000>; - read-only; - }; - - partition@600000 { - compatible = "u-boot,env"; - label = "0:appsblenv"; - reg = <0x600000 0x10000>; - - macaddr_lan: ethaddr { - #nvmem-cell-cells = <1>; - }; - }; - - partition@610000 { - label = "0:art"; - reg = <0x610000 0x40000>; - read-only; - }; - - partition@650000 { - label = "0:ethphyfw"; - reg = <0x650000 0x80000>; - read-only; - }; - - partition@6d0000 { - label = "0:crt"; - reg = <0x6d0000 0x10000>; - read-only; - }; - - partition@6e0000 { - label = "dual_flag"; - reg = <0x6e0000 0x10000>; - }; - - partition@6f0000 { - label = "reserved"; - reg = <0x6f0000 0x110000>; - read-only; - }; - }; - }; -}; - -&mdio { - status = "okay"; - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; - - qca8075_1: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0>; - }; - - qca8075_2: ethernet-phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <1>; - }; - - qca8075_3: ethernet-phy@2 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <2>; - }; - - qca8075_4: ethernet-phy@3 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <3>; - }; - - qca8081: ethernet-phy@4{ - compatible = "ethernet-phy-id004d.d101"; - reg = <28>; - reset-gpios = <&tlmm 31 GPIO_ACTIVE_LOW>; - }; - - aqr113c: ethernet-phy@5 { - compatible = "ethernet-phy-ieee802.3-c45"; - reg = <8>; - reset-gpios = <&tlmm 63 GPIO_ACTIVE_LOW>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; - switch_lan_bmp = <0x3e>; - switch_wan_bmp = <0x40>; - switch_mac_mode = <0x0>; - switch_mac_mode1 = <0xf>; - switch_mac_mode2 = <0xd>; - bm_tick_mode = <0>; - tm_tick_mode = <0>; - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0>; - }; - - port@1 { - port_id = <2>; - phy_address = <1>; - }; - - port@2 { - port_id = <3>; - phy_address = <2>; - }; - - port@3 { - port_id = <4>; - phy_address = <3>; - }; - - port@4 { - port_id = <5>; - phy_address = <28>; - port_mac_sel = "QGMAC_PORT"; - }; - - port@5 { - port_id = <6>; - ethernet-phy-ieee802.3-c45; - phy_address = <8>; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp1 { - status = "okay"; - phy-handle = <&qca8075_1>; - label = "lan1"; - nvmem-cells = <&macaddr_lan 0>; - nvmem-cell-names = "mac-address"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan2"; - nvmem-cells = <&macaddr_lan 0>; - nvmem-cell-names = "mac-address"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan3"; - nvmem-cells = <&macaddr_lan 0>; - nvmem-cell-names = "mac-address"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_4>; - label = "lan4"; - nvmem-cells = <&macaddr_lan 0>; - nvmem-cell-names = "mac-address"; -}; - -&dp5 { - status = "okay"; - phy-handle = <&qca8081>; - label = "wan"; - nvmem-cells = <&macaddr_lan 1>; - nvmem-cell-names = "mac-address"; -}; - -&dp6_syn { - status = "okay"; - phy-handle = <&aqr113c>; - label = "10g"; - nvmem-cells = <&macaddr_lan 0>; - nvmem-cell-names = "mac-address"; -}; - -&blsp1_i2c2 { - pinctrl-0 = <&i2c_0_pins>; - pinctrl-names = "default"; - status = "okay"; - - tmp103@70 { - compatible = "ti,tmp103"; - reg = <0x70>; - }; -}; - -&sdhc_1 { - status = "okay"; - /* unstable, problem with the hs400 > h200 speed switch */ - /delete-property/ mmc-hs400-1_8v; - mmc-hs200-1_8v; - mmc-ddr-1_8v; - vqmmc-supply = <&l11>; -}; - -&ssphy_0 { - status = "okay"; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&ssphy_1 { - status = "okay"; -}; - -&qusb_phy_1 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; -}; - -&usb_1 { - status = "okay"; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Zyxel-NBG7815"; -}; diff --git a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts b/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts deleted file mode 100644 index 32386dc93e..0000000000 --- a/target/linux/ipq807x/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later OR MIT - -/dts-v1/; - -#include "ipq8074.dtsi" -#include "ipq8074-hk-cpu.dtsi" -#include "ipq8074-ess.dtsi" -#include -#include -#include - -/ { - model = "Buffalo WXR-5950AX12"; - compatible = "buffalo,wxr-5950ax12", "qcom,ipq8074"; - - aliases { - serial0 = &blsp1_uart5; - led-boot = &led_power_white; - led-failsafe = &led_power_red; - led-running = &led_power_white; - led-upgrade = &led_power_white; - label-mac-device = &dp5_syn; - }; - - chosen { - stdout-path = "serial0:115200n8"; - bootargs-append = " ubi.mtd=user_property root=/dev/ubiblock1_0"; - }; - - leds { - compatible = "gpio-leds"; - - led-0 { - label = "white:router"; - gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-1 { - label = "red:router"; - gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led_power_red: led-2 { - label = "red:power"; - gpios = <&tlmm 31 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_POWER; - }; - - led_power_white: led-3 { - label = "white:power"; - gpios = <&tlmm 34 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_POWER; - }; - - led-4 { - label = "white:internet"; - gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-5 { - label = "red:internet"; - gpios = <&tlmm 44 GPIO_ACTIVE_HIGH>; - color = ; - }; - - led-6 { - label = "red:wireless"; - gpios = <&tlmm 55 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_WLAN; - }; - - led-7 { - label = "white:wireless"; - gpios = <&tlmm 56 GPIO_ACTIVE_HIGH>; - color = ; - function = LED_FUNCTION_WLAN; - }; - }; - - keys { - compatible = "gpio-keys"; - - /* - * mode: 3x position switch - * - * - ROUTER - * - AP - * - WB (Wireless Bridge) - */ - ap { - label = "mode-ap"; - gpios = <&tlmm 29 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - bridge { - label = "mode-wb"; - gpios = <&tlmm 30 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - /* - * op: 2x position switch - * - * - AUTO - * - MANUAL (select Router/AP/WB manually) - */ - manual { - label = "op-manual"; - gpios = <&tlmm 52 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - wps { - label = "wps"; - gpios = <&tlmm 51 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - reset { - label = "reset"; - gpios = <&tlmm 54 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; - - reg_usb_vbus: regulator-5v-vbus { - compatible = "regulator-fixed"; - regulator-name = "vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&tlmm 64 GPIO_ACTIVE_HIGH>; - enable-active-high; - regulator-always-on; - }; -}; - -&tlmm { - mdio_pins: mdio-pins { - mdc { - pins = "gpio68"; - function = "mdc"; - drive-strength = <8>; - bias-pull-up; - }; - - mdio { - pins = "gpio69"; - function = "mdio"; - drive-strength = <8>; - bias-pull-up; - }; - }; -}; - -&blsp1_uart5 { - status = "okay"; -}; - -&prng { - status = "okay"; -}; - -&cryptobam { - status = "okay"; -}; - -&crypto { - status = "okay"; -}; - -&qpic_bam { - status = "okay"; -}; - -&qpic_nand { - status = "okay"; - - nand@0 { - reg = <0>; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - nand-bus-width = <8>; - - partitions { - compatible = "qcom,smem-part"; - - partition-0-appsblenv { - compatible = "fixed-partitions"; - label = "0:appsblenv"; - read-only; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - compatible = "u-boot,env"; - label = "env-data"; - reg = <0x0 0x40000>; - - macaddr_appsblenv_ethaddr: ethaddr { - }; - }; - }; - }; - }; -}; - -&mdio { - status = "okay"; - pinctrl-0 = <&mdio_pins>; - pinctrl-names = "default"; - /* - * RESET pins of phy chips - * - * WXR-5950AX12 has 2x RESET pins for QCA8075 and AQR113C. - * The pin of QCA8075 is for the chip and not phys in the chip, the - * pin of AQR113C is for 2x chips. So both pins are not appropriate - * to declare them as reset-gpios in phy nodes. - * Multiple entries in reset-gpios of mdio may not be supported, but - * leave the following as-is to show that the those reset pin exists. - */ - reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>, /* QCA8075 RESET */ - <&tlmm 63 GPIO_ACTIVE_LOW>; /* AQR113C RESET (2x) */ - - aqr113c_1: ethernet-phy@0 { - compatible = "ethernet-phy-ieee802.3-c45"; - reg = <0x0>; - }; - - aqr113c_2: ethernet-phy@8 { - compatible = "ethernet-phy-ieee802.3-c45"; - reg = <0x8>; - }; - - qca8075_1: ethernet-phy@18 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x18>; - }; - - qca8075_2: ethernet-phy@19 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x19>; - }; - - qca8075_3: ethernet-phy@1a { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x1a>; - }; - - qca8075_4: ethernet-phy@1b { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x1b>; - }; - - qca8075_5: ethernet-phy@1c { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x1c>; - }; -}; - -&switch { - status = "okay"; - - switch_cpu_bmp = <0x1>; - switch_lan_bmp = <0x3e>; - switch_wan_bmp = <0x40>; - switch_mac_mode = <0xb>; - switch_mac_mode1 = <0xd>; - switch_mac_mode2 = <0xd>; - bm_tick_mode = <0>; - tm_tick_mode = <0>; - - qcom,port_phyinfo { - port@0 { - port_id = <1>; - phy_address = <0x18>; - }; - - port@1 { - port_id = <2>; - phy_address = <0x19>; - }; - - port@2 { - port_id = <3>; - phy_address = <0x1a>; - }; - - port@3 { - port_id = <4>; - phy_address = <0x1b>; - }; - - port@4 { - port_id = <5>; - ethernet-phy-ieee802.3-c45; - phy_address = <0x0>; - }; - - port@5 { - port_id = <6>; - ethernet-phy-ieee802.3-c45; - phy_address = <0x8>; - }; - }; -}; - -&edma { - status = "okay"; -}; - -&dp2 { - status = "okay"; - phy-handle = <&qca8075_2>; - label = "lan4"; - nvmem-cells = <&macaddr_appsblenv_ethaddr>; - nvmem-cell-names = "mac-address"; -}; - -&dp3 { - status = "okay"; - phy-handle = <&qca8075_3>; - label = "lan3"; - nvmem-cells = <&macaddr_appsblenv_ethaddr>; - nvmem-cell-names = "mac-address"; -}; - -&dp4 { - status = "okay"; - phy-handle = <&qca8075_4>; - label = "lan2"; - nvmem-cells = <&macaddr_appsblenv_ethaddr>; - nvmem-cell-names = "mac-address"; -}; - -&dp5_syn { - status = "okay"; - phy-handle = <&aqr113c_1>; - label = "wan"; - nvmem-cells = <&macaddr_appsblenv_ethaddr>; - nvmem-cell-names = "mac-address"; -}; - -&dp6_syn { - status = "okay"; - phy-handle = <&aqr113c_2>; - label = "lan1"; - nvmem-cells = <&macaddr_appsblenv_ethaddr>; - nvmem-cell-names = "mac-address"; -}; - -&ssphy_0 { - status = "okay"; -}; - -&qusb_phy_0 { - status = "okay"; -}; - -&usb_0 { - status = "okay"; - - vbus-supply = <®_usb_vbus>; -}; - -&wifi { - status = "okay"; - - qcom,ath11k-calibration-variant = "Buffalo-WXR-5950AX12"; -}; diff --git a/target/linux/ipq807x/generic/target.mk b/target/linux/ipq807x/generic/target.mk deleted file mode 100644 index f5cb1fb19b..0000000000 --- a/target/linux/ipq807x/generic/target.mk +++ /dev/null @@ -1 +0,0 @@ -BOARDNAME:=Generic diff --git a/target/linux/ipq807x/image/Makefile b/target/linux/ipq807x/image/Makefile deleted file mode 100644 index f59ad0c15f..0000000000 --- a/target/linux/ipq807x/image/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -include $(TOPDIR)/rules.mk -include $(INCLUDE_DIR)/image.mk - -define Device/Default - PROFILES := Default - KERNEL_LOADADDR := 0x41000000 - DEVICE_DTS = $$(SOC)-$(lastword $(subst _, ,$(1))) - DEVICE_DTS_CONFIG := config@1 - DEVICE_DTS_DIR := $(DTS_DIR)/qcom - IMAGES := sysupgrade.bin - IMAGE/sysupgrade.bin = sysupgrade-tar | append-metadata - IMAGE/sysupgrade.bin/squashfs := -endef - -include $(SUBTARGET).mk - -$(eval $(call BuildImage)) diff --git a/target/linux/ipq807x/image/generic.mk b/target/linux/ipq807x/image/generic.mk deleted file mode 100644 index 6003f2a5c0..0000000000 --- a/target/linux/ipq807x/image/generic.mk +++ /dev/null @@ -1,188 +0,0 @@ -define Device/FitImage - KERNEL_SUFFIX := -uImage.itb - KERNEL = kernel-bin | libdeflate-gzip | fit gzip $$(KDIR)/image-$$(DEVICE_DTS).dtb - KERNEL_NAME := Image -endef - -define Device/FitImageLzma - KERNEL_SUFFIX := -uImage.itb - KERNEL = kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb - KERNEL_NAME := Image -endef - -define Device/EmmcImage - IMAGES += factory.bin sysupgrade.bin - IMAGE/factory.bin := append-rootfs | pad-rootfs | pad-to 64k - IMAGE/sysupgrade.bin/squashfs := append-rootfs | pad-to 64k | sysupgrade-tar rootfs=$$$$@ | append-metadata -endef - -define Device/UbiFit - KERNEL_IN_UBI := 1 - IMAGES := factory.ubi sysupgrade.bin - IMAGE/factory.ubi := append-ubi - IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata -endef - -define Device/buffalo_wxr-5950ax12 - $(call Device/FitImage) - DEVICE_VENDOR := Buffalo - DEVICE_MODEL := WXR-5950AX12 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@hk01 - SOC := ipq8074 - IMAGES := sysupgrade.bin - IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata - DEVICE_PACKAGES := ipq-wifi-buffalo_wxr-5950ax12 -endef -TARGET_DEVICES += buffalo_wxr-5950ax12 - -define Device/compex_wpq873 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_VENDOR := Compex - DEVICE_MODEL := WPQ873 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@hk09.wpq873 - SOC := ipq8072 - IMAGE/factory.ubi := append-ubi | qsdk-ipq-factory-nand -endef -TARGET_DEVICES += compex_wpq873 - -define Device/dynalink_dl-wrx36 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_VENDOR := Dynalink - DEVICE_MODEL := DL-WRX36 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@rt5010w-d350-rev0 - SOC := ipq8072 - DEVICE_PACKAGES := ipq-wifi-dynalink_dl-wrx36 -endef -TARGET_DEVICES += dynalink_dl-wrx36 - -define Device/edgecore_eap102 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_VENDOR := Edgecore - DEVICE_MODEL := EAP102 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@ac02 - SOC := ipq8071 - DEVICE_PACKAGES := ipq-wifi-edgecore_eap102 - IMAGE/factory.ubi := append-ubi | qsdk-ipq-factory-nand -endef -TARGET_DEVICES += edgecore_eap102 - -define Device/edimax_cax1800 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_VENDOR := Edimax - DEVICE_MODEL := CAX1800 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@ac03 - SOC := ipq8070 - DEVICE_PACKAGES := ipq-wifi-edimax_cax1800 -endef -TARGET_DEVICES += edimax_cax1800 - -define Device/netgear_wax218 - $(call Device/FitImage) - $(call Device/UbiFit) - ARTIFACTS := web-ui-factory.fit - DEVICE_VENDOR := Netgear - DEVICE_MODEL := WAX218 - DEVICE_DTS_CONFIG := config@hk07 - BLOCKSIZE := 128k - PAGESIZE := 2048 - SOC := ipq8072 - ARTIFACT/web-ui-factory.fit := append-image initramfs-uImage.itb | \ - ubinize-kernel | qsdk-ipq-factory-nand - DEVICE_PACKAGES := kmod-spi-gpio kmod-spi-bitbang kmod-gpio-nxp-74hc164 \ - ipq-wifi-netgear_wax218 -endef -TARGET_DEVICES += netgear_wax218 - -define Device/prpl_haze - $(call Device/FitImage) - $(call Device/EmmcImage) - DEVICE_VENDOR := prpl Foundation - DEVICE_MODEL := Haze - DEVICE_DTS_CONFIG := config@hk09 - SOC := ipq8072 - DEVICE_PACKAGES += ath11k-firmware-qcn9074 ipq-wifi-prpl_haze kmod-ath11k-pci -endef -TARGET_DEVICES += prpl_haze - -define Device/qnap_301w - $(call Device/FitImage) - $(call Device/EmmcImage) - DEVICE_VENDOR := QNAP - DEVICE_MODEL := 301w - DEVICE_DTS_CONFIG := config@hk01 - KERNEL_SIZE := 16384k - SOC := ipq8072 - DEVICE_PACKAGES += ipq-wifi-qnap_301w -endef -TARGET_DEVICES += qnap_301w - -define Device/redmi_ax6 - $(call Device/xiaomi_ax3600) - DEVICE_VENDOR := Redmi - DEVICE_MODEL := AX6 - DEVICE_PACKAGES := ipq-wifi-redmi_ax6 -endef -TARGET_DEVICES += redmi_ax6 - -define Device/xiaomi_ax3600 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_VENDOR := Xiaomi - DEVICE_MODEL := AX3600 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@ac04 - SOC := ipq8071 - KERNEL_SIZE := 36608k - DEVICE_PACKAGES := ipq-wifi-xiaomi_ax3600 kmod-ath10k-ct-smallbuffers ath10k-firmware-qca9887-ct -ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) - ARTIFACTS := initramfs-factory.ubi - ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-uImage.itb | ubinize-kernel -endif -endef -TARGET_DEVICES += xiaomi_ax3600 - -define Device/xiaomi_ax9000 - $(call Device/FitImage) - $(call Device/UbiFit) - DEVICE_VENDOR := Xiaomi - DEVICE_MODEL := AX9000 - BLOCKSIZE := 128k - PAGESIZE := 2048 - DEVICE_DTS_CONFIG := config@hk14 - SOC := ipq8072 - KERNEL_SIZE := 57344k - DEVICE_PACKAGES := ipq-wifi-xiaomi_ax9000 kmod-ath11k-pci ath11k-firmware-qcn9074 \ - kmod-ath10k-ct ath10k-firmware-qca9887-ct -ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) - ARTIFACTS := initramfs-factory.ubi - ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-uImage.itb | ubinize-kernel -endif -endef -TARGET_DEVICES += xiaomi_ax9000 - -define Device/zyxel_nbg7815 - $(call Device/FitImage) - $(call Device/EmmcImage) - DEVICE_VENDOR := ZYXEL - DEVICE_MODEL := NBG7815 - DEVICE_DTS_CONFIG := config@nbg7815 - SOC := ipq8074 - DEVICE_PACKAGES += ipq-wifi-zyxel_nbg7815 kmod-ath11k-pci kmod-hwmon-tmp103 \ - kmod-bluetooth -endef -TARGET_DEVICES += zyxel_nbg7815 diff --git a/target/linux/ipq807x/patches-6.1/0001-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch b/target/linux/ipq807x/patches-6.1/0001-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch deleted file mode 100644 index dd57eae360..0000000000 --- a/target/linux/ipq807x/patches-6.1/0001-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 6463c10bfdbd684ec7ecfd408ea541283215a088 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:06:28 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq8074: add A53 PLL node - -Add the required node for A53 PLL which will be used to provide the CPU -clock via APCS for APSS scaling. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818220628.339366-9-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -675,6 +675,14 @@ - #mbox-cells = <1>; - }; - -+ a53pll: clock@b116000 { -+ compatible = "qcom,ipq8074-a53pll"; -+ reg = <0x0b116000 0x40>; -+ #clock-cells = <0>; -+ clocks = <&xo>; -+ clock-names = "xo"; -+ }; -+ - timer@b120000 { - #address-cells = <1>; - #size-cells = <1>; diff --git a/target/linux/ipq807x/patches-6.1/0002-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch b/target/linux/ipq807x/patches-6.1/0002-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch deleted file mode 100644 index 0320725999..0000000000 --- a/target/linux/ipq807x/patches-6.1/0002-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch +++ /dev/null @@ -1,134 +0,0 @@ -From e593e834fe8ba9bf314d8215ac05d8787f81efda Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:02:42 +0200 -Subject: [PATCH] thermal/drivers/tsens: Add support for combined interrupt - -Despite using tsens v2.3 IP, IPQ8074 and IPQ6018 only have one IRQ for -signaling both up/low and critical trips. - -Signed-off-by: Robert Marko -Reviewed-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818220245.338396-2-robimarko@gmail.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens-8960.c | 1 + - drivers/thermal/qcom/tsens-v0_1.c | 1 + - drivers/thermal/qcom/tsens-v1.c | 1 + - drivers/thermal/qcom/tsens-v2.c | 1 + - drivers/thermal/qcom/tsens.c | 38 ++++++++++++++++++++++++++----- - drivers/thermal/qcom/tsens.h | 2 ++ - 6 files changed, 38 insertions(+), 6 deletions(-) - ---- a/drivers/thermal/qcom/tsens-8960.c -+++ b/drivers/thermal/qcom/tsens-8960.c -@@ -269,6 +269,7 @@ static const struct tsens_ops ops_8960 = - static struct tsens_features tsens_8960_feat = { - .ver_major = VER_0, - .crit_int = 0, -+ .combo_int = 0, - .adc = 1, - .srot_split = 0, - .max_sensors = 11, ---- a/drivers/thermal/qcom/tsens-v0_1.c -+++ b/drivers/thermal/qcom/tsens-v0_1.c -@@ -549,6 +549,7 @@ static int __init init_8939(struct tsens - static struct tsens_features tsens_v0_1_feat = { - .ver_major = VER_0_1, - .crit_int = 0, -+ .combo_int = 0, - .adc = 1, - .srot_split = 1, - .max_sensors = 11, ---- a/drivers/thermal/qcom/tsens-v1.c -+++ b/drivers/thermal/qcom/tsens-v1.c -@@ -273,6 +273,7 @@ static int calibrate_8976(struct tsens_p - static struct tsens_features tsens_v1_feat = { - .ver_major = VER_1_X, - .crit_int = 0, -+ .combo_int = 0, - .adc = 1, - .srot_split = 1, - .max_sensors = 11, ---- a/drivers/thermal/qcom/tsens-v2.c -+++ b/drivers/thermal/qcom/tsens-v2.c -@@ -31,6 +31,7 @@ - static struct tsens_features tsens_v2_feat = { - .ver_major = VER_2_X, - .crit_int = 1, -+ .combo_int = 0, - .adc = 0, - .srot_split = 1, - .max_sensors = 16, ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -532,6 +532,27 @@ static irqreturn_t tsens_irq_thread(int - return IRQ_HANDLED; - } - -+/** -+ * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts -+ * @irq: irq number -+ * @data: tsens controller private data -+ * -+ * Handle the combined interrupt as if it were 2 separate interrupts, so call the -+ * critical handler first and then the up/low one. -+ * -+ * Return: IRQ_HANDLED -+ */ -+static irqreturn_t tsens_combined_irq_thread(int irq, void *data) -+{ -+ irqreturn_t ret; -+ -+ ret = tsens_critical_irq_thread(irq, data); -+ if (ret != IRQ_HANDLED) -+ return ret; -+ -+ return tsens_irq_thread(irq, data); -+} -+ - static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high) - { - struct tsens_sensor *s = tz->devdata; -@@ -1074,13 +1095,18 @@ static int tsens_register(struct tsens_p - tsens_mC_to_hw(priv->sensor, 0)); - } - -- ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); -- if (ret < 0) -- return ret; -+ if (priv->feat->combo_int) { -+ ret = tsens_register_irq(priv, "combined", -+ tsens_combined_irq_thread); -+ } else { -+ ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); -+ if (ret < 0) -+ return ret; - -- if (priv->feat->crit_int) -- ret = tsens_register_irq(priv, "critical", -- tsens_critical_irq_thread); -+ if (priv->feat->crit_int) -+ ret = tsens_register_irq(priv, "critical", -+ tsens_critical_irq_thread); -+ } - - return ret; - } ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -493,6 +493,7 @@ enum regfield_ids { - * struct tsens_features - Features supported by the IP - * @ver_major: Major number of IP version - * @crit_int: does the IP support critical interrupts? -+ * @combo_int: does the IP use one IRQ for up, low and critical thresholds? - * @adc: do the sensors only output adc code (instead of temperature)? - * @srot_split: does the IP neatly splits the register space into SROT and TM, - * with SROT only being available to secure boot firmware? -@@ -502,6 +503,7 @@ enum regfield_ids { - struct tsens_features { - unsigned int ver_major; - unsigned int crit_int:1; -+ unsigned int combo_int:1; - unsigned int adc:1; - unsigned int srot_split:1; - unsigned int has_watchdog:1; diff --git a/target/linux/ipq807x/patches-6.1/0003-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch b/target/linux/ipq807x/patches-6.1/0003-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch deleted file mode 100644 index 363061813a..0000000000 --- a/target/linux/ipq807x/patches-6.1/0003-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 7805365fee582056b32c69cf35aafbb94b14a8ca Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:02:43 +0200 -Subject: [PATCH] thermal/drivers/tsens: Allow configuring min and max trips - -IPQ8074 and IPQ6018 dont support negative trip temperatures and support -up to 204 degrees C as the max trip temperature. - -So, instead of always setting the -40 as min and 120 degrees C as max -allow it to be configured as part of the features. - -Signed-off-by: Robert Marko -Reviewed-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818220245.338396-3-robimarko@gmail.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens-8960.c | 2 ++ - drivers/thermal/qcom/tsens-v0_1.c | 2 ++ - drivers/thermal/qcom/tsens-v1.c | 2 ++ - drivers/thermal/qcom/tsens-v2.c | 2 ++ - drivers/thermal/qcom/tsens.c | 4 ++-- - drivers/thermal/qcom/tsens.h | 4 ++++ - 6 files changed, 14 insertions(+), 2 deletions(-) - ---- a/drivers/thermal/qcom/tsens-8960.c -+++ b/drivers/thermal/qcom/tsens-8960.c -@@ -273,6 +273,8 @@ static struct tsens_features tsens_8960_ - .adc = 1, - .srot_split = 0, - .max_sensors = 11, -+ .trip_min_temp = -40000, -+ .trip_max_temp = 120000, - }; - - struct tsens_plat_data data_8960 = { ---- a/drivers/thermal/qcom/tsens-v0_1.c -+++ b/drivers/thermal/qcom/tsens-v0_1.c -@@ -553,6 +553,8 @@ static struct tsens_features tsens_v0_1_ - .adc = 1, - .srot_split = 1, - .max_sensors = 11, -+ .trip_min_temp = -40000, -+ .trip_max_temp = 120000, - }; - - static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = { ---- a/drivers/thermal/qcom/tsens-v1.c -+++ b/drivers/thermal/qcom/tsens-v1.c -@@ -277,6 +277,8 @@ static struct tsens_features tsens_v1_fe - .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] = { ---- a/drivers/thermal/qcom/tsens-v2.c -+++ b/drivers/thermal/qcom/tsens-v2.c -@@ -35,6 +35,8 @@ static struct tsens_features tsens_v2_fe - .adc = 0, - .srot_split = 1, - .max_sensors = 16, -+ .trip_min_temp = -40000, -+ .trip_max_temp = 120000, - }; - - static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { ---- a/drivers/thermal/qcom/tsens.c -+++ b/drivers/thermal/qcom/tsens.c -@@ -573,8 +573,8 @@ static int tsens_set_trips(struct therma - dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", - hw_id, __func__, low, high); - -- cl_high = clamp_val(high, -40000, 120000); -- cl_low = clamp_val(low, -40000, 120000); -+ cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp); -+ cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp); - - high_val = tsens_mC_to_hw(s, cl_high); - low_val = tsens_mC_to_hw(s, cl_low); ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -499,6 +499,8 @@ enum regfield_ids { - * with SROT only being available to secure boot firmware? - * @has_watchdog: does this IP support watchdog functionality? - * @max_sensors: maximum sensors supported by this version of the IP -+ * @trip_min_temp: minimum trip temperature supported by this version of the IP -+ * @trip_max_temp: maximum trip temperature supported by this version of the IP - */ - struct tsens_features { - unsigned int ver_major; -@@ -508,6 +510,8 @@ struct tsens_features { - unsigned int srot_split:1; - unsigned int has_watchdog:1; - unsigned int max_sensors; -+ int trip_min_temp; -+ int trip_max_temp; - }; - - /** diff --git a/target/linux/ipq807x/patches-6.1/0004-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch b/target/linux/ipq807x/patches-6.1/0004-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch deleted file mode 100644 index eaea693959..0000000000 --- a/target/linux/ipq807x/patches-6.1/0004-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 0164d794cbc58488a7321272e95958d10cf103a4 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:02:44 +0200 -Subject: [PATCH] thermal/drivers/tsens: Add IPQ8074 support - -Qualcomm IPQ8074 uses tsens v2.3 IP, however unlike other tsens v2 IP -it only has one IRQ, that is used for up/low as well as critical. -It also does not support negative trip temperatures. - -Signed-off-by: Robert Marko -Reviewed-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818220245.338396-4-robimarko@gmail.com -Signed-off-by: Daniel Lezcano ---- - drivers/thermal/qcom/tsens-v2.c | 17 +++++++++++++++++ - drivers/thermal/qcom/tsens.c | 3 +++ - drivers/thermal/qcom/tsens.h | 2 +- - 3 files changed, 21 insertions(+), 1 deletion(-) - ---- a/drivers/thermal/qcom/tsens-v2.c -+++ b/drivers/thermal/qcom/tsens-v2.c -@@ -39,6 +39,17 @@ static struct tsens_features tsens_v2_fe - .trip_max_temp = 120000, - }; - -+static struct tsens_features ipq8074_feat = { -+ .ver_major = VER_2_X, -+ .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 */ -@@ -104,6 +115,12 @@ struct tsens_plat_data data_tsens_v2 = { - .fields = tsens_v2_regfields, - }; - -+struct tsens_plat_data data_ipq8074 = { -+ .ops = &ops_generic_v2, -+ .feat = &ipq8074_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 -@@ -981,6 +981,9 @@ static const struct of_device_id tsens_t - .compatible = "qcom,ipq8064-tsens", - .data = &data_8960, - }, { -+ .compatible = "qcom,ipq8074-tsens", -+ .data = &data_ipq8074, -+ }, { - .compatible = "qcom,mdm9607-tsens", - .data = &data_9607, - }, { ---- a/drivers/thermal/qcom/tsens.h -+++ b/drivers/thermal/qcom/tsens.h -@@ -597,6 +597,6 @@ extern struct tsens_plat_data data_8916, - extern struct tsens_plat_data data_tsens_v1, data_8976, data_8956; - - /* TSENS v2 targets */ --extern struct tsens_plat_data data_8996, data_tsens_v2; -+extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; - - #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/ipq807x/patches-6.1/0005-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch b/target/linux/ipq807x/patches-6.1/0005-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch deleted file mode 100644 index bad75e4597..0000000000 --- a/target/linux/ipq807x/patches-6.1/0005-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch +++ /dev/null @@ -1,130 +0,0 @@ -From c3cc0c2a17f552be2426200e47a9e2c62cf449ce Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:02:45 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq8074: add thermal nodes - -IPQ8074 has a tsens v2.3.0 peripheral which monitors -temperatures around the various subsystems on the -die. - -So lets add the tsens and thermal zone nodes, passive -CPU cooling will come in later patches after CPU frequency -scaling is supported. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818220245.338396-5-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 96 +++++++++++++++++++++++++++ - 1 file changed, 96 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -274,6 +274,16 @@ - status = "disabled"; - }; - -+ tsens: thermal-sensor@4a9000 { -+ compatible = "qcom,ipq8074-tsens"; -+ reg = <0x4a9000 0x1000>, /* TM */ -+ <0x4a8000 0x1000>; /* SROT */ -+ interrupts = ; -+ interrupt-names = "combined"; -+ #qcom,sensors = <16>; -+ #thermal-sensor-cells = <1>; -+ }; -+ - cryptobam: dma-controller@704000 { - compatible = "qcom,bam-v1.7.0"; - reg = <0x00704000 0x20000>; -@@ -874,4 +884,90 @@ - , - ; - }; -+ -+ thermal-zones { -+ nss-top-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 4>; -+ }; -+ -+ nss0-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 5>; -+ }; -+ -+ nss1-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 6>; -+ }; -+ -+ wcss-phya0-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 7>; -+ }; -+ -+ wcss-phya1-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 8>; -+ }; -+ -+ cpu0_thermal: cpu0-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 9>; -+ }; -+ -+ cpu1_thermal: cpu1-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 10>; -+ }; -+ -+ cpu2_thermal: cpu2-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 11>; -+ }; -+ -+ cpu3_thermal: cpu3-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 12>; -+ }; -+ -+ cluster_thermal: cluster-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 13>; -+ }; -+ -+ wcss-phyb0-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 14>; -+ }; -+ -+ wcss-phyb1-thermal { -+ polling-delay-passive = <250>; -+ polling-delay = <1000>; -+ -+ thermal-sensors = <&tsens 15>; -+ }; -+ }; - }; diff --git a/target/linux/ipq807x/patches-6.1/0006-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch b/target/linux/ipq807x/patches-6.1/0006-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch deleted file mode 100644 index e229851649..0000000000 --- a/target/linux/ipq807x/patches-6.1/0006-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0df592a0a1a3fff9133977192677aa915afc174f Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:08:49 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq8074: add clocks to APCS - -APCS now has support for providing the APSS clocks as the child device -for IPQ8074. - -So, add the A53 PLL and XO clocks in order to use APCS as the CPU -clocksource for APSS scaling. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818220849.339732-4-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -680,6 +680,8 @@ - apcs_glb: mailbox@b111000 { - compatible = "qcom,ipq8074-apcs-apps-global"; - reg = <0x0b111000 0x1000>; -+ clocks = <&a53pll>, <&xo>; -+ clock-names = "pll", "xo"; - - #clock-cells = <1>; - #mbox-cells = <1>; diff --git a/target/linux/ipq807x/patches-6.1/0007-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch b/target/linux/ipq807x/patches-6.1/0007-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch deleted file mode 100644 index 9162ea538d..0000000000 --- a/target/linux/ipq807x/patches-6.1/0007-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch +++ /dev/null @@ -1,3601 +0,0 @@ -From e6c5115d6845f25eda7e162dcd783a2044215867 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sun, 30 Oct 2022 18:57:01 +0100 -Subject: [PATCH] clk: qcom: ipq8074: convert to parent data - -Convert the IPQ8074 GCC driver to use parent data instead of global -name matching. - -Utilize ARRAY_SIZE for num_parents instead of hardcoding the value. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221030175703.1103224-1-robimarko@gmail.com ---- - drivers/clk/qcom/gcc-ipq8074.c | 1781 +++++++++++++++----------------- - 1 file changed, 813 insertions(+), 968 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq8074.c -+++ b/drivers/clk/qcom/gcc-ipq8074.c -@@ -49,349 +49,6 @@ enum { - P_UNIPHY2_TX, - }; - --static const char * const gcc_xo_gpll0_gpll0_out_main_div2[] = { -- "xo", -- "gpll0", -- "gpll0_out_main_div2", --}; -- --static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, -- { P_GPLL0_DIV2, 4 }, --}; -- --static const struct parent_map gcc_xo_gpll0_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, --}; -- --static const char * const gcc_xo_gpll0_gpll2_gpll0_out_main_div2[] = { -- "xo", -- "gpll0", -- "gpll2", -- "gpll0_out_main_div2", --}; -- --static const struct parent_map gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, -- { P_GPLL2, 2 }, -- { P_GPLL0_DIV2, 4 }, --}; -- --static const char * const gcc_xo_gpll0_sleep_clk[] = { -- "xo", -- "gpll0", -- "sleep_clk", --}; -- --static const struct parent_map gcc_xo_gpll0_sleep_clk_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 2 }, -- { P_SLEEP_CLK, 6 }, --}; -- --static const char * const gcc_xo_gpll6_gpll0_gpll0_out_main_div2[] = { -- "xo", -- "gpll6", -- "gpll0", -- "gpll0_out_main_div2", --}; -- --static const struct parent_map gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map[] = { -- { P_XO, 0 }, -- { P_GPLL6, 1 }, -- { P_GPLL0, 3 }, -- { P_GPLL0_DIV2, 4 }, --}; -- --static const char * const gcc_xo_gpll0_out_main_div2_gpll0[] = { -- "xo", -- "gpll0_out_main_div2", -- "gpll0", --}; -- --static const struct parent_map gcc_xo_gpll0_out_main_div2_gpll0_map[] = { -- { P_XO, 0 }, -- { P_GPLL0_DIV2, 2 }, -- { P_GPLL0, 1 }, --}; -- --static const char * const gcc_usb3phy_0_cc_pipe_clk_xo[] = { -- "usb3phy_0_cc_pipe_clk", -- "xo", --}; -- --static const struct parent_map gcc_usb3phy_0_cc_pipe_clk_xo_map[] = { -- { P_USB3PHY_0_PIPE, 0 }, -- { P_XO, 2 }, --}; -- --static const char * const gcc_usb3phy_1_cc_pipe_clk_xo[] = { -- "usb3phy_1_cc_pipe_clk", -- "xo", --}; -- --static const struct parent_map gcc_usb3phy_1_cc_pipe_clk_xo_map[] = { -- { P_USB3PHY_1_PIPE, 0 }, -- { P_XO, 2 }, --}; -- --static const char * const gcc_pcie20_phy0_pipe_clk_xo[] = { -- "pcie20_phy0_pipe_clk", -- "xo", --}; -- --static const struct parent_map gcc_pcie20_phy0_pipe_clk_xo_map[] = { -- { P_PCIE20_PHY0_PIPE, 0 }, -- { P_XO, 2 }, --}; -- --static const char * const gcc_pcie20_phy1_pipe_clk_xo[] = { -- "pcie20_phy1_pipe_clk", -- "xo", --}; -- --static const struct parent_map gcc_pcie20_phy1_pipe_clk_xo_map[] = { -- { P_PCIE20_PHY1_PIPE, 0 }, -- { P_XO, 2 }, --}; -- --static const char * const gcc_xo_gpll0_gpll6_gpll0_div2[] = { -- "xo", -- "gpll0", -- "gpll6", -- "gpll0_out_main_div2", --}; -- --static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_div2_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, -- { P_GPLL6, 2 }, -- { P_GPLL0_DIV2, 4 }, --}; -- --static const char * const gcc_xo_gpll0_gpll6_gpll0_out_main_div2[] = { -- "xo", -- "gpll0", -- "gpll6", -- "gpll0_out_main_div2", --}; -- --static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, -- { P_GPLL6, 2 }, -- { P_GPLL0_DIV2, 3 }, --}; -- --static const char * const gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { -- "xo", -- "bias_pll_nss_noc_clk", -- "gpll0", -- "gpll2", --}; -- --static const struct parent_map gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map[] = { -- { P_XO, 0 }, -- { P_BIAS_PLL_NSS_NOC, 1 }, -- { P_GPLL0, 2 }, -- { P_GPLL2, 3 }, --}; -- --static const char * const gcc_xo_nss_crypto_pll_gpll0[] = { -- "xo", -- "nss_crypto_pll", -- "gpll0", --}; -- --static const struct parent_map gcc_xo_nss_crypto_pll_gpll0_map[] = { -- { P_XO, 0 }, -- { P_NSS_CRYPTO_PLL, 1 }, -- { P_GPLL0, 2 }, --}; -- --static const char * const gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6[] = { -- "xo", -- "ubi32_pll", -- "gpll0", -- "gpll2", -- "gpll4", -- "gpll6", --}; -- --static const struct parent_map gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map[] = { -- { P_XO, 0 }, -- { P_UBI32_PLL, 1 }, -- { P_GPLL0, 2 }, -- { P_GPLL2, 3 }, -- { P_GPLL4, 4 }, -- { P_GPLL6, 5 }, --}; -- --static const char * const gcc_xo_gpll0_out_main_div2[] = { -- "xo", -- "gpll0_out_main_div2", --}; -- --static const struct parent_map gcc_xo_gpll0_out_main_div2_map[] = { -- { P_XO, 0 }, -- { P_GPLL0_DIV2, 1 }, --}; -- --static const char * const gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { -- "xo", -- "bias_pll_cc_clk", -- "gpll0", -- "gpll4", -- "nss_crypto_pll", -- "ubi32_pll", --}; -- --static const struct parent_map gcc_xo_bias_gpll0_gpll4_nss_ubi32_map[] = { -- { P_XO, 0 }, -- { P_BIAS_PLL, 1 }, -- { P_GPLL0, 2 }, -- { P_GPLL4, 3 }, -- { P_NSS_CRYPTO_PLL, 4 }, -- { P_UBI32_PLL, 5 }, --}; -- --static const char * const gcc_xo_gpll0_gpll4[] = { -- "xo", -- "gpll0", -- "gpll4", --}; -- --static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, -- { P_GPLL4, 2 }, --}; -- --static const char * const gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { -- "xo", -- "uniphy0_gcc_rx_clk", -- "uniphy0_gcc_tx_clk", -- "ubi32_pll", -- "bias_pll_cc_clk", --}; -- --static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { -- { P_XO, 0 }, -- { P_UNIPHY0_RX, 1 }, -- { P_UNIPHY0_TX, 2 }, -- { P_UBI32_PLL, 5 }, -- { P_BIAS_PLL, 6 }, --}; -- --static const char * const gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { -- "xo", -- "uniphy0_gcc_tx_clk", -- "uniphy0_gcc_rx_clk", -- "ubi32_pll", -- "bias_pll_cc_clk", --}; -- --static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { -- { P_XO, 0 }, -- { P_UNIPHY0_TX, 1 }, -- { P_UNIPHY0_RX, 2 }, -- { P_UBI32_PLL, 5 }, -- { P_BIAS_PLL, 6 }, --}; -- --static const char * const gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { -- "xo", -- "uniphy0_gcc_rx_clk", -- "uniphy0_gcc_tx_clk", -- "uniphy1_gcc_rx_clk", -- "uniphy1_gcc_tx_clk", -- "ubi32_pll", -- "bias_pll_cc_clk", --}; -- --static const struct parent_map --gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map[] = { -- { P_XO, 0 }, -- { P_UNIPHY0_RX, 1 }, -- { P_UNIPHY0_TX, 2 }, -- { P_UNIPHY1_RX, 3 }, -- { P_UNIPHY1_TX, 4 }, -- { P_UBI32_PLL, 5 }, -- { P_BIAS_PLL, 6 }, --}; -- --static const char * const gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { -- "xo", -- "uniphy0_gcc_tx_clk", -- "uniphy0_gcc_rx_clk", -- "uniphy1_gcc_tx_clk", -- "uniphy1_gcc_rx_clk", -- "ubi32_pll", -- "bias_pll_cc_clk", --}; -- --static const struct parent_map --gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map[] = { -- { P_XO, 0 }, -- { P_UNIPHY0_TX, 1 }, -- { P_UNIPHY0_RX, 2 }, -- { P_UNIPHY1_TX, 3 }, -- { P_UNIPHY1_RX, 4 }, -- { P_UBI32_PLL, 5 }, -- { P_BIAS_PLL, 6 }, --}; -- --static const char * const gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { -- "xo", -- "uniphy2_gcc_rx_clk", -- "uniphy2_gcc_tx_clk", -- "ubi32_pll", -- "bias_pll_cc_clk", --}; -- --static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { -- { P_XO, 0 }, -- { P_UNIPHY2_RX, 1 }, -- { P_UNIPHY2_TX, 2 }, -- { P_UBI32_PLL, 5 }, -- { P_BIAS_PLL, 6 }, --}; -- --static const char * const gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { -- "xo", -- "uniphy2_gcc_tx_clk", -- "uniphy2_gcc_rx_clk", -- "ubi32_pll", -- "bias_pll_cc_clk", --}; -- --static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { -- { P_XO, 0 }, -- { P_UNIPHY2_TX, 1 }, -- { P_UNIPHY2_RX, 2 }, -- { P_UBI32_PLL, 5 }, -- { P_BIAS_PLL, 6 }, --}; -- --static const char * const gcc_xo_gpll0_gpll6_gpll0_sleep_clk[] = { -- "xo", -- "gpll0", -- "gpll6", -- "gpll0_out_main_div2", -- "sleep_clk", --}; -- --static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map[] = { -- { P_XO, 0 }, -- { P_GPLL0, 1 }, -- { P_GPLL6, 2 }, -- { P_GPLL0_DIV2, 4 }, -- { P_SLEEP_CLK, 6 }, --}; -- - static struct clk_alpha_pll gpll0_main = { - .offset = 0x21000, - .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], -@@ -400,8 +57,9 @@ static struct clk_alpha_pll gpll0_main = - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gpll0_main", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .ops = &clk_alpha_pll_ops, -@@ -414,9 +72,8 @@ static struct clk_fixed_factor gpll0_out - .div = 2, - .hw.init = &(struct clk_init_data){ - .name = "gpll0_out_main_div2", -- .parent_names = (const char *[]){ -- "gpll0_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gpll0_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -429,9 +86,8 @@ static struct clk_alpha_pll_postdiv gpll - .width = 4, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gpll0", -- .parent_names = (const char *[]){ -- "gpll0_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gpll0_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_alpha_pll_postdiv_ro_ops, - }, -@@ -445,8 +101,9 @@ static struct clk_alpha_pll gpll2_main = - .enable_mask = BIT(2), - .hw.init = &(struct clk_init_data){ - .name = "gpll2_main", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .ops = &clk_alpha_pll_ops, -@@ -461,9 +118,8 @@ static struct clk_alpha_pll_postdiv gpll - .width = 4, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gpll2", -- .parent_names = (const char *[]){ -- "gpll2_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gpll2_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -478,8 +134,9 @@ static struct clk_alpha_pll gpll4_main = - .enable_mask = BIT(5), - .hw.init = &(struct clk_init_data){ - .name = "gpll4_main", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .ops = &clk_alpha_pll_ops, -@@ -494,9 +151,8 @@ static struct clk_alpha_pll_postdiv gpll - .width = 4, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gpll4", -- .parent_names = (const char *[]){ -- "gpll4_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gpll4_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -512,8 +168,9 @@ static struct clk_alpha_pll gpll6_main = - .enable_mask = BIT(7), - .hw.init = &(struct clk_init_data){ - .name = "gpll6_main", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .ops = &clk_alpha_pll_ops, -@@ -528,9 +185,8 @@ static struct clk_alpha_pll_postdiv gpll - .width = 2, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gpll6", -- .parent_names = (const char *[]){ -- "gpll6_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gpll6_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -542,9 +198,8 @@ static struct clk_fixed_factor gpll6_out - .div = 2, - .hw.init = &(struct clk_init_data){ - .name = "gpll6_out_main_div2", -- .parent_names = (const char *[]){ -- "gpll6_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gpll6_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -560,8 +215,9 @@ static struct clk_alpha_pll ubi32_pll_ma - .enable_mask = BIT(6), - .hw.init = &(struct clk_init_data){ - .name = "ubi32_pll_main", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .ops = &clk_alpha_pll_huayra_ops, -@@ -575,9 +231,8 @@ static struct clk_alpha_pll_postdiv ubi3 - .width = 2, - .clkr.hw.init = &(struct clk_init_data){ - .name = "ubi32_pll", -- .parent_names = (const char *[]){ -- "ubi32_pll_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &ubi32_pll_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -592,8 +247,9 @@ static struct clk_alpha_pll nss_crypto_p - .enable_mask = BIT(4), - .hw.init = &(struct clk_init_data){ - .name = "nss_crypto_pll_main", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .ops = &clk_alpha_pll_ops, -@@ -607,9 +263,8 @@ static struct clk_alpha_pll_postdiv nss_ - .width = 4, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_crypto_pll", -- .parent_names = (const char *[]){ -- "nss_crypto_pll_main" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_crypto_pll_main.clkr.hw }, - .num_parents = 1, - .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -623,6 +278,18 @@ static const struct freq_tbl ftbl_pcnoc_ - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_gpll0_out_main_div2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw}, -+ { .hw = &gpll0_out_main_div2.hw}, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+ { P_GPLL0_DIV2, 4 }, -+}; -+ - static struct clk_rcg2 pcnoc_bfdcd_clk_src = { - .cmd_rcgr = 0x27000, - .freq_tbl = ftbl_pcnoc_bfdcd_clk_src, -@@ -630,8 +297,8 @@ static struct clk_rcg2 pcnoc_bfdcd_clk_s - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcnoc_bfdcd_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - .flags = CLK_IS_CRITICAL, - }, -@@ -642,9 +309,8 @@ static struct clk_fixed_factor pcnoc_clk - .div = 1, - .hw.init = &(struct clk_init_data){ - .name = "pcnoc_clk_src", -- .parent_names = (const char *[]){ -- "pcnoc_bfdcd_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -658,8 +324,9 @@ static struct clk_branch gcc_sleep_clk_s - .enable_mask = BIT(1), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sleep_clk_src", -- .parent_names = (const char *[]){ -- "sleep_clk" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "sleep_clk", -+ .name = "sleep_clk", - }, - .num_parents = 1, - .ops = &clk_branch2_ops, -@@ -682,8 +349,8 @@ static struct clk_rcg2 blsp1_qup1_i2c_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup1_i2c_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -708,8 +375,8 @@ static struct clk_rcg2 blsp1_qup1_spi_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup1_spi_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -721,8 +388,8 @@ static struct clk_rcg2 blsp1_qup2_i2c_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup2_i2c_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -735,8 +402,8 @@ static struct clk_rcg2 blsp1_qup2_spi_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup2_spi_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -748,8 +415,8 @@ static struct clk_rcg2 blsp1_qup3_i2c_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup3_i2c_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -762,8 +429,8 @@ static struct clk_rcg2 blsp1_qup3_spi_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup3_spi_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -775,8 +442,8 @@ static struct clk_rcg2 blsp1_qup4_i2c_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup4_i2c_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -789,8 +456,8 @@ static struct clk_rcg2 blsp1_qup4_spi_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup4_spi_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -802,8 +469,8 @@ static struct clk_rcg2 blsp1_qup5_i2c_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup5_i2c_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -816,8 +483,8 @@ static struct clk_rcg2 blsp1_qup5_spi_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup5_spi_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -829,8 +496,8 @@ static struct clk_rcg2 blsp1_qup6_i2c_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup6_i2c_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -843,8 +510,8 @@ static struct clk_rcg2 blsp1_qup6_spi_ap - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_qup6_spi_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -877,8 +544,8 @@ static struct clk_rcg2 blsp1_uart1_apps_ - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_uart1_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -891,8 +558,8 @@ static struct clk_rcg2 blsp1_uart2_apps_ - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_uart2_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -905,8 +572,8 @@ static struct clk_rcg2 blsp1_uart3_apps_ - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_uart3_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -919,8 +586,8 @@ static struct clk_rcg2 blsp1_uart4_apps_ - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_uart4_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -933,8 +600,8 @@ static struct clk_rcg2 blsp1_uart5_apps_ - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_uart5_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -947,8 +614,8 @@ static struct clk_rcg2 blsp1_uart6_apps_ - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "blsp1_uart6_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -958,6 +625,11 @@ static const struct clk_parent_data gcc_ - { .hw = &gpll0.clkr.hw }, - }; - -+static const struct parent_map gcc_xo_gpll0_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+}; -+ - static const struct freq_tbl ftbl_pcie_axi_clk_src[] = { - F(19200000, P_XO, 1, 0, 0), - F(200000000, P_GPLL0, 4, 0, 0), -@@ -972,7 +644,7 @@ static struct clk_rcg2 pcie0_axi_clk_src - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcie0_axi_clk_src", - .parent_data = gcc_xo_gpll0, -- .num_parents = 2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), - .ops = &clk_rcg2_ops, - }, - }; -@@ -981,6 +653,18 @@ static const struct freq_tbl ftbl_pcie_a - F(19200000, P_XO, 1, 0, 0), - }; - -+static const struct clk_parent_data gcc_xo_gpll0_sleep_clk[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .fw_name = "sleep_clk", .name = "sleep_clk" }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_sleep_clk_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 2 }, -+ { P_SLEEP_CLK, 6 }, -+}; -+ - static struct clk_rcg2 pcie0_aux_clk_src = { - .cmd_rcgr = 0x75024, - .freq_tbl = ftbl_pcie_aux_clk_src, -@@ -989,12 +673,22 @@ static struct clk_rcg2 pcie0_aux_clk_src - .parent_map = gcc_xo_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcie0_aux_clk_src", -- .parent_names = gcc_xo_gpll0_sleep_clk, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; - -+static const struct clk_parent_data gcc_pcie20_phy0_pipe_clk_xo[] = { -+ { .name = "pcie20_phy0_pipe_clk" }, -+ { .fw_name = "xo", .name = "xo" }, -+}; -+ -+static const struct parent_map gcc_pcie20_phy0_pipe_clk_xo_map[] = { -+ { P_PCIE20_PHY0_PIPE, 0 }, -+ { P_XO, 2 }, -+}; -+ - static struct clk_regmap_mux pcie0_pipe_clk_src = { - .reg = 0x7501c, - .shift = 8, -@@ -1003,8 +697,8 @@ static struct clk_regmap_mux pcie0_pipe_ - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "pcie0_pipe_clk_src", -- .parent_names = gcc_pcie20_phy0_pipe_clk_xo, -- .num_parents = 2, -+ .parent_data = gcc_pcie20_phy0_pipe_clk_xo, -+ .num_parents = ARRAY_SIZE(gcc_pcie20_phy0_pipe_clk_xo), - .ops = &clk_regmap_mux_closest_ops, - .flags = CLK_SET_RATE_PARENT, - }, -@@ -1019,7 +713,7 @@ static struct clk_rcg2 pcie1_axi_clk_src - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcie1_axi_clk_src", - .parent_data = gcc_xo_gpll0, -- .num_parents = 2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1032,12 +726,22 @@ static struct clk_rcg2 pcie1_aux_clk_src - .parent_map = gcc_xo_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcie1_aux_clk_src", -- .parent_names = gcc_xo_gpll0_sleep_clk, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; - -+static const struct clk_parent_data gcc_pcie20_phy1_pipe_clk_xo[] = { -+ { .name = "pcie20_phy1_pipe_clk" }, -+ { .fw_name = "xo", .name = "xo" }, -+}; -+ -+static const struct parent_map gcc_pcie20_phy1_pipe_clk_xo_map[] = { -+ { P_PCIE20_PHY1_PIPE, 0 }, -+ { P_XO, 2 }, -+}; -+ - static struct clk_regmap_mux pcie1_pipe_clk_src = { - .reg = 0x7601c, - .shift = 8, -@@ -1046,8 +750,8 @@ static struct clk_regmap_mux pcie1_pipe_ - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "pcie1_pipe_clk_src", -- .parent_names = gcc_pcie20_phy1_pipe_clk_xo, -- .num_parents = 2, -+ .parent_data = gcc_pcie20_phy1_pipe_clk_xo, -+ .num_parents = ARRAY_SIZE(gcc_pcie20_phy1_pipe_clk_xo), - .ops = &clk_regmap_mux_closest_ops, - .flags = CLK_SET_RATE_PARENT, - }, -@@ -1066,6 +770,20 @@ static const struct freq_tbl ftbl_sdcc_a - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_gpll2_gpll0_out_main_div2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll2.clkr.hw }, -+ { .hw = &gpll0_out_main_div2.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+ { P_GPLL2, 2 }, -+ { P_GPLL0_DIV2, 4 }, -+}; -+ - static struct clk_rcg2 sdcc1_apps_clk_src = { - .cmd_rcgr = 0x42004, - .freq_tbl = ftbl_sdcc_apps_clk_src, -@@ -1074,8 +792,8 @@ static struct clk_rcg2 sdcc1_apps_clk_sr - .parent_map = gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "sdcc1_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, -- .num_parents = 4, -+ .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll2_gpll0_out_main_div2), - .ops = &clk_rcg2_floor_ops, - }, - }; -@@ -1086,6 +804,20 @@ static const struct freq_tbl ftbl_sdcc_i - F(308570000, P_GPLL6, 3.5, 0, 0), - }; - -+static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_div2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll6.clkr.hw }, -+ { .hw = &gpll0_out_main_div2.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_div2_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+ { P_GPLL6, 2 }, -+ { P_GPLL0_DIV2, 4 }, -+}; -+ - static struct clk_rcg2 sdcc1_ice_core_clk_src = { - .cmd_rcgr = 0x5d000, - .freq_tbl = ftbl_sdcc_ice_core_clk_src, -@@ -1094,8 +826,8 @@ static struct clk_rcg2 sdcc1_ice_core_cl - .parent_map = gcc_xo_gpll0_gpll6_gpll0_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "sdcc1_ice_core_clk_src", -- .parent_names = gcc_xo_gpll0_gpll6_gpll0_div2, -- .num_parents = 4, -+ .parent_data = gcc_xo_gpll0_gpll6_gpll0_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1108,8 +840,8 @@ static struct clk_rcg2 sdcc2_apps_clk_sr - .parent_map = gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "sdcc2_apps_clk_src", -- .parent_names = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, -- .num_parents = 4, -+ .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll2_gpll0_out_main_div2), - .ops = &clk_rcg2_floor_ops, - }, - }; -@@ -1121,6 +853,18 @@ static const struct freq_tbl ftbl_usb_ma - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_out_main_div2_gpll0[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0_out_main_div2.hw }, -+ { .hw = &gpll0.clkr.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_out_main_div2_gpll0_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0_DIV2, 2 }, -+ { P_GPLL0, 1 }, -+}; -+ - static struct clk_rcg2 usb0_master_clk_src = { - .cmd_rcgr = 0x3e00c, - .freq_tbl = ftbl_usb_master_clk_src, -@@ -1129,8 +873,8 @@ static struct clk_rcg2 usb0_master_clk_s - .parent_map = gcc_xo_gpll0_out_main_div2_gpll0_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "usb0_master_clk_src", -- .parent_names = gcc_xo_gpll0_out_main_div2_gpll0, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_out_main_div2_gpll0, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2_gpll0), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1148,8 +892,8 @@ static struct clk_rcg2 usb0_aux_clk_src - .parent_map = gcc_xo_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "usb0_aux_clk_src", -- .parent_names = gcc_xo_gpll0_sleep_clk, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1161,6 +905,20 @@ static const struct freq_tbl ftbl_usb_mo - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll6_gpll0_gpll0_out_main_div2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll6.clkr.hw }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll0_out_main_div2.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL6, 1 }, -+ { P_GPLL0, 3 }, -+ { P_GPLL0_DIV2, 4 }, -+}; -+ - static struct clk_rcg2 usb0_mock_utmi_clk_src = { - .cmd_rcgr = 0x3e020, - .freq_tbl = ftbl_usb_mock_utmi_clk_src, -@@ -1169,12 +927,22 @@ static struct clk_rcg2 usb0_mock_utmi_cl - .parent_map = gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "usb0_mock_utmi_clk_src", -- .parent_names = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, -- .num_parents = 4, -+ .parent_data = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll6_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; - -+static const struct clk_parent_data gcc_usb3phy_0_cc_pipe_clk_xo[] = { -+ { .name = "usb3phy_0_cc_pipe_clk" }, -+ { .fw_name = "xo", .name = "xo" }, -+}; -+ -+static const struct parent_map gcc_usb3phy_0_cc_pipe_clk_xo_map[] = { -+ { P_USB3PHY_0_PIPE, 0 }, -+ { P_XO, 2 }, -+}; -+ - static struct clk_regmap_mux usb0_pipe_clk_src = { - .reg = 0x3e048, - .shift = 8, -@@ -1183,8 +951,8 @@ static struct clk_regmap_mux usb0_pipe_c - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "usb0_pipe_clk_src", -- .parent_names = gcc_usb3phy_0_cc_pipe_clk_xo, -- .num_parents = 2, -+ .parent_data = gcc_usb3phy_0_cc_pipe_clk_xo, -+ .num_parents = ARRAY_SIZE(gcc_usb3phy_0_cc_pipe_clk_xo), - .ops = &clk_regmap_mux_closest_ops, - .flags = CLK_SET_RATE_PARENT, - }, -@@ -1199,8 +967,8 @@ static struct clk_rcg2 usb1_master_clk_s - .parent_map = gcc_xo_gpll0_out_main_div2_gpll0_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "usb1_master_clk_src", -- .parent_names = gcc_xo_gpll0_out_main_div2_gpll0, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_out_main_div2_gpll0, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2_gpll0), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1213,8 +981,8 @@ static struct clk_rcg2 usb1_aux_clk_src - .parent_map = gcc_xo_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "usb1_aux_clk_src", -- .parent_names = gcc_xo_gpll0_sleep_clk, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1227,12 +995,22 @@ static struct clk_rcg2 usb1_mock_utmi_cl - .parent_map = gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "usb1_mock_utmi_clk_src", -- .parent_names = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, -- .num_parents = 4, -+ .parent_data = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll6_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; - -+static const struct clk_parent_data gcc_usb3phy_1_cc_pipe_clk_xo[] = { -+ { .name = "usb3phy_1_cc_pipe_clk" }, -+ { .fw_name = "xo", .name = "xo" }, -+}; -+ -+static const struct parent_map gcc_usb3phy_1_cc_pipe_clk_xo_map[] = { -+ { P_USB3PHY_1_PIPE, 0 }, -+ { P_XO, 2 }, -+}; -+ - static struct clk_regmap_mux usb1_pipe_clk_src = { - .reg = 0x3f048, - .shift = 8, -@@ -1241,8 +1019,8 @@ static struct clk_regmap_mux usb1_pipe_c - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "usb1_pipe_clk_src", -- .parent_names = gcc_usb3phy_1_cc_pipe_clk_xo, -- .num_parents = 2, -+ .parent_data = gcc_usb3phy_1_cc_pipe_clk_xo, -+ .num_parents = ARRAY_SIZE(gcc_usb3phy_1_cc_pipe_clk_xo), - .ops = &clk_regmap_mux_closest_ops, - .flags = CLK_SET_RATE_PARENT, - }, -@@ -1256,8 +1034,9 @@ static struct clk_branch gcc_xo_clk_src - .enable_mask = BIT(1), - .hw.init = &(struct clk_init_data){ - .name = "gcc_xo_clk_src", -- .parent_names = (const char *[]){ -- "xo" -+ .parent_data = &(const struct clk_parent_data){ -+ .fw_name = "xo", -+ .name = "xo", - }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, -@@ -1271,9 +1050,8 @@ static struct clk_fixed_factor gcc_xo_di - .div = 4, - .hw.init = &(struct clk_init_data){ - .name = "gcc_xo_div4_clk_src", -- .parent_names = (const char *[]){ -- "gcc_xo_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1291,6 +1069,20 @@ static const struct freq_tbl ftbl_system - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_out_main_div2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll6.clkr.hw }, -+ { .hw = &gpll0_out_main_div2.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+ { P_GPLL6, 2 }, -+ { P_GPLL0_DIV2, 3 }, -+}; -+ - static struct clk_rcg2 system_noc_bfdcd_clk_src = { - .cmd_rcgr = 0x26004, - .freq_tbl = ftbl_system_noc_bfdcd_clk_src, -@@ -1298,8 +1090,8 @@ static struct clk_rcg2 system_noc_bfdcd_ - .parent_map = gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "system_noc_bfdcd_clk_src", -- .parent_names = gcc_xo_gpll0_gpll6_gpll0_out_main_div2, -- .num_parents = 4, -+ .parent_data = gcc_xo_gpll0_gpll6_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - .flags = CLK_IS_CRITICAL, - }, -@@ -1310,9 +1102,8 @@ static struct clk_fixed_factor system_no - .div = 1, - .hw.init = &(struct clk_init_data){ - .name = "system_noc_clk_src", -- .parent_names = (const char *[]){ -- "system_noc_bfdcd_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &system_noc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1333,7 +1124,7 @@ static struct clk_rcg2 nss_ce_clk_src = - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_ce_clk_src", - .parent_data = gcc_xo_gpll0, -- .num_parents = 2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1344,6 +1135,20 @@ static const struct freq_tbl ftbl_nss_no - { } - }; - -+static const struct clk_parent_data gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "bias_pll_nss_noc_clk" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll2.clkr.hw }, -+}; -+ -+static const struct parent_map gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map[] = { -+ { P_XO, 0 }, -+ { P_BIAS_PLL_NSS_NOC, 1 }, -+ { P_GPLL0, 2 }, -+ { P_GPLL2, 3 }, -+}; -+ - static struct clk_rcg2 nss_noc_bfdcd_clk_src = { - .cmd_rcgr = 0x68088, - .freq_tbl = ftbl_nss_noc_bfdcd_clk_src, -@@ -1351,8 +1156,8 @@ static struct clk_rcg2 nss_noc_bfdcd_clk - .parent_map = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_noc_bfdcd_clk_src", -- .parent_names = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2, -- .num_parents = 4, -+ .parent_data = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2, -+ .num_parents = ARRAY_SIZE(gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1362,9 +1167,8 @@ static struct clk_fixed_factor nss_noc_c - .div = 1, - .hw.init = &(struct clk_init_data){ - .name = "nss_noc_clk_src", -- .parent_names = (const char *[]){ -- "nss_noc_bfdcd_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1377,6 +1181,18 @@ static const struct freq_tbl ftbl_nss_cr - { } - }; - -+static const struct clk_parent_data gcc_xo_nss_crypto_pll_gpll0[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &nss_crypto_pll.clkr.hw }, -+ { .hw = &gpll0.clkr.hw }, -+}; -+ -+static const struct parent_map gcc_xo_nss_crypto_pll_gpll0_map[] = { -+ { P_XO, 0 }, -+ { P_NSS_CRYPTO_PLL, 1 }, -+ { P_GPLL0, 2 }, -+}; -+ - static struct clk_rcg2 nss_crypto_clk_src = { - .cmd_rcgr = 0x68144, - .freq_tbl = ftbl_nss_crypto_clk_src, -@@ -1385,8 +1201,8 @@ static struct clk_rcg2 nss_crypto_clk_sr - .parent_map = gcc_xo_nss_crypto_pll_gpll0_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_crypto_clk_src", -- .parent_names = gcc_xo_nss_crypto_pll_gpll0, -- .num_parents = 3, -+ .parent_data = gcc_xo_nss_crypto_pll_gpll0, -+ .num_parents = ARRAY_SIZE(gcc_xo_nss_crypto_pll_gpll0), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1400,6 +1216,24 @@ static const struct freq_tbl ftbl_nss_ub - { } - }; - -+static const struct clk_parent_data gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll2.clkr.hw }, -+ { .hw = &gpll4.clkr.hw }, -+ { .hw = &gpll6.clkr.hw }, -+}; -+ -+static const struct parent_map gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map[] = { -+ { P_XO, 0 }, -+ { P_UBI32_PLL, 1 }, -+ { P_GPLL0, 2 }, -+ { P_GPLL2, 3 }, -+ { P_GPLL4, 4 }, -+ { P_GPLL6, 5 }, -+}; -+ - static struct clk_rcg2 nss_ubi0_clk_src = { - .cmd_rcgr = 0x68104, - .freq_tbl = ftbl_nss_ubi_clk_src, -@@ -1407,8 +1241,8 @@ static struct clk_rcg2 nss_ubi0_clk_src - .parent_map = gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_ubi0_clk_src", -- .parent_names = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, -- .num_parents = 6, -+ .parent_data = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, -+ .num_parents = ARRAY_SIZE(gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6), - .ops = &clk_rcg2_ops, - .flags = CLK_SET_RATE_PARENT, - }, -@@ -1421,9 +1255,8 @@ static struct clk_regmap_div nss_ubi0_di - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_ubi0_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_ubi0_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ubi0_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1438,8 +1271,8 @@ static struct clk_rcg2 nss_ubi1_clk_src - .parent_map = gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_ubi1_clk_src", -- .parent_names = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, -- .num_parents = 6, -+ .parent_data = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, -+ .num_parents = ARRAY_SIZE(gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6), - .ops = &clk_rcg2_ops, - .flags = CLK_SET_RATE_PARENT, - }, -@@ -1452,9 +1285,8 @@ static struct clk_regmap_div nss_ubi1_di - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_ubi1_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_ubi1_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ubi1_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ro_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1468,6 +1300,16 @@ static const struct freq_tbl ftbl_ubi_mp - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_out_main_div2[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0_out_main_div2.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_out_main_div2_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0_DIV2, 1 }, -+}; -+ - static struct clk_rcg2 ubi_mpt_clk_src = { - .cmd_rcgr = 0x68090, - .freq_tbl = ftbl_ubi_mpt_clk_src, -@@ -1475,8 +1317,8 @@ static struct clk_rcg2 ubi_mpt_clk_src = - .parent_map = gcc_xo_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "ubi_mpt_clk_src", -- .parent_names = gcc_xo_gpll0_out_main_div2, -- .num_parents = 2, -+ .parent_data = gcc_xo_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1487,6 +1329,18 @@ static const struct freq_tbl ftbl_nss_im - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_gpll4[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll4.clkr.hw }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+ { P_GPLL4, 2 }, -+}; -+ - static struct clk_rcg2 nss_imem_clk_src = { - .cmd_rcgr = 0x68158, - .freq_tbl = ftbl_nss_imem_clk_src, -@@ -1494,8 +1348,8 @@ static struct clk_rcg2 nss_imem_clk_src - .parent_map = gcc_xo_gpll0_gpll4_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_imem_clk_src", -- .parent_names = gcc_xo_gpll0_gpll4, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll4, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll4), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1506,6 +1360,24 @@ static const struct freq_tbl ftbl_nss_pp - { } - }; - -+static const struct clk_parent_data gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "bias_pll_cc_clk" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll4.clkr.hw }, -+ { .hw = &nss_crypto_pll.clkr.hw }, -+ { .hw = &ubi32_pll.clkr.hw }, -+}; -+ -+static const struct parent_map gcc_xo_bias_gpll0_gpll4_nss_ubi32_map[] = { -+ { P_XO, 0 }, -+ { P_BIAS_PLL, 1 }, -+ { P_GPLL0, 2 }, -+ { P_GPLL4, 3 }, -+ { P_NSS_CRYPTO_PLL, 4 }, -+ { P_UBI32_PLL, 5 }, -+}; -+ - static struct clk_rcg2 nss_ppe_clk_src = { - .cmd_rcgr = 0x68080, - .freq_tbl = ftbl_nss_ppe_clk_src, -@@ -1513,8 +1385,8 @@ static struct clk_rcg2 nss_ppe_clk_src = - .parent_map = gcc_xo_bias_gpll0_gpll4_nss_ubi32_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_ppe_clk_src", -- .parent_names = gcc_xo_bias_gpll0_gpll4_nss_ubi32, -- .num_parents = 6, -+ .parent_data = gcc_xo_bias_gpll0_gpll4_nss_ubi32, -+ .num_parents = ARRAY_SIZE(gcc_xo_bias_gpll0_gpll4_nss_ubi32), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1524,9 +1396,8 @@ static struct clk_fixed_factor nss_ppe_c - .div = 4, - .hw.init = &(struct clk_init_data){ - .name = "nss_ppe_cdiv_clk_src", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1540,6 +1411,22 @@ static const struct freq_tbl ftbl_nss_po - { } - }; - -+static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "uniphy0_gcc_rx_clk" }, -+ { .name = "uniphy0_gcc_tx_clk" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .name = "bias_pll_cc_clk" }, -+}; -+ -+static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { -+ { P_XO, 0 }, -+ { P_UNIPHY0_RX, 1 }, -+ { P_UNIPHY0_TX, 2 }, -+ { P_UBI32_PLL, 5 }, -+ { P_BIAS_PLL, 6 }, -+}; -+ - static struct clk_rcg2 nss_port1_rx_clk_src = { - .cmd_rcgr = 0x68020, - .freq_tbl = ftbl_nss_port1_rx_clk_src, -@@ -1547,8 +1434,8 @@ static struct clk_rcg2 nss_port1_rx_clk_ - .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port1_rx_clk_src", -- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1560,9 +1447,8 @@ static struct clk_regmap_div nss_port1_r - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port1_rx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port1_rx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port1_rx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1577,6 +1463,22 @@ static const struct freq_tbl ftbl_nss_po - { } - }; - -+static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "uniphy0_gcc_tx_clk" }, -+ { .name = "uniphy0_gcc_rx_clk" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .name = "bias_pll_cc_clk" }, -+}; -+ -+static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { -+ { P_XO, 0 }, -+ { P_UNIPHY0_TX, 1 }, -+ { P_UNIPHY0_RX, 2 }, -+ { P_UBI32_PLL, 5 }, -+ { P_BIAS_PLL, 6 }, -+}; -+ - static struct clk_rcg2 nss_port1_tx_clk_src = { - .cmd_rcgr = 0x68028, - .freq_tbl = ftbl_nss_port1_tx_clk_src, -@@ -1584,8 +1486,8 @@ static struct clk_rcg2 nss_port1_tx_clk_ - .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port1_tx_clk_src", -- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1597,9 +1499,8 @@ static struct clk_regmap_div nss_port1_t - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port1_tx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port1_tx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port1_tx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1614,8 +1515,8 @@ static struct clk_rcg2 nss_port2_rx_clk_ - .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port2_rx_clk_src", -- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1627,9 +1528,8 @@ static struct clk_regmap_div nss_port2_r - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port2_rx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port2_rx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port2_rx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1644,8 +1544,8 @@ static struct clk_rcg2 nss_port2_tx_clk_ - .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port2_tx_clk_src", -- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1657,9 +1557,8 @@ static struct clk_regmap_div nss_port2_t - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port2_tx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port2_tx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port2_tx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1674,8 +1573,8 @@ static struct clk_rcg2 nss_port3_rx_clk_ - .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port3_rx_clk_src", -- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1687,9 +1586,8 @@ static struct clk_regmap_div nss_port3_r - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port3_rx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port3_rx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port3_rx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1704,8 +1602,8 @@ static struct clk_rcg2 nss_port3_tx_clk_ - .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port3_tx_clk_src", -- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1717,9 +1615,8 @@ static struct clk_regmap_div nss_port3_t - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port3_tx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port3_tx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port3_tx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1734,8 +1631,8 @@ static struct clk_rcg2 nss_port4_rx_clk_ - .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port4_rx_clk_src", -- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1747,9 +1644,8 @@ static struct clk_regmap_div nss_port4_r - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port4_rx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port4_rx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port4_rx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1764,8 +1660,8 @@ static struct clk_rcg2 nss_port4_tx_clk_ - .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port4_tx_clk_src", -- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1777,9 +1673,8 @@ static struct clk_regmap_div nss_port4_t - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port4_tx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port4_tx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port4_tx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1799,6 +1694,27 @@ static const struct freq_tbl ftbl_nss_po - { } - }; - -+static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "uniphy0_gcc_rx_clk" }, -+ { .name = "uniphy0_gcc_tx_clk" }, -+ { .name = "uniphy1_gcc_rx_clk" }, -+ { .name = "uniphy1_gcc_tx_clk" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .name = "bias_pll_cc_clk" }, -+}; -+ -+static const struct parent_map -+gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map[] = { -+ { P_XO, 0 }, -+ { P_UNIPHY0_RX, 1 }, -+ { P_UNIPHY0_TX, 2 }, -+ { P_UNIPHY1_RX, 3 }, -+ { P_UNIPHY1_TX, 4 }, -+ { P_UBI32_PLL, 5 }, -+ { P_BIAS_PLL, 6 }, -+}; -+ - static struct clk_rcg2 nss_port5_rx_clk_src = { - .cmd_rcgr = 0x68060, - .freq_tbl = ftbl_nss_port5_rx_clk_src, -@@ -1806,8 +1722,8 @@ static struct clk_rcg2 nss_port5_rx_clk_ - .parent_map = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port5_rx_clk_src", -- .parent_names = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, -- .num_parents = 7, -+ .parent_data = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1819,9 +1735,8 @@ static struct clk_regmap_div nss_port5_r - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port5_rx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port5_rx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_rx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1841,6 +1756,27 @@ static const struct freq_tbl ftbl_nss_po - { } - }; - -+static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "uniphy0_gcc_tx_clk" }, -+ { .name = "uniphy0_gcc_rx_clk" }, -+ { .name = "uniphy1_gcc_tx_clk" }, -+ { .name = "uniphy1_gcc_rx_clk" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .name = "bias_pll_cc_clk" }, -+}; -+ -+static const struct parent_map -+gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map[] = { -+ { P_XO, 0 }, -+ { P_UNIPHY0_TX, 1 }, -+ { P_UNIPHY0_RX, 2 }, -+ { P_UNIPHY1_TX, 3 }, -+ { P_UNIPHY1_RX, 4 }, -+ { P_UBI32_PLL, 5 }, -+ { P_BIAS_PLL, 6 }, -+}; -+ - static struct clk_rcg2 nss_port5_tx_clk_src = { - .cmd_rcgr = 0x68068, - .freq_tbl = ftbl_nss_port5_tx_clk_src, -@@ -1848,8 +1784,8 @@ static struct clk_rcg2 nss_port5_tx_clk_ - .parent_map = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port5_tx_clk_src", -- .parent_names = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, -- .num_parents = 7, -+ .parent_data = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1861,9 +1797,8 @@ static struct clk_regmap_div nss_port5_t - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port5_tx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port5_tx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_tx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1883,6 +1818,22 @@ static const struct freq_tbl ftbl_nss_po - { } - }; - -+static const struct clk_parent_data gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "uniphy2_gcc_rx_clk" }, -+ { .name = "uniphy2_gcc_tx_clk" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .name = "bias_pll_cc_clk" }, -+}; -+ -+static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { -+ { P_XO, 0 }, -+ { P_UNIPHY2_RX, 1 }, -+ { P_UNIPHY2_TX, 2 }, -+ { P_UBI32_PLL, 5 }, -+ { P_BIAS_PLL, 6 }, -+}; -+ - static struct clk_rcg2 nss_port6_rx_clk_src = { - .cmd_rcgr = 0x68070, - .freq_tbl = ftbl_nss_port6_rx_clk_src, -@@ -1890,8 +1841,8 @@ static struct clk_rcg2 nss_port6_rx_clk_ - .parent_map = gcc_xo_uniphy2_rx_tx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port6_rx_clk_src", -- .parent_names = gcc_xo_uniphy2_rx_tx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy2_rx_tx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy2_rx_tx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1903,9 +1854,8 @@ static struct clk_regmap_div nss_port6_r - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port6_rx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port6_rx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port6_rx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1925,6 +1875,22 @@ static const struct freq_tbl ftbl_nss_po - { } - }; - -+static const struct clk_parent_data gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .name = "uniphy2_gcc_tx_clk" }, -+ { .name = "uniphy2_gcc_rx_clk" }, -+ { .hw = &ubi32_pll.clkr.hw }, -+ { .name = "bias_pll_cc_clk" }, -+}; -+ -+static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { -+ { P_XO, 0 }, -+ { P_UNIPHY2_TX, 1 }, -+ { P_UNIPHY2_RX, 2 }, -+ { P_UBI32_PLL, 5 }, -+ { P_BIAS_PLL, 6 }, -+}; -+ - static struct clk_rcg2 nss_port6_tx_clk_src = { - .cmd_rcgr = 0x68078, - .freq_tbl = ftbl_nss_port6_tx_clk_src, -@@ -1932,8 +1898,8 @@ static struct clk_rcg2 nss_port6_tx_clk_ - .parent_map = gcc_xo_uniphy2_tx_rx_ubi32_bias_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "nss_port6_tx_clk_src", -- .parent_names = gcc_xo_uniphy2_tx_rx_ubi32_bias, -- .num_parents = 5, -+ .parent_data = gcc_xo_uniphy2_tx_rx_ubi32_bias, -+ .num_parents = ARRAY_SIZE(gcc_xo_uniphy2_tx_rx_ubi32_bias), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1945,9 +1911,8 @@ static struct clk_regmap_div nss_port6_t - .clkr = { - .hw.init = &(struct clk_init_data){ - .name = "nss_port6_tx_div_clk_src", -- .parent_names = (const char *[]){ -- "nss_port6_tx_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port6_tx_clk_src.clkr.hw }, - .num_parents = 1, - .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT, -@@ -1970,8 +1935,8 @@ static struct clk_rcg2 crypto_clk_src = - .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "crypto_clk_src", -- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, -- .num_parents = 3, -+ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), - .ops = &clk_rcg2_ops, - }, - }; -@@ -1981,6 +1946,22 @@ static struct freq_tbl ftbl_gp_clk_src[] - { } - }; - -+static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_sleep_clk[] = { -+ { .fw_name = "xo", .name = "xo" }, -+ { .hw = &gpll0.clkr.hw }, -+ { .hw = &gpll6.clkr.hw }, -+ { .hw = &gpll0_out_main_div2.hw }, -+ { .fw_name = "sleep_clk", .name = "sleep_clk" }, -+}; -+ -+static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map[] = { -+ { P_XO, 0 }, -+ { P_GPLL0, 1 }, -+ { P_GPLL6, 2 }, -+ { P_GPLL0_DIV2, 4 }, -+ { P_SLEEP_CLK, 6 }, -+}; -+ - static struct clk_rcg2 gp1_clk_src = { - .cmd_rcgr = 0x08004, - .freq_tbl = ftbl_gp_clk_src, -@@ -1989,8 +1970,8 @@ static struct clk_rcg2 gp1_clk_src = { - .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gp1_clk_src", -- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, -- .num_parents = 5, -+ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; -@@ -2003,8 +1984,8 @@ static struct clk_rcg2 gp2_clk_src = { - .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gp2_clk_src", -- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, -- .num_parents = 5, -+ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; -@@ -2017,8 +1998,8 @@ static struct clk_rcg2 gp3_clk_src = { - .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "gp3_clk_src", -- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, -- .num_parents = 5, -+ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), - .ops = &clk_rcg2_ops, - }, - }; -@@ -2030,9 +2011,8 @@ static struct clk_branch gcc_blsp1_ahb_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2047,9 +2027,8 @@ static struct clk_branch gcc_blsp1_qup1_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup1_i2c_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup1_i2c_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup1_i2c_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2064,9 +2043,8 @@ static struct clk_branch gcc_blsp1_qup1_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup1_spi_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup1_spi_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup1_spi_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2081,9 +2059,8 @@ static struct clk_branch gcc_blsp1_qup2_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup2_i2c_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup2_i2c_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup2_i2c_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2098,9 +2075,8 @@ static struct clk_branch gcc_blsp1_qup2_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup2_spi_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup2_spi_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup2_spi_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2115,9 +2091,8 @@ static struct clk_branch gcc_blsp1_qup3_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup3_i2c_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup3_i2c_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup3_i2c_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2132,9 +2107,8 @@ static struct clk_branch gcc_blsp1_qup3_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup3_spi_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup3_spi_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup3_spi_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2149,9 +2123,8 @@ static struct clk_branch gcc_blsp1_qup4_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup4_i2c_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup4_i2c_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup4_i2c_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2166,9 +2139,8 @@ static struct clk_branch gcc_blsp1_qup4_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup4_spi_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup4_spi_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup4_spi_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2183,9 +2155,8 @@ static struct clk_branch gcc_blsp1_qup5_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup5_i2c_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup5_i2c_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup5_i2c_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2200,9 +2171,8 @@ static struct clk_branch gcc_blsp1_qup5_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup5_spi_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup5_spi_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup5_spi_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2217,9 +2187,8 @@ static struct clk_branch gcc_blsp1_qup6_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup6_i2c_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup6_i2c_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup6_i2c_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2234,9 +2203,8 @@ static struct clk_branch gcc_blsp1_qup6_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_qup6_spi_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_qup6_spi_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_qup6_spi_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2251,9 +2219,8 @@ static struct clk_branch gcc_blsp1_uart1 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_uart1_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_uart1_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_uart1_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2268,9 +2235,8 @@ static struct clk_branch gcc_blsp1_uart2 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_uart2_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_uart2_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_uart2_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2285,9 +2251,8 @@ static struct clk_branch gcc_blsp1_uart3 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_uart3_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_uart3_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_uart3_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2302,9 +2267,8 @@ static struct clk_branch gcc_blsp1_uart4 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_uart4_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_uart4_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_uart4_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2319,9 +2283,8 @@ static struct clk_branch gcc_blsp1_uart5 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_uart5_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_uart5_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_uart5_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2336,9 +2299,8 @@ static struct clk_branch gcc_blsp1_uart6 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_blsp1_uart6_apps_clk", -- .parent_names = (const char *[]){ -- "blsp1_uart6_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &blsp1_uart6_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2354,9 +2316,8 @@ static struct clk_branch gcc_prng_ahb_cl - .enable_mask = BIT(8), - .hw.init = &(struct clk_init_data){ - .name = "gcc_prng_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2371,9 +2332,8 @@ static struct clk_branch gcc_qpic_ahb_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_qpic_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2388,9 +2348,8 @@ static struct clk_branch gcc_qpic_clk = - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_qpic_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2405,9 +2364,8 @@ static struct clk_branch gcc_pcie0_ahb_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie0_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2422,9 +2380,8 @@ static struct clk_branch gcc_pcie0_aux_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie0_aux_clk", -- .parent_names = (const char *[]){ -- "pcie0_aux_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie0_aux_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2439,9 +2396,8 @@ static struct clk_branch gcc_pcie0_axi_m - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie0_axi_m_clk", -- .parent_names = (const char *[]){ -- "pcie0_axi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie0_axi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2456,9 +2412,8 @@ static struct clk_branch gcc_pcie0_axi_s - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie0_axi_s_clk", -- .parent_names = (const char *[]){ -- "pcie0_axi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie0_axi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2474,9 +2429,8 @@ static struct clk_branch gcc_pcie0_pipe_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie0_pipe_clk", -- .parent_names = (const char *[]){ -- "pcie0_pipe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie0_pipe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2491,9 +2445,8 @@ static struct clk_branch gcc_sys_noc_pci - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sys_noc_pcie0_axi_clk", -- .parent_names = (const char *[]){ -- "pcie0_axi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie0_axi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2508,9 +2461,8 @@ static struct clk_branch gcc_pcie1_ahb_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie1_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2525,9 +2477,8 @@ static struct clk_branch gcc_pcie1_aux_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie1_aux_clk", -- .parent_names = (const char *[]){ -- "pcie1_aux_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie1_aux_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2542,9 +2493,8 @@ static struct clk_branch gcc_pcie1_axi_m - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie1_axi_m_clk", -- .parent_names = (const char *[]){ -- "pcie1_axi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie1_axi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2559,9 +2509,8 @@ static struct clk_branch gcc_pcie1_axi_s - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie1_axi_s_clk", -- .parent_names = (const char *[]){ -- "pcie1_axi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie1_axi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2577,9 +2526,8 @@ static struct clk_branch gcc_pcie1_pipe_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_pcie1_pipe_clk", -- .parent_names = (const char *[]){ -- "pcie1_pipe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie1_pipe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2594,9 +2542,8 @@ static struct clk_branch gcc_sys_noc_pci - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sys_noc_pcie1_axi_clk", -- .parent_names = (const char *[]){ -- "pcie1_axi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcie1_axi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2611,9 +2558,8 @@ static struct clk_branch gcc_usb0_aux_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb0_aux_clk", -- .parent_names = (const char *[]){ -- "usb0_aux_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb0_aux_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2628,9 +2574,8 @@ static struct clk_branch gcc_sys_noc_usb - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sys_noc_usb0_axi_clk", -- .parent_names = (const char *[]){ -- "usb0_master_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb0_master_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2645,9 +2590,8 @@ static struct clk_branch gcc_usb0_master - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb0_master_clk", -- .parent_names = (const char *[]){ -- "usb0_master_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb0_master_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2662,9 +2606,8 @@ static struct clk_branch gcc_usb0_mock_u - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb0_mock_utmi_clk", -- .parent_names = (const char *[]){ -- "usb0_mock_utmi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb0_mock_utmi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2679,9 +2622,8 @@ static struct clk_branch gcc_usb0_phy_cf - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb0_phy_cfg_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2697,9 +2639,8 @@ static struct clk_branch gcc_usb0_pipe_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb0_pipe_clk", -- .parent_names = (const char *[]){ -- "usb0_pipe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb0_pipe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2714,9 +2655,8 @@ static struct clk_branch gcc_usb0_sleep_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb0_sleep_clk", -- .parent_names = (const char *[]){ -- "gcc_sleep_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_sleep_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2731,9 +2671,8 @@ static struct clk_branch gcc_usb1_aux_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb1_aux_clk", -- .parent_names = (const char *[]){ -- "usb1_aux_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb1_aux_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2748,9 +2687,8 @@ static struct clk_branch gcc_sys_noc_usb - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sys_noc_usb1_axi_clk", -- .parent_names = (const char *[]){ -- "usb1_master_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb1_master_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2765,9 +2703,8 @@ static struct clk_branch gcc_usb1_master - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb1_master_clk", -- .parent_names = (const char *[]){ -- "usb1_master_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb1_master_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2782,9 +2719,8 @@ static struct clk_branch gcc_usb1_mock_u - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb1_mock_utmi_clk", -- .parent_names = (const char *[]){ -- "usb1_mock_utmi_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb1_mock_utmi_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2799,9 +2735,8 @@ static struct clk_branch gcc_usb1_phy_cf - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb1_phy_cfg_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2817,9 +2752,8 @@ static struct clk_branch gcc_usb1_pipe_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb1_pipe_clk", -- .parent_names = (const char *[]){ -- "usb1_pipe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &usb1_pipe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2834,9 +2768,8 @@ static struct clk_branch gcc_usb1_sleep_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_usb1_sleep_clk", -- .parent_names = (const char *[]){ -- "gcc_sleep_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_sleep_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2851,9 +2784,8 @@ static struct clk_branch gcc_sdcc1_ahb_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sdcc1_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2868,9 +2800,8 @@ static struct clk_branch gcc_sdcc1_apps_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sdcc1_apps_clk", -- .parent_names = (const char *[]){ -- "sdcc1_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &sdcc1_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2885,9 +2816,8 @@ static struct clk_branch gcc_sdcc1_ice_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sdcc1_ice_core_clk", -- .parent_names = (const char *[]){ -- "sdcc1_ice_core_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &sdcc1_ice_core_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2902,9 +2832,8 @@ static struct clk_branch gcc_sdcc2_ahb_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sdcc2_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2919,9 +2848,8 @@ static struct clk_branch gcc_sdcc2_apps_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_sdcc2_apps_clk", -- .parent_names = (const char *[]){ -- "sdcc2_apps_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &sdcc2_apps_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2936,9 +2864,8 @@ static struct clk_branch gcc_mem_noc_nss - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_mem_noc_nss_axi_clk", -- .parent_names = (const char *[]){ -- "nss_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2953,9 +2880,8 @@ static struct clk_branch gcc_nss_ce_apb_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ce_apb_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2970,9 +2896,8 @@ static struct clk_branch gcc_nss_ce_axi_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ce_axi_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -2987,9 +2912,8 @@ static struct clk_branch gcc_nss_cfg_clk - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_cfg_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3004,9 +2928,8 @@ static struct clk_branch gcc_nss_crypto_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_crypto_clk", -- .parent_names = (const char *[]){ -- "nss_crypto_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_crypto_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3021,9 +2944,8 @@ static struct clk_branch gcc_nss_csr_clk - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_csr_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3038,9 +2960,8 @@ static struct clk_branch gcc_nss_edma_cf - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_edma_cfg_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3055,9 +2976,8 @@ static struct clk_branch gcc_nss_edma_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_edma_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3072,9 +2992,8 @@ static struct clk_branch gcc_nss_imem_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_imem_clk", -- .parent_names = (const char *[]){ -- "nss_imem_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_imem_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3089,9 +3008,8 @@ static struct clk_branch gcc_nss_noc_clk - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_noc_clk", -- .parent_names = (const char *[]){ -- "nss_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3106,9 +3024,8 @@ static struct clk_branch gcc_nss_ppe_btq - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ppe_btq_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3123,9 +3040,8 @@ static struct clk_branch gcc_nss_ppe_cfg - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ppe_cfg_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3140,9 +3056,8 @@ static struct clk_branch gcc_nss_ppe_clk - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ppe_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3157,9 +3072,8 @@ static struct clk_branch gcc_nss_ppe_ipe - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ppe_ipe_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3174,9 +3088,8 @@ static struct clk_branch gcc_nss_ptp_ref - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_ptp_ref_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_cdiv_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_cdiv_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3192,9 +3105,8 @@ static struct clk_branch gcc_crypto_ppe_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_crypto_ppe_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3209,9 +3121,8 @@ static struct clk_branch gcc_nssnoc_ce_a - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_ce_apb_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3226,9 +3137,8 @@ static struct clk_branch gcc_nssnoc_ce_a - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_ce_axi_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3243,9 +3153,8 @@ static struct clk_branch gcc_nssnoc_cryp - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_crypto_clk", -- .parent_names = (const char *[]){ -- "nss_crypto_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_crypto_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3260,9 +3169,8 @@ static struct clk_branch gcc_nssnoc_ppe_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_ppe_cfg_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3277,9 +3185,8 @@ static struct clk_branch gcc_nssnoc_ppe_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_ppe_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3294,9 +3201,8 @@ static struct clk_branch gcc_nssnoc_qosg - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_qosgen_ref_clk", -- .parent_names = (const char *[]){ -- "gcc_xo_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3311,9 +3217,8 @@ static struct clk_branch gcc_nssnoc_snoc - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_snoc_clk", -- .parent_names = (const char *[]){ -- "system_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &system_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3328,9 +3233,8 @@ static struct clk_branch gcc_nssnoc_time - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_timeout_ref_clk", -- .parent_names = (const char *[]){ -- "gcc_xo_div4_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_div4_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3345,9 +3249,8 @@ static struct clk_branch gcc_nssnoc_ubi0 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_ubi0_ahb_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3362,9 +3265,8 @@ static struct clk_branch gcc_nssnoc_ubi1 - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nssnoc_ubi1_ahb_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3380,9 +3282,8 @@ static struct clk_branch gcc_ubi0_ahb_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi0_ahb_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3398,9 +3299,8 @@ static struct clk_branch gcc_ubi0_axi_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi0_axi_clk", -- .parent_names = (const char *[]){ -- "nss_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3416,9 +3316,8 @@ static struct clk_branch gcc_ubi0_nc_axi - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi0_nc_axi_clk", -- .parent_names = (const char *[]){ -- "nss_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3434,9 +3333,8 @@ static struct clk_branch gcc_ubi0_core_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi0_core_clk", -- .parent_names = (const char *[]){ -- "nss_ubi0_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ubi0_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3452,9 +3350,8 @@ static struct clk_branch gcc_ubi0_mpt_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi0_mpt_clk", -- .parent_names = (const char *[]){ -- "ubi_mpt_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &ubi_mpt_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3470,9 +3367,8 @@ static struct clk_branch gcc_ubi1_ahb_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi1_ahb_clk", -- .parent_names = (const char *[]){ -- "nss_ce_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ce_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3488,9 +3384,8 @@ static struct clk_branch gcc_ubi1_axi_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi1_axi_clk", -- .parent_names = (const char *[]){ -- "nss_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3506,9 +3401,8 @@ static struct clk_branch gcc_ubi1_nc_axi - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi1_nc_axi_clk", -- .parent_names = (const char *[]){ -- "nss_noc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_noc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3524,9 +3418,8 @@ static struct clk_branch gcc_ubi1_core_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi1_core_clk", -- .parent_names = (const char *[]){ -- "nss_ubi1_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ubi1_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3542,9 +3435,8 @@ static struct clk_branch gcc_ubi1_mpt_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_ubi1_mpt_clk", -- .parent_names = (const char *[]){ -- "ubi_mpt_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &ubi_mpt_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3559,9 +3451,8 @@ static struct clk_branch gcc_cmn_12gpll_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_cmn_12gpll_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3576,9 +3467,8 @@ static struct clk_branch gcc_cmn_12gpll_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_cmn_12gpll_sys_clk", -- .parent_names = (const char *[]){ -- "gcc_xo_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3593,9 +3483,8 @@ static struct clk_branch gcc_mdio_ahb_cl - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_mdio_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3610,9 +3499,8 @@ static struct clk_branch gcc_uniphy0_ahb - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3627,9 +3515,8 @@ static struct clk_branch gcc_uniphy0_sys - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_sys_clk", -- .parent_names = (const char *[]){ -- "gcc_xo_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3644,9 +3531,8 @@ static struct clk_branch gcc_uniphy1_ahb - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy1_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3661,9 +3547,8 @@ static struct clk_branch gcc_uniphy1_sys - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy1_sys_clk", -- .parent_names = (const char *[]){ -- "gcc_xo_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3678,9 +3563,8 @@ static struct clk_branch gcc_uniphy2_ahb - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy2_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3695,9 +3579,8 @@ static struct clk_branch gcc_uniphy2_sys - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy2_sys_clk", -- .parent_names = (const char *[]){ -- "gcc_xo_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gcc_xo_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3712,9 +3595,8 @@ static struct clk_branch gcc_nss_port1_r - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port1_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port1_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port1_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3729,9 +3611,8 @@ static struct clk_branch gcc_nss_port1_t - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port1_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port1_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port1_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3746,9 +3627,8 @@ static struct clk_branch gcc_nss_port2_r - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port2_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port2_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port2_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3763,9 +3643,8 @@ static struct clk_branch gcc_nss_port2_t - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port2_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port2_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port2_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3780,9 +3659,8 @@ static struct clk_branch gcc_nss_port3_r - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port3_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port3_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port3_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3797,9 +3675,8 @@ static struct clk_branch gcc_nss_port3_t - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port3_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port3_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port3_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3814,9 +3691,8 @@ static struct clk_branch gcc_nss_port4_r - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port4_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port4_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port4_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3831,9 +3707,8 @@ static struct clk_branch gcc_nss_port4_t - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port4_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port4_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port4_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3848,9 +3723,8 @@ static struct clk_branch gcc_nss_port5_r - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port5_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port5_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3865,9 +3739,8 @@ static struct clk_branch gcc_nss_port5_t - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port5_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port5_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3882,9 +3755,8 @@ static struct clk_branch gcc_nss_port6_r - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port6_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port6_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port6_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3899,9 +3771,8 @@ static struct clk_branch gcc_nss_port6_t - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_nss_port6_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port6_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port6_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3916,9 +3787,8 @@ static struct clk_branch gcc_port1_mac_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_port1_mac_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3933,9 +3803,8 @@ static struct clk_branch gcc_port2_mac_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_port2_mac_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3950,9 +3819,8 @@ static struct clk_branch gcc_port3_mac_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_port3_mac_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3967,9 +3835,8 @@ static struct clk_branch gcc_port4_mac_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_port4_mac_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -3984,9 +3851,8 @@ static struct clk_branch gcc_port5_mac_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_port5_mac_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4001,9 +3867,8 @@ static struct clk_branch gcc_port6_mac_c - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_port6_mac_clk", -- .parent_names = (const char *[]){ -- "nss_ppe_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_ppe_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4018,9 +3883,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port1_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port1_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port1_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4035,9 +3899,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port1_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port1_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port1_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4052,9 +3915,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port2_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port2_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port2_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4069,9 +3931,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port2_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port2_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port2_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4086,9 +3947,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port3_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port3_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port3_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4103,9 +3963,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port3_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port3_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port3_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4120,9 +3979,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port4_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port4_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port4_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4137,9 +3995,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port4_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port4_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port4_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4154,9 +4011,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port5_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port5_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4171,9 +4027,8 @@ static struct clk_branch gcc_uniphy0_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy0_port5_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port5_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4188,9 +4043,8 @@ static struct clk_branch gcc_uniphy1_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy1_port5_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port5_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4205,9 +4059,8 @@ static struct clk_branch gcc_uniphy1_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy1_port5_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port5_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port5_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4222,9 +4075,8 @@ static struct clk_branch gcc_uniphy2_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy2_port6_rx_clk", -- .parent_names = (const char *[]){ -- "nss_port6_rx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port6_rx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4239,9 +4091,8 @@ static struct clk_branch gcc_uniphy2_por - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_uniphy2_port6_tx_clk", -- .parent_names = (const char *[]){ -- "nss_port6_tx_div_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &nss_port6_tx_div_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4257,9 +4108,8 @@ static struct clk_branch gcc_crypto_ahb_ - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_crypto_ahb_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4275,9 +4125,8 @@ static struct clk_branch gcc_crypto_axi_ - .enable_mask = BIT(1), - .hw.init = &(struct clk_init_data){ - .name = "gcc_crypto_axi_clk", -- .parent_names = (const char *[]){ -- "pcnoc_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &pcnoc_clk_src.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4293,9 +4142,8 @@ static struct clk_branch gcc_crypto_clk - .enable_mask = BIT(2), - .hw.init = &(struct clk_init_data){ - .name = "gcc_crypto_clk", -- .parent_names = (const char *[]){ -- "crypto_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &crypto_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4310,9 +4158,8 @@ static struct clk_branch gcc_gp1_clk = { - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_gp1_clk", -- .parent_names = (const char *[]){ -- "gp1_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gp1_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4327,9 +4174,8 @@ static struct clk_branch gcc_gp2_clk = { - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_gp2_clk", -- .parent_names = (const char *[]){ -- "gp2_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gp2_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4344,9 +4190,8 @@ static struct clk_branch gcc_gp3_clk = { - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_gp3_clk", -- .parent_names = (const char *[]){ -- "gp3_clk_src" -- }, -+ .parent_hws = (const struct clk_hw *[]){ -+ &gp3_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, -@@ -4368,7 +4213,7 @@ static struct clk_rcg2 pcie0_rchng_clk_s - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcie0_rchng_clk_src", - .parent_data = gcc_xo_gpll0, -- .num_parents = 2, -+ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), - .ops = &clk_rcg2_ops, - }, - }; diff --git a/target/linux/ipq807x/patches-6.1/0008-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch b/target/linux/ipq807x/patches-6.1/0008-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch deleted file mode 100644 index e0725a6612..0000000000 --- a/target/linux/ipq807x/patches-6.1/0008-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 813ba3e427671ba3ff35c825087b03f0ad91cf02 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 7 Nov 2022 14:28:59 +0100 -Subject: [PATCH] clk: qcom: reset: support resetting multiple bits - -This patch adds the support for giving the complete bitmask -in reset structure and reset operation will use this bitmask -for all reset operations. - -Currently, reset structure only takes a single bit for each reset -and then calculates the bitmask by using the BIT() macro. - -However, this is not sufficient anymore for newer SoC-s like IPQ8074, -IPQ6018 and more, since their networking resets require multiple bits -to be asserted in order to properly reset the HW block completely. - -So, in order to allow asserting multiple bits add "bitmask" field to -qcom_reset_map, and then use that bitmask value if its populated in the -driver, if its not populated, then we just default to existing behaviour -and calculate the bitmask on the fly. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221107132901.489240-1-robimarko@gmail.com ---- - drivers/clk/qcom/reset.c | 4 ++-- - drivers/clk/qcom/reset.h | 1 + - 2 files changed, 3 insertions(+), 2 deletions(-) - ---- a/drivers/clk/qcom/reset.c -+++ b/drivers/clk/qcom/reset.c -@@ -30,7 +30,7 @@ qcom_reset_assert(struct reset_controlle - - rst = to_qcom_reset_controller(rcdev); - map = &rst->reset_map[id]; -- mask = BIT(map->bit); -+ mask = map->bitmask ? map->bitmask : BIT(map->bit); - - return regmap_update_bits(rst->regmap, map->reg, mask, mask); - } -@@ -44,7 +44,7 @@ qcom_reset_deassert(struct reset_control - - rst = to_qcom_reset_controller(rcdev); - map = &rst->reset_map[id]; -- mask = BIT(map->bit); -+ mask = map->bitmask ? map->bitmask : BIT(map->bit); - - return regmap_update_bits(rst->regmap, map->reg, mask, 0); - } ---- a/drivers/clk/qcom/reset.h -+++ b/drivers/clk/qcom/reset.h -@@ -12,6 +12,7 @@ struct qcom_reset_map { - unsigned int reg; - u8 bit; - u8 udelay; -+ u32 bitmask; - }; - - struct regmap; diff --git a/target/linux/ipq807x/patches-6.1/0009-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch b/target/linux/ipq807x/patches-6.1/0009-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch deleted file mode 100644 index 75f16a1673..0000000000 --- a/target/linux/ipq807x/patches-6.1/0009-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch +++ /dev/null @@ -1,39 +0,0 @@ -From e78a40eb24187a8b4f9b89e2181f674df39c2013 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 7 Nov 2022 14:29:00 +0100 -Subject: [PATCH] dt-bindings: clock: qcom: ipq8074: add missing networking - resets - -Add bindings for the missing networking resets found in IPQ8074 GCC. - -Signed-off-by: Robert Marko -Acked-by: Krzysztof Kozlowski -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221107132901.489240-2-robimarko@gmail.com ---- - include/dt-bindings/clock/qcom,gcc-ipq8074.h | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - ---- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h -+++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h -@@ -367,6 +367,20 @@ - #define GCC_PCIE1_AHB_ARES 129 - #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 - #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 -+#define GCC_PPE_FULL_RESET 132 -+#define GCC_UNIPHY0_SOFT_RESET 133 -+#define GCC_UNIPHY0_XPCS_RESET 134 -+#define GCC_UNIPHY1_SOFT_RESET 135 -+#define GCC_UNIPHY1_XPCS_RESET 136 -+#define GCC_UNIPHY2_SOFT_RESET 137 -+#define GCC_UNIPHY2_XPCS_RESET 138 -+#define GCC_EDMA_HW_RESET 139 -+#define GCC_NSSPORT1_RESET 140 -+#define GCC_NSSPORT2_RESET 141 -+#define GCC_NSSPORT3_RESET 142 -+#define GCC_NSSPORT4_RESET 143 -+#define GCC_NSSPORT5_RESET 144 -+#define GCC_NSSPORT6_RESET 145 - - #define USB0_GDSC 0 - #define USB1_GDSC 1 diff --git a/target/linux/ipq807x/patches-6.1/0010-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch b/target/linux/ipq807x/patches-6.1/0010-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch deleted file mode 100644 index 212fc84869..0000000000 --- a/target/linux/ipq807x/patches-6.1/0010-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch +++ /dev/null @@ -1,41 +0,0 @@ -From da76cb63d04dc22ed32123b8c1d084c006d67bfb Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 7 Nov 2022 14:29:01 +0100 -Subject: [PATCH] clk: qcom: ipq8074: add missing networking resets - -Downstream QCA 5.4 kernel defines networking resets which are not present -in the mainline kernel but are required for the networking drivers. - -So, port the downstream resets and avoid using magic values for mask, -construct mask for resets which require multiple bits to be set/cleared. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221107132901.489240-3-robimarko@gmail.com ---- - drivers/clk/qcom/gcc-ipq8074.c | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - ---- a/drivers/clk/qcom/gcc-ipq8074.c -+++ b/drivers/clk/qcom/gcc-ipq8074.c -@@ -4671,6 +4671,20 @@ static const struct qcom_reset_map gcc_i - [GCC_PCIE1_AXI_SLAVE_ARES] = { 0x76040, 4 }, - [GCC_PCIE1_AHB_ARES] = { 0x76040, 5 }, - [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, -+ [GCC_PPE_FULL_RESET] = { .reg = 0x68014, .bitmask = GENMASK(19, 16) }, -+ [GCC_UNIPHY0_SOFT_RESET] = { .reg = 0x56004, .bitmask = GENMASK(13, 4) | BIT(1) }, -+ [GCC_UNIPHY0_XPCS_RESET] = { 0x56004, 2 }, -+ [GCC_UNIPHY1_SOFT_RESET] = { .reg = 0x56104, .bitmask = GENMASK(5, 4) | BIT(1) }, -+ [GCC_UNIPHY1_XPCS_RESET] = { 0x56104, 2 }, -+ [GCC_UNIPHY2_SOFT_RESET] = { .reg = 0x56204, .bitmask = GENMASK(5, 4) | BIT(1) }, -+ [GCC_UNIPHY2_XPCS_RESET] = { 0x56204, 2 }, -+ [GCC_EDMA_HW_RESET] = { .reg = 0x68014, .bitmask = GENMASK(21, 20) }, -+ [GCC_NSSPORT1_RESET] = { .reg = 0x68014, .bitmask = BIT(24) | GENMASK(1, 0) }, -+ [GCC_NSSPORT2_RESET] = { .reg = 0x68014, .bitmask = BIT(25) | GENMASK(3, 2) }, -+ [GCC_NSSPORT3_RESET] = { .reg = 0x68014, .bitmask = BIT(26) | GENMASK(5, 4) }, -+ [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) }, - }; - - static struct gdsc *gcc_ipq8074_gdscs[] = { diff --git a/target/linux/ipq807x/patches-6.1/0011-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch b/target/linux/ipq807x/patches-6.1/0011-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch deleted file mode 100644 index 7372b1da8e..0000000000 --- a/target/linux/ipq807x/patches-6.1/0011-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch +++ /dev/null @@ -1,152 +0,0 @@ -From 78936d46470938caa9a7ea529deeb36777b4f98e Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 16 Nov 2022 22:46:55 +0100 -Subject: [PATCH] clk: qcom: ipq8074: populate fw_name for all parents - -It appears that having only .name populated in parent_data for clocks -which are only globally searchable currently will not work as the clk core -won't copy that name if there is no .fw_name present as well. - -So, populate .fw_name for all parent clocks in parent_data. - -Fixes: ae55ad32e273 ("clk: qcom: ipq8074: convert to parent data") - -Co-developed-by: Christian Marangi -Signed-off-by: Christian Marangi -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221116214655.1116467-1-robimarko@gmail.com ---- - drivers/clk/qcom/gcc-ipq8074.c | 52 +++++++++++++++++----------------- - 1 file changed, 26 insertions(+), 26 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq8074.c -+++ b/drivers/clk/qcom/gcc-ipq8074.c -@@ -680,7 +680,7 @@ static struct clk_rcg2 pcie0_aux_clk_src - }; - - static const struct clk_parent_data gcc_pcie20_phy0_pipe_clk_xo[] = { -- { .name = "pcie20_phy0_pipe_clk" }, -+ { .fw_name = "pcie0_pipe", .name = "pcie20_phy0_pipe_clk" }, - { .fw_name = "xo", .name = "xo" }, - }; - -@@ -733,7 +733,7 @@ static struct clk_rcg2 pcie1_aux_clk_src - }; - - static const struct clk_parent_data gcc_pcie20_phy1_pipe_clk_xo[] = { -- { .name = "pcie20_phy1_pipe_clk" }, -+ { .fw_name = "pcie1_pipe", .name = "pcie20_phy1_pipe_clk" }, - { .fw_name = "xo", .name = "xo" }, - }; - -@@ -1137,7 +1137,7 @@ static const struct freq_tbl ftbl_nss_no - - static const struct clk_parent_data gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { - { .fw_name = "xo", .name = "xo" }, -- { .name = "bias_pll_nss_noc_clk" }, -+ { .fw_name = "bias_pll_nss_noc_clk", .name = "bias_pll_nss_noc_clk" }, - { .hw = &gpll0.clkr.hw }, - { .hw = &gpll2.clkr.hw }, - }; -@@ -1362,7 +1362,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", .name = "xo" }, -- { .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 }, -@@ -1413,10 +1413,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", .name = "xo" }, -- { .name = "uniphy0_gcc_rx_clk" }, -- { .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 }, -- { .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[] = { -@@ -1465,10 +1465,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", .name = "xo" }, -- { .name = "uniphy0_gcc_tx_clk" }, -- { .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 }, -- { .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[] = { -@@ -1696,12 +1696,12 @@ static const struct freq_tbl ftbl_nss_po - - static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { - { .fw_name = "xo", .name = "xo" }, -- { .name = "uniphy0_gcc_rx_clk" }, -- { .name = "uniphy0_gcc_tx_clk" }, -- { .name = "uniphy1_gcc_rx_clk" }, -- { .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 }, -- { .name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map -@@ -1758,12 +1758,12 @@ static const struct freq_tbl ftbl_nss_po - - static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { - { .fw_name = "xo", .name = "xo" }, -- { .name = "uniphy0_gcc_tx_clk" }, -- { .name = "uniphy0_gcc_rx_clk" }, -- { .name = "uniphy1_gcc_tx_clk" }, -- { .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 }, -- { .name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map -@@ -1820,10 +1820,10 @@ static const struct freq_tbl ftbl_nss_po - - static const struct clk_parent_data gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { - { .fw_name = "xo", .name = "xo" }, -- { .name = "uniphy2_gcc_rx_clk" }, -- { .name = "uniphy2_gcc_tx_clk" }, -+ { .fw_name = "uniphy2_gcc_rx_clk", .name = "uniphy2_gcc_rx_clk" }, -+ { .fw_name = "uniphy2_gcc_tx_clk", .name = "uniphy2_gcc_tx_clk" }, - { .hw = &ubi32_pll.clkr.hw }, -- { .name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { -@@ -1877,10 +1877,10 @@ static const struct freq_tbl ftbl_nss_po - - static const struct clk_parent_data gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { - { .fw_name = "xo", .name = "xo" }, -- { .name = "uniphy2_gcc_tx_clk" }, -- { .name = "uniphy2_gcc_rx_clk" }, -+ { .fw_name = "uniphy2_gcc_tx_clk", .name = "uniphy2_gcc_tx_clk" }, -+ { .fw_name = "uniphy2_gcc_rx_clk", .name = "uniphy2_gcc_rx_clk" }, - { .hw = &ubi32_pll.clkr.hw }, -- { .name = "bias_pll_cc_clk" }, -+ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, - }; - - static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { diff --git a/target/linux/ipq807x/patches-6.1/0012-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch b/target/linux/ipq807x/patches-6.1/0012-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch deleted file mode 100644 index 1f99de002b..0000000000 --- a/target/linux/ipq807x/patches-6.1/0012-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 9033c3c86ea0dd35bd2ab957317573b755967298 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sun, 30 Oct 2022 18:57:03 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074: pass XO and sleep clocks to GCC - -Pass XO and sleep clocks to the GCC controller so it does not have to -find them by matching globaly by name. - -If not passed directly, driver maintains backwards compatibility by then -falling back to global lookup. - -Since we are here, set cell numbers in decimal instead of hex. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221030175703.1103224-3-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -361,9 +361,11 @@ - gcc: gcc@1800000 { - compatible = "qcom,gcc-ipq8074"; - reg = <0x01800000 0x80000>; -- #clock-cells = <0x1>; -+ clocks = <&xo>, <&sleep_clk>; -+ clock-names = "xo", "sleep_clk"; -+ #clock-cells = <1>; - #power-domain-cells = <1>; -- #reset-cells = <0x1>; -+ #reset-cells = <1>; - }; - - tcsr_mutex: hwlock@1905000 { diff --git a/target/linux/ipq807x/patches-6.1/0013-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch b/target/linux/ipq807x/patches-6.1/0013-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch deleted file mode 100644 index cd146420cf..0000000000 --- a/target/linux/ipq807x/patches-6.1/0013-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch +++ /dev/null @@ -1,149 +0,0 @@ -From fb76b808f8628215afebaf0f8af0bde635302590 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:18:14 +0200 -Subject: [PATCH] arm64: dts: qcom: add PMP8074 DTSI - -PMP8074 is a companion PMIC to the Qualcomm IPQ8074 series that is -controlled via SPMI. - -Add DTSI for it providing GPIO, regulator, RTC and VADC support. - -RTC is disabled by default as there is no built-in battery so it will -loose time unless board vendor added a battery, so make it optional. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818221815.346233-4-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/pmp8074.dtsi | 125 ++++++++++++++++++++++++++ - 1 file changed, 125 insertions(+) - create mode 100644 arch/arm64/boot/dts/qcom/pmp8074.dtsi - ---- /dev/null -+++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi -@@ -0,0 +1,125 @@ -+// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause -+ -+#include -+#include -+ -+&spmi_bus { -+ pmic@0 { -+ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; -+ reg = <0x0 SPMI_USID>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ pmp8074_adc: adc@3100 { -+ compatible = "qcom,spmi-adc-rev2"; -+ reg = <0x3100>; -+ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ #io-channel-cells = <1>; -+ -+ ref-gnd@0 { -+ reg = ; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ vref-1p25@1 { -+ reg = ; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ vref-vadc@2 { -+ reg = ; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ pmic_die: die-temp@6 { -+ reg = ; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ xo_therm: xo-temp@76 { -+ reg = ; -+ qcom,ratiometric; -+ qcom,hw-settle-time = <200>; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ pa_therm1: thermistor1@77 { -+ reg = ; -+ qcom,ratiometric; -+ qcom,hw-settle-time = <200>; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ pa_therm2: thermistor2@78 { -+ reg = ; -+ qcom,ratiometric; -+ qcom,hw-settle-time = <200>; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ pa_therm3: thermistor3@79 { -+ reg = ; -+ qcom,ratiometric; -+ qcom,hw-settle-time = <200>; -+ qcom,pre-scaling = <1 1>; -+ }; -+ -+ vph-pwr@131 { -+ reg = ; -+ qcom,pre-scaling = <1 3>; -+ }; -+ }; -+ -+ pmp8074_rtc: rtc@6000 { -+ compatible = "qcom,pm8941-rtc"; -+ reg = <0x6000>; -+ reg-names = "rtc", "alarm"; -+ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>; -+ allow-set-time; -+ status = "disabled"; -+ }; -+ -+ pmp8074_gpios: gpio@c000 { -+ compatible = "qcom,pmp8074-gpio", "qcom,spmi-gpio"; -+ reg = <0xc000>; -+ gpio-controller; -+ #gpio-cells = <2>; -+ gpio-ranges = <&pmp8074_gpios 0 0 12>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ }; -+ -+ pmic@1 { -+ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; -+ reg = <0x1 SPMI_USID>; -+ -+ regulators { -+ compatible = "qcom,pmp8074-regulators"; -+ -+ s3: s3 { -+ regulator-name = "vdd_s3"; -+ regulator-min-microvolt = <592000>; -+ regulator-max-microvolt = <1064000>; -+ regulator-always-on; -+ regulator-boot-on; -+ }; -+ -+ s4: s4 { -+ regulator-name = "vdd_s4"; -+ regulator-min-microvolt = <712000>; -+ regulator-max-microvolt = <992000>; -+ regulator-always-on; -+ regulator-boot-on; -+ }; -+ -+ l11: l11 { -+ regulator-name = "l11"; -+ regulator-min-microvolt = <1800000>; -+ regulator-max-microvolt = <3300000>; -+ }; -+ }; -+ }; -+}; diff --git a/target/linux/ipq807x/patches-6.1/0014-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch b/target/linux/ipq807x/patches-6.1/0014-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch deleted file mode 100644 index ebd3763a58..0000000000 --- a/target/linux/ipq807x/patches-6.1/0014-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 2c394cfc1779886048feca7dc7f4075da5f6328c Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 19 Aug 2022 00:18:15 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq8074-hk01: add VQMMC supply - -Since now we have control over the PMP8074 PMIC providing various system -voltages including L11 which provides the SDIO/eMMC I/O voltage set it as -the SDHCI VQMMC supply. - -This allows SDHCI controller to switch to 1.8V I/O mode and support high -speed modes like HS200 and HS400. - -Signed-off-by: Robert Marko -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20220818221815.346233-5-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts -+++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts -@@ -3,6 +3,7 @@ - /* Copyright (c) 2017, The Linux Foundation. All rights reserved. - */ - #include "ipq8074.dtsi" -+#include "pmp8074.dtsi" - - / { - model = "Qualcomm Technologies, Inc. IPQ8074-HK01"; -@@ -84,6 +85,7 @@ - - &sdhc_1 { - status = "okay"; -+ vqmmc-supply = <&l11>; - }; - - &qusb_phy_0 { diff --git a/target/linux/ipq807x/patches-6.1/0015-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch b/target/linux/ipq807x/patches-6.1/0015-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch deleted file mode 100644 index e08f6d1f3c..0000000000 --- a/target/linux/ipq807x/patches-6.1/0015-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 82ceb86227b1fc15c76d5fc691b2bf425f1a63b3 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Mon, 7 Nov 2022 10:29:30 +0100 -Subject: [PATCH] arm64: dts: qcom: hk01: use GPIO flags for tlmm - -Use respective GPIO_ACTIVE_LOW/HIGH flags for tlmm GPIOs instead of -harcoding the cell value. - -Signed-off-by: Robert Marko -Reviewed-by: Krzysztof Kozlowski -Reviewed-by: Konrad Dybcio -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221107092930.33325-3-robimarko@gmail.com ---- - arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts -+++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts -@@ -4,6 +4,7 @@ - */ - #include "ipq8074.dtsi" - #include "pmp8074.dtsi" -+#include - - / { - model = "Qualcomm Technologies, Inc. IPQ8074-HK01"; -@@ -52,12 +53,12 @@ - - &pcie0 { - status = "okay"; -- perst-gpios = <&tlmm 61 0x1>; -+ perst-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>; - }; - - &pcie1 { - status = "okay"; -- perst-gpios = <&tlmm 58 0x1>; -+ perst-gpios = <&tlmm 58 GPIO_ACTIVE_LOW>; - }; - - &pcie_qmp0 { diff --git a/target/linux/ipq807x/patches-6.1/0016-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch b/target/linux/ipq807x/patches-6.1/0016-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch deleted file mode 100644 index a8bf2492f4..0000000000 --- a/target/linux/ipq807x/patches-6.1/0016-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 1b1c1423ca3e740984aa883512a72c4ea08fbe28 Mon Sep 17 00:00:00 2001 -From: Konrad Dybcio -Date: Mon, 7 Nov 2022 15:55:17 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074-*: Fix up comments - -Make sure all multiline C-style commends begin with just '/*' with -the comment text starting on a new line. - -Also, fix up some whitespace within comments. - -Signed-off-by: Konrad Dybcio -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221107145522.6706-8-konrad.dybcio@linaro.org ---- - arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 3 ++- - arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts | 3 ++- - arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts | 3 ++- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 12 ++++++------ - 4 files changed, 12 insertions(+), 9 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts -+++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: GPL-2.0-only - /dts-v1/; --/* Copyright (c) 2017, The Linux Foundation. All rights reserved. -+/* -+ * Copyright (c) 2017, The Linux Foundation. All rights reserved. - */ - #include "ipq8074.dtsi" - #include "pmp8074.dtsi" ---- a/arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts -+++ b/arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts -@@ -1,5 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0-only --/* Copyright (c) 2020 The Linux Foundation. All rights reserved. -+/* -+ * Copyright (c) 2020 The Linux Foundation. All rights reserved. - */ - /dts-v1/; - ---- a/arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts -+++ b/arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: GPL-2.0-only - /dts-v1/; --/* Copyright (c) 2020 The Linux Foundation. All rights reserved. -+/* -+ * Copyright (c) 2020 The Linux Foundation. All rights reserved. - */ - #include "ipq8074-hk10.dtsi" - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -129,10 +129,10 @@ - status = "disabled"; - - usb1_ssphy: phy@58200 { -- reg = <0x00058200 0x130>, /* Tx */ -+ reg = <0x00058200 0x130>, /* Tx */ - <0x00058400 0x200>, /* Rx */ -- <0x00058800 0x1f8>, /* PCS */ -- <0x00058600 0x044>; /* PCS misc*/ -+ <0x00058800 0x1f8>, /* PCS */ -+ <0x00058600 0x044>; /* PCS misc */ - #phy-cells = <0>; - #clock-cells = <0>; - clocks = <&gcc GCC_USB1_PIPE_CLK>; -@@ -172,10 +172,10 @@ - status = "disabled"; - - usb0_ssphy: phy@78200 { -- reg = <0x00078200 0x130>, /* Tx */ -+ reg = <0x00078200 0x130>, /* Tx */ - <0x00078400 0x200>, /* Rx */ -- <0x00078800 0x1f8>, /* PCS */ -- <0x00078600 0x044>; /* PCS misc*/ -+ <0x00078800 0x1f8>, /* PCS */ -+ <0x00078600 0x044>; /* PCS misc */ - #phy-cells = <0>; - #clock-cells = <0>; - clocks = <&gcc GCC_USB0_PIPE_CLK>; diff --git a/target/linux/ipq807x/patches-6.1/0017-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch b/target/linux/ipq807x/patches-6.1/0017-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch deleted file mode 100644 index 1ce1140682..0000000000 --- a/target/linux/ipq807x/patches-6.1/0017-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 5f20690f77878b1ba24ec88df01b92d5131a6780 Mon Sep 17 00:00:00 2001 -From: Krzysztof Kozlowski -Date: Tue, 8 Nov 2022 15:23:57 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074: align TLMM pin configuration with - DT schema - -DT schema expects TLMM pin configuration nodes to be named with -'-state' suffix and their optional children with '-pins' suffix. - -Signed-off-by: Krzysztof Kozlowski -Reviewed-by: Konrad Dybcio -Signed-off-by: Bjorn Andersson -Link: https://lore.kernel.org/r/20221108142357.67202-2-krzysztof.kozlowski@linaro.org ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -318,35 +318,35 @@ - interrupt-controller; - #interrupt-cells = <0x2>; - -- serial_4_pins: serial4-pinmux { -+ serial_4_pins: serial4-state { - pins = "gpio23", "gpio24"; - function = "blsp4_uart1"; - drive-strength = <8>; - bias-disable; - }; - -- i2c_0_pins: i2c-0-pinmux { -+ i2c_0_pins: i2c-0-state { - pins = "gpio42", "gpio43"; - function = "blsp1_i2c"; - drive-strength = <8>; - bias-disable; - }; - -- spi_0_pins: spi-0-pins { -+ spi_0_pins: spi-0-state { - pins = "gpio38", "gpio39", "gpio40", "gpio41"; - function = "blsp0_spi"; - drive-strength = <8>; - bias-disable; - }; - -- hsuart_pins: hsuart-pins { -+ hsuart_pins: hsuart-state { - pins = "gpio46", "gpio47", "gpio48", "gpio49"; - function = "blsp2_uart"; - drive-strength = <8>; - bias-disable; - }; - -- qpic_pins: qpic-pins { -+ qpic_pins: qpic-state { - pins = "gpio1", "gpio3", "gpio4", - "gpio5", "gpio6", "gpio7", - "gpio8", "gpio10", "gpio11", diff --git a/target/linux/ipq807x/patches-6.1/0019-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch b/target/linux/ipq807x/patches-6.1/0019-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch deleted file mode 100644 index 8719bf74c4..0000000000 --- a/target/linux/ipq807x/patches-6.1/0019-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch +++ /dev/null @@ -1,24 +0,0 @@ -From a4748d2850783d36f77ccf2b5fcc86ccf1800ef1 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 16 Nov 2022 22:48:36 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074: set Gen2 PCIe pcie max-link-speed - -Add the generic 'max-link-speed' property to describe the Gen2 PCIe link -generation limit. -This allows the generic DWC code to configure the link speed correctly. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -766,6 +766,7 @@ - linux,pci-domain = <1>; - bus-range = <0x00 0xff>; - num-lanes = <1>; -+ max-link-speed = <2>; - #address-cells = <3>; - #size-cells = <2>; - diff --git a/target/linux/ipq807x/patches-6.1/0020-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch b/target/linux/ipq807x/patches-6.1/0020-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch deleted file mode 100644 index 60caee46ca..0000000000 --- a/target/linux/ipq807x/patches-6.1/0020-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch +++ /dev/null @@ -1,26 +0,0 @@ -From f356132229b18ceef5d5ef9103bbaa9bdeb84c8d Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Fri, 13 Jan 2023 17:44:47 +0100 -Subject: [PATCH] PCI: qcom: Add IPQ8074 Gen3 port support - -IPQ8074 has one Gen2 and one Gen3 port, with Gen2 port already supported. -Add compatible for Gen3 port which uses the same controller as IPQ6018. - -Link: https://lore.kernel.org/r/20230113164449.906002-7-robimarko@gmail.com -Signed-off-by: Robert Marko -Signed-off-by: Lorenzo Pieralisi -Signed-off-by: Bjorn Helgaas ---- - 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 -@@ -1745,6 +1745,7 @@ static const struct of_device_id qcom_pc - { .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 }, - { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 }, - { .compatible = "qcom,pcie-ipq8074", .data = &cfg_2_3_3 }, -+ { .compatible = "qcom,pcie-ipq8074-gen3", .data = &cfg_2_9_0 }, - { .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 }, - { .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 }, - { .compatible = "qcom,pcie-sa8540p", .data = &cfg_1_9_0 }, diff --git a/target/linux/ipq807x/patches-6.1/0021-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch b/target/linux/ipq807x/patches-6.1/0021-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch deleted file mode 100644 index eb772be4ce..0000000000 --- a/target/linux/ipq807x/patches-6.1/0021-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 614d31c231c7707322b643f409eeb7e28adc7f8c Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sun, 8 Jan 2023 13:36:28 +0100 -Subject: [PATCH] clk: qcom: ipq8074: populate fw_name for usb3phy-s - -Having only .name populated in parent_data for clocks which are only -globally searchable currently will not work as the clk core won't copy -that name if there is no .fw_name present as well. - -So, populate .fw_name for usb3phy clocks in parent_data as they were -missed by me in ("clk: qcom: ipq8074: populate fw_name for all parents"). - -Fixes: ae55ad32e273 ("clk: qcom: ipq8074: convert to parent data") -Signed-off-by: Robert Marko ---- - drivers/clk/qcom/gcc-ipq8074.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq8074.c -+++ b/drivers/clk/qcom/gcc-ipq8074.c -@@ -934,7 +934,7 @@ static struct clk_rcg2 usb0_mock_utmi_cl - }; - - static const struct clk_parent_data gcc_usb3phy_0_cc_pipe_clk_xo[] = { -- { .name = "usb3phy_0_cc_pipe_clk" }, -+ { .fw_name = "usb3phy_0_cc_pipe_clk", .name = "usb3phy_0_cc_pipe_clk" }, - { .fw_name = "xo", .name = "xo" }, - }; - -@@ -1002,7 +1002,7 @@ static struct clk_rcg2 usb1_mock_utmi_cl - }; - - static const struct clk_parent_data gcc_usb3phy_1_cc_pipe_clk_xo[] = { -- { .name = "usb3phy_1_cc_pipe_clk" }, -+ { .fw_name = "usb3phy_1_cc_pipe_clk", .name = "usb3phy_1_cc_pipe_clk" }, - { .fw_name = "xo", .name = "xo" }, - }; - diff --git a/target/linux/ipq807x/patches-6.1/0022-v6.5-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch b/target/linux/ipq807x/patches-6.1/0022-v6.5-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch deleted file mode 100644 index 72b38270c4..0000000000 --- a/target/linux/ipq807x/patches-6.1/0022-v6.5-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch +++ /dev/null @@ -1,77 +0,0 @@ -From b8295c6eb276b60f7b75c53a9703ca8fee01eba2 Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Fri, 26 May 2023 13:09:17 +0200 -Subject: [PATCH] soc: qcom: mdt_loader: Fix unconditional call to - scm_pas_mem_setup - -Commit ebeb20a9cd3f ("soc: qcom: mdt_loader: Always invoke PAS -mem_setup") dropped the relocate check and made pas_mem_setup run -unconditionally. The code was later moved with commit f4e526ff7e38 -("soc: qcom: mdt_loader: Extract PAS operations") to -qcom_mdt_pas_init() effectively losing track of what was actually -done. - -The assumption that PAS mem_setup can be done anytime was effectively -wrong, with no good reason and this caused regression on some SoC -that use remoteproc to bringup ath11k. One example is IPQ8074 SoC that -effectively broke resulting in remoteproc silently die and ath11k not -working. - -On this SoC FW relocate is not enabled and PAS mem_setup was correctly -skipped in previous kernel version resulting in correct bringup and -function of remoteproc and ath11k. - -To fix the regression, reintroduce the relocate check in -qcom_mdt_pas_init() and correctly skip PAS mem_setup where relocate is -not enabled. - -Fixes: ebeb20a9cd3f ("soc: qcom: mdt_loader: Always invoke PAS mem_setup") -Reported-by: Robert Marko -Tested-by: Robert Marko -Co-developed-by: Robert Marko -Signed-off-by: Robert Marko -Signed-off-by: Christian Marangi -Cc: stable@vger.kernel.org ---- - drivers/soc/qcom/mdt_loader.c | 16 +++++++++++----- - 1 file changed, 11 insertions(+), 5 deletions(-) - ---- a/drivers/soc/qcom/mdt_loader.c -+++ b/drivers/soc/qcom/mdt_loader.c -@@ -210,6 +210,7 @@ int qcom_mdt_pas_init(struct device *dev - const struct elf32_hdr *ehdr; - phys_addr_t min_addr = PHYS_ADDR_MAX; - phys_addr_t max_addr = 0; -+ bool relocate = false; - size_t metadata_len; - void *metadata; - int ret; -@@ -224,6 +225,9 @@ int qcom_mdt_pas_init(struct device *dev - if (!mdt_phdr_valid(phdr)) - continue; - -+ if (phdr->p_flags & QCOM_MDT_RELOCATABLE) -+ relocate = true; -+ - if (phdr->p_paddr < min_addr) - min_addr = phdr->p_paddr; - -@@ -246,11 +250,13 @@ int qcom_mdt_pas_init(struct device *dev - goto out; - } - -- ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); -- if (ret) { -- /* Unable to set up relocation */ -- dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); -- goto out; -+ if (relocate) { -+ ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); -+ if (ret) { -+ /* Unable to set up relocation */ -+ dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); -+ goto out; -+ } - } - - out: diff --git a/target/linux/ipq807x/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch b/target/linux/ipq807x/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch deleted file mode 100644 index 5713775948..0000000000 --- a/target/linux/ipq807x/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch +++ /dev/null @@ -1,203 +0,0 @@ -From 032be4f49dda786fea9e1501212f6cd09a7ded96 Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Thu, 3 Nov 2022 14:49:43 +0100 -Subject: [PATCH] clk: qcom: clk-rcg2: introduce support for multiple conf for - same freq - -Some RCG frequency can be reached by multiple configuration. - -We currently declare multiple configuration for the same frequency but -that is not supported and always the first configuration will be taken. - -These multiple configuration are needed as based on the current parent -configuration, it may be needed to use a different configuration to -reach the same frequency. - -To handle this introduce 2 new macro, FM and C. - -- FM is used to declare an empty freq_tbl with just the frequency and an - array of confs to insert all the config for the provided frequency. - -- C is used to declare a fre_conf where src, pre_div, m and n are - provided. - -The driver is changed to handle this special freq_tbl and select the -correct config by calculating the final rate and deciding based on the -one that is less different than the requested one. - -Tested-by: Robert Marko -Signed-off-by: Christian Marangi ---- - drivers/clk/qcom/clk-rcg.h | 14 ++++++- - drivers/clk/qcom/clk-rcg2.c | 84 +++++++++++++++++++++++++++++++++---- - 2 files changed, 88 insertions(+), 10 deletions(-) - ---- a/drivers/clk/qcom/clk-rcg.h -+++ b/drivers/clk/qcom/clk-rcg.h -@@ -7,7 +7,17 @@ - #include - #include "clk-regmap.h" - --#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } -+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n), 0, NULL } -+ -+#define FM(_f, _confs) { .freq = (_f), .confs_num = ARRAY_SIZE(_confs), .confs = (_confs) } -+#define C(s, h, m, n) { (s), (2 * (h) - 1), (m), (n) } -+ -+struct freq_conf { -+ u8 src; -+ u8 pre_div; -+ u16 m; -+ u16 n; -+}; - - struct freq_tbl { - unsigned long freq; -@@ -15,6 +25,8 @@ struct freq_tbl { - u8 pre_div; - u16 m; - u16 n; -+ int confs_num; -+ const struct freq_conf *confs; - }; - - /** ---- a/drivers/clk/qcom/clk-rcg2.c -+++ b/drivers/clk/qcom/clk-rcg2.c -@@ -209,11 +209,60 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, - return __clk_rcg2_recalc_rate(hw, parent_rate, cfg); - } - -+static void -+clk_rcg2_select_conf(struct clk_hw *hw, struct freq_tbl *f_tbl, -+ const struct freq_tbl *f, unsigned long req_rate) -+{ -+ unsigned long best_rate = 0, parent_rate, rate; -+ const struct freq_conf *conf, *best_conf; -+ struct clk_rcg2 *rcg = to_clk_rcg2(hw); -+ struct clk_hw *p; -+ int index, i; -+ -+ /* Search in each provided config the one that is near the wanted rate */ -+ for (i = 0, conf = f->confs; i < f->confs_num; i++, conf++) { -+ index = qcom_find_src_index(hw, rcg->parent_map, conf->src); -+ if (index < 0) -+ continue; -+ -+ p = clk_hw_get_parent_by_index(hw, index); -+ if (!p) -+ continue; -+ -+ parent_rate = clk_hw_get_rate(p); -+ rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div); -+ -+ if (rate == req_rate) { -+ best_conf = conf; -+ break; -+ } -+ -+ if (abs(req_rate - rate) < abs(best_rate - rate)) { -+ best_rate = rate; -+ best_conf = conf; -+ } -+ } -+ -+ /* -+ * Very unlikely. -+ * Force the first conf if we can't find a correct config. -+ */ -+ if (unlikely(i == f->confs_num)) -+ best_conf = f->confs; -+ -+ /* Apply the config */ -+ f_tbl->src = best_conf->src; -+ f_tbl->pre_div = best_conf->pre_div; -+ f_tbl->m = best_conf->m; -+ f_tbl->n = best_conf->n; -+} -+ - static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, - struct clk_rate_request *req, - enum freq_policy policy) - { - unsigned long clk_flags, rate = req->rate; -+ struct freq_tbl f_tbl; - struct clk_hw *p; - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - int index; -@@ -232,7 +281,15 @@ static int _freq_tbl_determine_rate(stru - if (!f) - return -EINVAL; - -- index = qcom_find_src_index(hw, rcg->parent_map, f->src); -+ f_tbl = *f; -+ /* -+ * A single freq may be reached by multiple configuration. -+ * Try to find the bast one if we have this kind of freq_table. -+ */ -+ if (f->confs) -+ clk_rcg2_select_conf(hw, &f_tbl, f, rate); -+ -+ index = qcom_find_src_index(hw, rcg->parent_map, f_tbl.src); - if (index < 0) - return index; - -@@ -242,18 +299,18 @@ static int _freq_tbl_determine_rate(stru - return -EINVAL; - - if (clk_flags & CLK_SET_RATE_PARENT) { -- rate = f->freq; -- if (f->pre_div) { -+ rate = f_tbl.freq; -+ if (f_tbl.pre_div) { - if (!rate) - rate = req->rate; - rate /= 2; -- rate *= f->pre_div + 1; -+ rate *= f_tbl.pre_div + 1; - } - -- if (f->n) { -+ if (f_tbl.n) { - u64 tmp = rate; -- tmp = tmp * f->n; -- do_div(tmp, f->m); -+ tmp = tmp * f_tbl.n; -+ do_div(tmp, f_tbl.m); - rate = tmp; - } - } else { -@@ -261,7 +318,7 @@ static int _freq_tbl_determine_rate(stru - } - req->best_parent_hw = p; - req->best_parent_rate = rate; -- req->rate = f->freq; -+ req->rate = f_tbl.freq; - - return 0; - } -@@ -357,6 +414,7 @@ static int __clk_rcg2_set_rate(struct cl - { - struct clk_rcg2 *rcg = to_clk_rcg2(hw); - const struct freq_tbl *f; -+ struct freq_tbl f_tbl; - - switch (policy) { - case FLOOR: -@@ -372,7 +430,15 @@ static int __clk_rcg2_set_rate(struct cl - if (!f) - return -EINVAL; - -- return clk_rcg2_configure(rcg, f); -+ f_tbl = *f; -+ /* -+ * A single freq may be reached by multiple configuration. -+ * Try to find the best one if we have this kind of freq_table. -+ */ -+ if (f->confs) -+ clk_rcg2_select_conf(hw, &f_tbl, f, rate); -+ -+ return clk_rcg2_configure(rcg, &f_tbl); - } - - static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/target/linux/ipq807x/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch b/target/linux/ipq807x/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch deleted file mode 100644 index 32fb2d9d87..0000000000 --- a/target/linux/ipq807x/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch +++ /dev/null @@ -1,129 +0,0 @@ -From f778553f296792f4d1e8b3552603ad6116ea3eb3 Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Thu, 3 Nov 2022 14:49:44 +0100 -Subject: [PATCH] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple - conf - -Rework nss_port5/6 to use the new multiple configuration implementation -and correctly fix the clocks for these port under some corner case. - -This is particularly relevant for device that have 2.5G or 10G port -connected to port5 or port 6 on ipq8074. As the parent are shared -across multiple port it may be required to select the correct -configuration to accomplish the desired clock. Without this patch such -port doesn't work in some specific ethernet speed as the clock will be -set to the wrong frequency as we just select the first configuration for -the related frequency instead of selecting the best one. - -Tested-by: Robert Marko # ipq8074 Qnap QHora-301W -Signed-off-by: Christian Marangi ---- - drivers/clk/qcom/gcc-ipq8074.c | 64 +++++++++++++++++++++++++--------- - 1 file changed, 48 insertions(+), 16 deletions(-) - ---- a/drivers/clk/qcom/gcc-ipq8074.c -+++ b/drivers/clk/qcom/gcc-ipq8074.c -@@ -1682,13 +1682,21 @@ static struct clk_regmap_div nss_port4_t - }, - }; - -+static const struct freq_conf ftbl_nss_port5_rx_clk_src_25[] = { -+ C(P_UNIPHY1_RX, 12.5, 0, 0), -+ C(P_UNIPHY0_RX, 5, 0, 0), -+}; -+ -+static const struct freq_conf ftbl_nss_port5_rx_clk_src_125[] = { -+ C(P_UNIPHY1_RX, 2.5, 0, 0), -+ C(P_UNIPHY0_RX, 1, 0, 0), -+}; -+ - static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { - F(19200000, P_XO, 1, 0, 0), -- F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), -- F(25000000, P_UNIPHY0_RX, 5, 0, 0), -+ FM(25000000, ftbl_nss_port5_rx_clk_src_25), - F(78125000, P_UNIPHY1_RX, 4, 0, 0), -- F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), -- F(125000000, P_UNIPHY0_RX, 1, 0, 0), -+ FM(125000000, ftbl_nss_port5_rx_clk_src_125), - F(156250000, P_UNIPHY1_RX, 2, 0, 0), - F(312500000, P_UNIPHY1_RX, 1, 0, 0), - { } -@@ -1744,13 +1752,21 @@ static struct clk_regmap_div nss_port5_r - }, - }; - -+static struct freq_conf ftbl_nss_port5_tx_clk_src_25[] = { -+ C(P_UNIPHY1_TX, 12.5, 0, 0), -+ C(P_UNIPHY0_TX, 5, 0, 0), -+}; -+ -+static struct freq_conf ftbl_nss_port5_tx_clk_src_125[] = { -+ C(P_UNIPHY1_TX, 2.5, 0, 0), -+ C(P_UNIPHY0_TX, 1, 0, 0), -+}; -+ - static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { - F(19200000, P_XO, 1, 0, 0), -- F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), -- F(25000000, P_UNIPHY0_TX, 5, 0, 0), -+ FM(25000000, ftbl_nss_port5_tx_clk_src_25), - F(78125000, P_UNIPHY1_TX, 4, 0, 0), -- F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), -- F(125000000, P_UNIPHY0_TX, 1, 0, 0), -+ FM(125000000, ftbl_nss_port5_tx_clk_src_125), - F(156250000, P_UNIPHY1_TX, 2, 0, 0), - F(312500000, P_UNIPHY1_TX, 1, 0, 0), - { } -@@ -1806,13 +1822,21 @@ static struct clk_regmap_div nss_port5_t - }, - }; - -+static struct freq_conf ftbl_nss_port6_rx_clk_src_25[] = { -+ C(P_UNIPHY2_RX, 5, 0, 0), -+ C(P_UNIPHY2_RX, 12.5, 0, 0), -+}; -+ -+static struct freq_conf ftbl_nss_port6_rx_clk_src_125[] = { -+ C(P_UNIPHY2_RX, 1, 0, 0), -+ C(P_UNIPHY2_RX, 2.5, 0, 0), -+}; -+ - static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = { - F(19200000, P_XO, 1, 0, 0), -- F(25000000, P_UNIPHY2_RX, 5, 0, 0), -- F(25000000, P_UNIPHY2_RX, 12.5, 0, 0), -+ FM(25000000, ftbl_nss_port6_rx_clk_src_25), - F(78125000, P_UNIPHY2_RX, 4, 0, 0), -- F(125000000, P_UNIPHY2_RX, 1, 0, 0), -- F(125000000, P_UNIPHY2_RX, 2.5, 0, 0), -+ FM(125000000, ftbl_nss_port6_rx_clk_src_125), - F(156250000, P_UNIPHY2_RX, 2, 0, 0), - F(312500000, P_UNIPHY2_RX, 1, 0, 0), - { } -@@ -1863,13 +1887,21 @@ static struct clk_regmap_div nss_port6_r - }, - }; - -+static struct freq_conf ftbl_nss_port6_tx_clk_src_25[] = { -+ C(P_UNIPHY2_TX, 5, 0, 0), -+ C(P_UNIPHY2_TX, 12.5, 0, 0), -+}; -+ -+static struct freq_conf ftbl_nss_port6_tx_clk_src_125[] = { -+ C(P_UNIPHY2_TX, 1, 0, 0), -+ C(P_UNIPHY2_TX, 2.5, 0, 0), -+}; -+ - static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = { - F(19200000, P_XO, 1, 0, 0), -- F(25000000, P_UNIPHY2_TX, 5, 0, 0), -- F(25000000, P_UNIPHY2_TX, 12.5, 0, 0), -+ FM(25000000, ftbl_nss_port6_tx_clk_src_25), - F(78125000, P_UNIPHY2_TX, 4, 0, 0), -- F(125000000, P_UNIPHY2_TX, 1, 0, 0), -- F(125000000, P_UNIPHY2_TX, 2.5, 0, 0), -+ FM(125000000, ftbl_nss_port6_tx_clk_src_125), - F(156250000, P_UNIPHY2_TX, 2, 0, 0), - F(312500000, P_UNIPHY2_TX, 1, 0, 0), - { } diff --git a/target/linux/ipq807x/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch b/target/linux/ipq807x/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch deleted file mode 100644 index af53c077ff..0000000000 --- a/target/linux/ipq807x/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch +++ /dev/null @@ -1,70 +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 -@@ -85,6 +85,26 @@ - #size-cells = <2>; - ranges; - -+ nss@40000000 { -+ no-map; -+ reg = <0x0 0x40000000 0x0 0x01000000>; -+ }; -+ -+ tzapp_region: tzapp@4a400000 { -+ no-map; -+ reg = <0x0 0x4a400000 0x0 0x00200000>; -+ }; -+ -+ uboot@4a600000 { -+ no-map; -+ reg = <0x0 0x4a600000 0x0 0x00400000>; -+ }; -+ -+ sbl@4aa00000 { -+ no-map; -+ reg = <0x0 0x4aa00000 0x0 0x00100000>; -+ }; -+ - smem@4ab00000 { - compatible = "qcom,smem"; - reg = <0x0 0x4ab00000 0x0 0x00100000>; -@@ -97,6 +117,21 @@ - no-map; - reg = <0x0 0x4ac00000 0x0 0x00400000>; - }; -+ -+ 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/ipq807x/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch b/target/linux/ipq807x/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch deleted file mode 100644 index 69d4126f76..0000000000 --- a/target/linux/ipq807x/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 8a576b5bc9f0555d1d970cacabcaa24a3b74fa57 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 16 Nov 2022 22:15:01 +0100 -Subject: [PATCH] arm64: dts: qcom: ipq8074: pass QMP PCI PHY PIPE clocks to - GCC - -Pass QMP PCI PHY PIPE clocks to the GCC controller so it does not have to -find them by matching globaly by name. - -If not passed directly, driver maintains backwards compatibility by then -falling back to global lookup. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -396,8 +396,8 @@ - gcc: gcc@1800000 { - compatible = "qcom,gcc-ipq8074"; - reg = <0x01800000 0x80000>; -- clocks = <&xo>, <&sleep_clk>; -- clock-names = "xo", "sleep_clk"; -+ clocks = <&xo>, <&sleep_clk>, <&pcie_phy0>, <&pcie_phy1>; -+ clock-names = "xo", "sleep_clk", "pcie0_pipe", "pcie1_pipe"; - #clock-cells = <1>; - #power-domain-cells = <1>; - #reset-cells = <1>; diff --git a/target/linux/ipq807x/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch b/target/linux/ipq807x/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch deleted file mode 100644 index 9fefd8852a..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 -@@ -699,7 +699,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>; -@@ -811,8 +811,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 142 -@@ -873,8 +872,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 75 diff --git a/target/linux/ipq807x/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch b/target/linux/ipq807x/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch deleted file mode 100644 index 0a984948b5..0000000000 --- a/target/linux/ipq807x/patches-6.1/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; - -@@ -151,6 +139,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; -@@ -240,6 +243,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 */ -@@ -733,6 +742,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) - goto free_rproc; - -- if (wcss->version == WCSS_QCS404) { -- ret = q6v5_wcss_init_clock(wcss); -+ if (desc->init_clock) { -+ ret = desc->init_clock(wcss); - if (ret) - goto free_rproc; -+ } - -- ret = q6v5_wcss_init_regulator(wcss); -+ if (desc->init_regulator) { -+ ret = desc->init_regulator(wcss); - if (ret) - goto free_rproc; - } -@@ -1087,6 +1113,7 @@ static int q6v5_wcss_remove(struct platf - } - - 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, -@@ -1096,6 +1123,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/ipq807x/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch b/target/linux/ipq807x/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch deleted file mode 100644 index 0328efc041..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 3 -+ -+#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_ssr ssr_subdev; -@@ -152,6 +157,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) -@@ -251,6 +257,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) { -@@ -285,6 +300,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"); -@@ -718,6 +734,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); -@@ -742,6 +767,7 @@ static int q6v5_wcss_stop(struct rproc * - return ret; - } - -+pas_done: - clk_disable_unprepare(wcss->prng_clk); - qcom_q6v5_unprepare(&wcss->q6v5); - -@@ -765,9 +791,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 = 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) -@@ -1120,6 +1156,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/ipq807x/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch b/target/linux/ipq807x/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch deleted file mode 100644 index e5c9506c89..0000000000 --- a/target/linux/ipq807x/patches-6.1/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_ssr ssr_subdev; -@@ -147,7 +148,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; -@@ -789,8 +791,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 = 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) -@@ -1150,7 +1174,8 @@ static int q6v5_wcss_remove(struct platf - - 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, -@@ -1163,7 +1188,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/ipq807x/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch b/target/linux/ipq807x/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch deleted file mode 100644 index be63d46e8e..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 -@@ -1179,6 +1179,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/ipq807x/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch b/target/linux/ipq807x/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch deleted file mode 100644 index f0b717210f..0000000000 --- a/target/linux/ipq807x/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch +++ /dev/null @@ -1,79 +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 -@@ -86,7 +86,7 @@ - #define TCSR_WCSS_CLK_MASK 0x1F - #define TCSR_WCSS_CLK_ENABLE 0x14 - --#define MAX_HALT_REG 3 -+#define MAX_HALT_REG 4 - - #define WCNSS_PAS_ID 6 - -@@ -154,6 +154,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; -@@ -875,10 +876,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; -@@ -929,9 +933,9 @@ static int q6v5_wcss_init_mmio(struct q6 - return -EINVAL; - } - -- wcss->halt_q6 = halt_reg[0]; -- wcss->halt_wcss = halt_reg[1]; -- wcss->halt_nc = halt_reg[2]; -+ wcss->halt_q6 = halt_reg[1]; -+ wcss->halt_wcss = halt_reg[2]; -+ wcss->halt_nc = halt_reg[3]; - - return 0; - } -@@ -1179,6 +1183,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, -@@ -1193,6 +1198,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/ipq807x/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch b/target/linux/ipq807x/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch deleted file mode 100644 index fe0e0f9e0b..0000000000 --- a/target/linux/ipq807x/patches-6.1/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/ipq807x/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch b/target/linux/ipq807x/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch deleted file mode 100644 index 791531775e..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 -@@ -4717,6 +4717,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/ipq807x/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch b/target/linux/ipq807x/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch deleted file mode 100644 index 8674522a11..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 -@@ -161,6 +161,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) -@@ -1150,6 +1151,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) - goto free_rproc; -@@ -1188,6 +1190,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 = { -@@ -1204,6 +1207,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/ipq807x/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch b/target/linux/ipq807x/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch deleted file mode 100644 index e37ba37e96..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 -@@ -140,6 +140,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 { - #address-cells = <0x1>; - #size-cells = <0x1>; -@@ -409,6 +435,11 @@ - #hwlock-cells = <1>; - }; - -+ tcsr_q6: syscon@1945000 { -+ compatible = "syscon"; -+ reg = <0x01945000 0xe000>; -+ }; -+ - spmi_bus: spmi@200f000 { - compatible = "qcom,spmi-pmic-arb"; - reg = <0x0200f000 0x001000>, -@@ -913,6 +944,56 @@ - "axi_s_sticky"; - status = "disabled"; - }; -+ -+ 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/ipq807x/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch b/target/linux/ipq807x/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch deleted file mode 100644 index bd5410c934..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 -@@ -994,6 +994,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/ipq807x/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch b/target/linux/ipq807x/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch deleted file mode 100644 index a3c5f344ab..0000000000 --- a/target/linux/ipq807x/patches-6.1/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/ipq807x/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch b/target/linux/ipq807x/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch deleted file mode 100644 index 3520b38134..0000000000 --- a/target/linux/ipq807x/patches-6.1/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/ipq807x/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch b/target/linux/ipq807x/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch deleted file mode 100644 index 09abbfa2e5..0000000000 --- a/target/linux/ipq807x/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 11592aa862e67f4477dee7e94d5c8244d893de1b Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sat, 31 Dec 2022 13:03:41 +0100 -Subject: [PATCH] cpufreq: qcom-nvmem: add support for IPQ8074 - -IPQ8074 comes in 2 families: -* IPQ8070A/IPQ8071A (Acorn) up to 1.4GHz -* IPQ8072A/IPQ8074A/IPQ8076A/IPQ8078A (Hawkeye) up to 2.2GHz - -So, in order to be able to share one OPP table lets add support for IPQ8074 -family based of SMEM SoC ID-s as speedbin fuse is always 0 on IPQ8074. - -IPQ8074 compatible is blacklisted from DT platdev as the cpufreq device -will get created by NVMEM CPUFreq driver. - -Signed-off-by: Robert Marko ---- -Changes in v2: -* Print an error if SMEM ID is not part of the IPQ8074 family -and restrict the speed to Acorn variant (1.4GHz) ---- - drivers/cpufreq/cpufreq-dt-platdev.c | 1 + - drivers/cpufreq/qcom-cpufreq-nvmem.c | 43 ++++++++++++++++++++++++++++ - 2 files changed, 44 insertions(+) - ---- a/drivers/cpufreq/cpufreq-dt-platdev.c -+++ b/drivers/cpufreq/cpufreq-dt-platdev.c -@@ -164,6 +164,7 @@ static const struct of_device_id blockli - { .compatible = "ti,omap3", }, - - { .compatible = "qcom,ipq8064", }, -+ { .compatible = "qcom,ipq8074", }, - { .compatible = "qcom,apq8064", }, - { .compatible = "qcom,msm8974", }, - { .compatible = "qcom,msm8960", }, ---- a/drivers/cpufreq/qcom-cpufreq-nvmem.c -+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c -@@ -31,6 +31,9 @@ - - #include - -+#define IPQ8074_HAWKEYE_VERSION BIT(0) -+#define IPQ8074_ACORN_VERSION BIT(1) -+ - struct qcom_cpufreq_drv; - - struct qcom_cpufreq_match_data { -@@ -204,6 +207,41 @@ len_error: - return ret; - } - -+static int qcom_cpufreq_ipq8074_name_version(struct device *cpu_dev, -+ struct nvmem_cell *speedbin_nvmem, -+ char **pvs_name, -+ struct qcom_cpufreq_drv *drv) -+{ -+ u32 msm_id; -+ int ret; -+ *pvs_name = NULL; -+ -+ ret = qcom_smem_get_soc_id(&msm_id); -+ if (ret) -+ return ret; -+ -+ switch (msm_id) { -+ case QCOM_ID_IPQ8070A: -+ case QCOM_ID_IPQ8071A: -+ drv->versions = IPQ8074_ACORN_VERSION; -+ break; -+ case QCOM_ID_IPQ8072A: -+ case QCOM_ID_IPQ8074A: -+ case QCOM_ID_IPQ8076A: -+ case QCOM_ID_IPQ8078A: -+ drv->versions = IPQ8074_HAWKEYE_VERSION; -+ break; -+ default: -+ dev_err(cpu_dev, -+ "SoC ID %u is not part of IPQ8074 family, limiting to 1.4GHz!\n", -+ msm_id); -+ drv->versions = IPQ8074_ACORN_VERSION; -+ break; -+ } -+ -+ return 0; -+} -+ - static const struct qcom_cpufreq_match_data match_data_kryo = { - .get_version = qcom_cpufreq_kryo_name_version, - }; -@@ -218,6 +256,10 @@ static const struct qcom_cpufreq_match_d - .genpd_names = qcs404_genpd_names, - }; - -+static const struct qcom_cpufreq_match_data match_data_ipq8074 = { -+ .get_version = qcom_cpufreq_ipq8074_name_version, -+}; -+ - static int qcom_cpufreq_probe(struct platform_device *pdev) - { - struct qcom_cpufreq_drv *drv; -@@ -363,6 +405,7 @@ static const struct of_device_id qcom_cp - { .compatible = "qcom,msm8996", .data = &match_data_kryo }, - { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, - { .compatible = "qcom,ipq8064", .data = &match_data_krait }, -+ { .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 }, - { .compatible = "qcom,apq8064", .data = &match_data_krait }, - { .compatible = "qcom,msm8974", .data = &match_data_krait }, - { .compatible = "qcom,msm8960", .data = &match_data_krait }, diff --git a/target/linux/ipq807x/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch b/target/linux/ipq807x/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch deleted file mode 100644 index 3a6f4e9c87..0000000000 --- a/target/linux/ipq807x/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch +++ /dev/null @@ -1,128 +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 -@@ -340,6 +340,113 @@ - status = "disabled"; - }; - -+ qfprom: efuse@a4000 { -+ compatible = "qcom,ipq8074-qfprom", "qcom,qfprom"; -+ reg = <0x000a4000 0x1000>; -+ #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 { - compatible = "qcom,prng-ee"; - reg = <0x000e3000 0x1000>; diff --git a/target/linux/ipq807x/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch b/target/linux/ipq807x/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch deleted file mode 100644 index 9c1e7b9d29..0000000000 --- a/target/linux/ipq807x/patches-6.1/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 { -@@ -83,6 +87,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/ipq807x/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch b/target/linux/ipq807x/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch deleted file mode 100644 index 8c2e59eeb4..0000000000 --- a/target/linux/ipq807x/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch +++ /dev/null @@ -1,36 +0,0 @@ -From f5aaf6669bd4f1f0218dd7fd5dc90941267ad860 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Tue, 30 May 2023 23:26:30 +0200 -Subject: [PATCH] mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M - -It seems that Micron MTFC4GACAJCN-1M despite advertising TRIM support does -not work when the core is trying to use REQ_OP_WRITE_ZEROES. - -We are seeing the following errors in OpenWrt under 6.1 on Qnap Qhora 301W -that we did not previously have and tracked it down to REQ_OP_WRITE_ZEROES: -[ 18.085950] I/O error, dev loop0, sector 596 op 0x9:(WRITE_ZEROES) flags 0x800 phys_seg 0 prio class 2 - -Disabling TRIM makes the error go away, so lets add a quirk for this eMMC -to disable TRIM. - -Signed-off-by: Robert Marko ---- - drivers/mmc/core/quirks.h | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/drivers/mmc/core/quirks.h -+++ b/drivers/mmc/core/quirks.h -@@ -101,6 +101,13 @@ static const struct mmc_fixup __maybe_un - MMC_QUIRK_TRIM_BROKEN), - - /* -+ * Micron MTFC4GACAJCN-1M advertises TRIM but it does not seems to -+ * support being used to offload WRITE_ZEROES. -+ */ -+ MMC_FIXUP("Q2J54A", CID_MANFID_MICRON, 0x014e, add_quirk_mmc, -+ MMC_QUIRK_TRIM_BROKEN), -+ -+ /* - * Some SD cards reports discard support while they don't - */ - MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, diff --git a/target/linux/ipq807x/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch b/target/linux/ipq807x/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch deleted file mode 100644 index ac7af52a74..0000000000 --- a/target/linux/ipq807x/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 26c97b6fb7d291f55e0e4a410d266d1355118ed9 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 31 May 2023 20:21:26 +0200 -Subject: [PATCH] mmc: core: disable TRIM on Kingston EMMC04G-M627 - -It seems that Kingston EMMC04G-M627 despite advertising TRIM support does -not work when the core is trying to use REQ_OP_WRITE_ZEROES. - -We are seeing I/O errors in OpenWrt under 6.1 on Zyxel NBG7815 that we did -not previously have and tracked it down to REQ_OP_WRITE_ZEROES. - -Trying to use fstrim seems to also throw errors like: -[93010.835112] I/O error, dev loop0, sector 16902 op 0x3:(DISCARD) flags 0x800 phys_seg 1 prio class 2 - -Disabling TRIM makes the error go away, so lets add a quirk for this eMMC -to disable TRIM. - -Signed-off-by: Robert Marko ---- - drivers/mmc/core/quirks.h | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/drivers/mmc/core/quirks.h -+++ b/drivers/mmc/core/quirks.h -@@ -108,6 +108,13 @@ static const struct mmc_fixup __maybe_un - MMC_QUIRK_TRIM_BROKEN), - - /* -+ * Kingston EMMC04G-M627 advertises TRIM but it does not seems to -+ * support being used to offload WRITE_ZEROES. -+ */ -+ MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, -+ MMC_QUIRK_TRIM_BROKEN), -+ -+ /* - * Some SD cards reports discard support while they don't - */ - MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, diff --git a/target/linux/ipq807x/patches-6.1/0134-arm64-dts-qcom-ipq8074-add-critical-thermal-trips.patch b/target/linux/ipq807x/patches-6.1/0134-arm64-dts-qcom-ipq8074-add-critical-thermal-trips.patch deleted file mode 100644 index 6231d8afb7..0000000000 --- a/target/linux/ipq807x/patches-6.1/0134-arm64-dts-qcom-ipq8074-add-critical-thermal-trips.patch +++ /dev/null @@ -1,197 +0,0 @@ -From 145bbf2b88990ef3ff00ee541bb7662008683c16 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Wed, 7 Jun 2023 20:26:26 +0200 -Subject: [PATCH] arm64: dts: qcom: ipq8074: add critical thermal trips - -According to bindings, thermal zones must have associated trips as well. -Since we currently dont have CPUFreq support and thus no passive cooling -lets start by defining critical trips to protect the devices against -severe overheating. - -Signed-off-by: Robert Marko ---- - arch/arm64/boot/dts/qcom/ipq8074.dtsi | 96 +++++++++++++++++++++++++++ - 1 file changed, 96 insertions(+) - ---- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi -@@ -1293,6 +1293,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 4>; -+ -+ trips { -+ nss-top-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - nss0-thermal { -@@ -1300,6 +1308,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 5>; -+ -+ trips { -+ nss-0-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - nss1-thermal { -@@ -1307,6 +1323,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 6>; -+ -+ trips { -+ nss-1-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - wcss-phya0-thermal { -@@ -1314,6 +1338,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 7>; -+ -+ trips { -+ wcss-phya0-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - wcss-phya1-thermal { -@@ -1321,6 +1353,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 8>; -+ -+ trips { -+ wcss-phya1-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - cpu0_thermal: cpu0-thermal { -@@ -1328,6 +1368,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 9>; -+ -+ trips { -+ cpu0-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - cpu1_thermal: cpu1-thermal { -@@ -1335,6 +1383,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 10>; -+ -+ trips { -+ cpu1-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - cpu2_thermal: cpu2-thermal { -@@ -1342,6 +1398,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 11>; -+ -+ trips { -+ cpu2-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - cpu3_thermal: cpu3-thermal { -@@ -1349,6 +1413,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 12>; -+ -+ trips { -+ cpu3-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - cluster_thermal: cluster-thermal { -@@ -1356,6 +1428,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 13>; -+ -+ trips { -+ cluster-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - wcss-phyb0-thermal { -@@ -1363,6 +1443,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 14>; -+ -+ trips { -+ wcss-phyb0-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - - wcss-phyb1-thermal { -@@ -1370,6 +1458,14 @@ - polling-delay = <1000>; - - thermal-sensors = <&tsens 15>; -+ -+ trips { -+ wcss-phyb1-crit { -+ temperature = <110000>; -+ hysteresis = <1000>; -+ type = "critical"; -+ }; -+ }; - }; - }; - }; diff --git a/target/linux/ipq807x/patches-6.1/0900-power-Add-Qualcomm-APM.patch b/target/linux/ipq807x/patches-6.1/0900-power-Add-Qualcomm-APM.patch deleted file mode 100644 index 2e5c72b7d1..0000000000 --- a/target/linux/ipq807x/patches-6.1/0900-power-Add-Qualcomm-APM.patch +++ /dev/null @@ -1,1047 +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 -@@ -1,3 +1,4 @@ - # SPDX-License-Identifier: GPL-2.0-only - source "drivers/power/reset/Kconfig" - source "drivers/power/supply/Kconfig" -+source "drivers/power/qcom/Kconfig" ---- a/drivers/power/Makefile -+++ b/drivers/power/Makefile -@@ -1,3 +1,4 @@ - # SPDX-License-Identifier: GPL-2.0-only - obj-$(CONFIG_POWER_RESET) += reset/ - 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,944 @@ -+/* -+ * 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_find_property(dev->of_node, "qcom,apm-post-halt-delay", NULL)) { -+ 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_find_property(dev->of_node, "qcom,apm-halt-clk-delay", NULL)) { -+ 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_find_property(dev->of_node, "qcom,apm-resume-clk-delay", NULL)) { -+ 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_find_property(dev->of_node, "qcom,apm-sel-switch-delay", NULL)) { -+ 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 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; -+ const struct of_device_id *match; -+ 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; -+ } -+ -+ match = of_match_device(msm_apm_match_table, dev); -+ if (!match) -+ 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)match->data; -+ 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 int 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); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver msm_apm_driver = { -+ .driver = { -+ .name = MSM_APM_DRIVER_NAME, -+ .of_match_table = msm_apm_match_table, -+ .owner = THIS_MODULE, -+ }, -+ .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/ipq807x/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch b/target/linux/ipq807x/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch deleted file mode 100644 index 9b9f7159f3..0000000000 --- a/target/linux/ipq807x/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch +++ /dev/null @@ -1,12144 +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 -@@ -1524,4 +1524,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 -@@ -110,6 +110,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,695 @@ -+/* -+ * 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 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 of_device_id *match; -+ 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; -+ -+ match = of_match_device(cpr3_regulator_match_table, &pdev->dev); -+ if (!match) -+ return -ENODEV; -+ -+ cpr_data = (struct cpr3_reg_data *)match->data; -+ 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 int cpr3_npu_regulator_remove(struct platform_device *pdev) -+{ -+ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); -+ -+ return 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, -+ .owner = THIS_MODULE, -+ }, -+ .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; -+ struct cpr4_reg_data *cpr4_regulator_data; -+ 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,2750 @@ -+/* -+ * 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 -+ -+#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 -+ -+/** -+ * 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_find_property(vreg->of_node, "qcom,system-voltage", NULL)) { -+ 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_find_property(vreg->of_node, -+ "qcom,allow-aging-voltage-adjustment", NULL)) { -+ 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_find_property(vreg->of_node, -+ "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) { -+ 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_find_property(vreg->of_node, "qcom,cpr-aging-derate", -+ NULL)) { -+ 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_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) { -+ 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_find_property(ctrl->dev->of_node, "clock-names", NULL)) { -+ 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_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { -+ 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_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { -+ 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_find_property(vreg->of_node, prop, NULL)) -+ 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) -+ strlcpy(prop_name, prop_name_v2, sizeof(prop_name_v2)); -+ else -+ strlcpy(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 { -+ strlcpy(prop_str, "qcom,cpr-cold-temp-voltage-adjustment", -+ sizeof(prop_str)); -+ } -+ -+ if (!of_find_property(vreg->of_node, prop_str, NULL)) { -+ /* 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) -+ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", -+ sizeof(prop_str)); -+ else -+ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", -+ sizeof(prop_str)); -+ -+ if (!of_find_property(vreg->of_node, prop_str, NULL)) { -+ /* 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 { -+ strlcpy(req_prop_str, "qcom,cpr-cold-temp-voltage-adjustment", -+ sizeof(req_prop_str)); -+ } -+ -+ if (soc_version_major > 1) -+ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", -+ sizeof(prop_str)); -+ else -+ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", -+ sizeof(prop_str)); -+ -+ if (!of_find_property(vreg->of_node, req_prop_str, NULL)) { -+ /* No adjustment required. */ -+ cpr3_info(vreg, "Cold temperature adjustment not required.\n"); -+ return 0; -+ } -+ -+ if (!of_find_property(vreg->of_node, prop_str, NULL)) { -+ /* 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 { -+ strlcpy(prop_str, "qcom,cpr-open-loop-voltage-fuse-adjustment", -+ sizeof(prop_str)); -+ } -+ -+ if (!of_find_property(vreg->of_node, prop_str, NULL)) { -+ /* 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_find_property(vreg->of_node, -+ "qcom,cpr-open-loop-voltage-adjustment", NULL)) { -+ /* 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_find_property(vreg->of_node, -+ "qcom,cpr-open-loop-voltage-min-diff", NULL)) { -+ 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_find_property(vreg->of_node, volt_adj, NULL) -+ && !of_find_property(vreg->of_node, volt_fuse_adj, NULL) -+ && !vreg->aging_allowed) { -+ /* No adjustment required. */ -+ return 0; -+ } else if (!of_find_property(vreg->of_node, ro_scaling, NULL)) { -+ 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_find_property(vreg->of_node, volt_fuse_adj, NULL)) { -+ 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_find_property(vreg->of_node, volt_adj, NULL)) { -+ 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_find_property(node, "qcom,apm-ctrl", NULL)) { -+ /* 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_find_property(node, use_corner_band ? -+ "qcom,corner-band-allow-temp-adjustment" -+ : "qcom,corner-allow-temp-adjustment", NULL)) { -+ if (!ctrl->allow_temp_adj) { -+ cpr3_err(ctrl, "Temperature adjustment configurations missing\n"); -+ return -EINVAL; -+ } -+ -+ vreg->allow_temp_adj = true; -+ } -+ -+ if (of_find_property(node, use_corner_band ? -+ "qcom,corner-band-allow-core-count-adjustment" -+ : "qcom,corner-allow-core-count-adjustment", -+ NULL)) { -+ 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_find_property(vreg->of_node, -+ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { -+ /* 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_find_property(vreg->of_node, -+ "qcom,cpr-closed-loop-voltage-adjustment", NULL); -+ fused_adjustment = of_find_property(vreg->of_node, -+ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); -+ -+ if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { -+ /* No adjustment required. */ -+ return 0; -+ } else if (!of_find_property(vreg->of_node, -+ "qcom,cpr-ro-scaling-factor", NULL)) { -+ 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,1819 @@ -+/* -+ * 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_find_property(node, "qcom,cpr-ro-sel", NULL)) { -+ 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_find_property(node, -+ "qcom,cpr-misc-fuse-voltage-adjustment", -+ NULL)) { -+ 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_find_property(vreg->of_node, -+ "qcom,cpr-misc-fuse-voltage-adjustment", NULL)) { -+ 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_find_property(vreg->of_node, "qcom,allow-boost", NULL)) { -+ 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_find_property(vreg->of_node, -+ "qcom,cpr-boost-voltage-fuse-adjustment", NULL)) { -+ 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_find_property(vreg->of_node, -+ "qcom,cpr-boost-temp-adjustment", NULL)) { -+ 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_find_property(ctrl->dev->of_node, "vdd-limit-supply", NULL)) { -+ 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 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 of_device_id *match; -+ 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; -+ -+ match = of_match_device(cpr4_regulator_match_table, &pdev->dev); -+ if (!match) -+ return -ENODEV; -+ -+ cpr_data = (struct cpr4_reg_data *)match->data; -+ 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 int cpr4_apss_regulator_remove(struct platform_device *pdev) -+{ -+ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); -+ -+ return 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, -+ .owner = THIS_MODULE, -+ }, -+ .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); ---- /dev/null -+++ b/include/soc/qcom/socinfo.h -@@ -0,0 +1,463 @@ -+/* Copyright (c) 2009-2014, 2016, 2020, 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 _ARCH_ARM_MACH_MSM_SOCINFO_H_ -+#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ -+ -+#include -+ -+#define CPU_IPQ8074 323 -+#define CPU_IPQ8072 342 -+#define CPU_IPQ8076 343 -+#define CPU_IPQ8078 344 -+#define CPU_IPQ8070 375 -+#define CPU_IPQ8071 376 -+ -+#define CPU_IPQ8072A 389 -+#define CPU_IPQ8074A 390 -+#define CPU_IPQ8076A 391 -+#define CPU_IPQ8078A 392 -+#define CPU_IPQ8070A 395 -+#define CPU_IPQ8071A 396 -+ -+#define CPU_IPQ8172 397 -+#define CPU_IPQ8173 398 -+#define CPU_IPQ8174 399 -+ -+#define CPU_IPQ6018 402 -+#define CPU_IPQ6028 403 -+#define CPU_IPQ6000 421 -+#define CPU_IPQ6010 422 -+#define CPU_IPQ6005 453 -+ -+#define CPU_IPQ5010 446 -+#define CPU_IPQ5018 447 -+#define CPU_IPQ5028 448 -+#define CPU_IPQ5000 503 -+#define CPU_IPQ0509 504 -+#define CPU_IPQ0518 505 -+ -+#define CPU_IPQ9514 510 -+#define CPU_IPQ9554 512 -+#define CPU_IPQ9570 513 -+#define CPU_IPQ9574 514 -+#define CPU_IPQ9550 511 -+#define CPU_IPQ9510 521 -+ -+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); -+} -+ -+static inline int read_ipq_cpu_type(void) -+{ -+ const int *prop; -+ prop = of_get_property(of_find_node_by_path("/"), "cpu_type", NULL); -+ /* -+ * Return Default CPU type if "cpu_type" property is not found in DTSI -+ */ -+ if (!prop) -+ return CPU_IPQ8074; -+ -+ return le32_to_cpu(*prop); -+} -+ -+static inline int cpu_is_ipq8070(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8070; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8071(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8071; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8072(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8072; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8074(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8074; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8076(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8076; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8078(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8078; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8072a(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8072A; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8074a(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8074A; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8076a(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8076A; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8078a(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8078A; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8070a(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8070A; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8071a(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8071A; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8172(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8172; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8173(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8173; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq8174(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ8174; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq6018(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ6018; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq6028(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ6028; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq6000(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ6000; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq6010(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ6010; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq6005(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ6005; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq5010(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ5010; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq5018(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ5018; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq5028(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ5028; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq5000(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ5000; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq0509(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ0509; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq0518(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ0518; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq9514(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ9514; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq9554(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ9554; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq9570(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ9570; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq9574(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ9574; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq9550(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ9550; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq9510(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return read_ipq_cpu_type() == CPU_IPQ9510; -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq807x(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq8072() || cpu_is_ipq8074() || -+ cpu_is_ipq8076() || cpu_is_ipq8078() || -+ cpu_is_ipq8070() || cpu_is_ipq8071() || -+ cpu_is_ipq8072a() || cpu_is_ipq8074a() || -+ cpu_is_ipq8076a() || cpu_is_ipq8078a() || -+ cpu_is_ipq8070a() || cpu_is_ipq8071a() || -+ cpu_is_ipq8172() || cpu_is_ipq8173() || -+ cpu_is_ipq8174(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq60xx(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq6018() || cpu_is_ipq6028() || -+ cpu_is_ipq6000() || cpu_is_ipq6010() || -+ cpu_is_ipq6005(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq50xx(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq5010() || cpu_is_ipq5018() || -+ cpu_is_ipq5028() || cpu_is_ipq5000() || -+ cpu_is_ipq0509() || cpu_is_ipq0518(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_ipq95xx(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq9514() || cpu_is_ipq9554() || -+ cpu_is_ipq9570() || cpu_is_ipq9574() || -+ cpu_is_ipq9550() || cpu_is_ipq9510(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_nss_crypto_enabled(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq807x() || cpu_is_ipq60xx() || -+ cpu_is_ipq50xx() || cpu_is_ipq9570() || -+ cpu_is_ipq9550() || cpu_is_ipq9574() || -+ cpu_is_ipq9554(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_internal_wifi_enabled(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq807x() || cpu_is_ipq60xx() || -+ cpu_is_ipq50xx() || cpu_is_ipq9514() || -+ cpu_is_ipq9554() || cpu_is_ipq9574(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_uniphy1_enabled(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq807x() || cpu_is_ipq60xx() || -+ cpu_is_ipq9554() || cpu_is_ipq9570() || -+ cpu_is_ipq9574() || cpu_is_ipq9550(); -+#else -+ return 0; -+#endif -+} -+ -+static inline int cpu_is_uniphy2_enabled(void) -+{ -+#ifdef CONFIG_ARCH_QCOM -+ return cpu_is_ipq807x() || cpu_is_ipq9570() || -+ cpu_is_ipq9574(); -+#else -+ return 0; -+#endif -+} -+ -+#endif /* _ARCH_ARM_MACH_MSM_SOCINFO_H_ */ diff --git a/target/linux/ipq807x/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch b/target/linux/ipq807x/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch deleted file mode 100644 index 9b8b4df12b..0000000000 --- a/target/linux/ipq807x/patches-6.1/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/Makefile b/target/linux/qualcommax/Makefile new file mode 100644 index 0000000000..c6b74e05c9 --- /dev/null +++ b/target/linux/qualcommax/Makefile @@ -0,0 +1,22 @@ +include $(TOPDIR)/rules.mk + +ARCH:=aarch64 +BOARD:=qualcommax +BOARDNAME:=Qualcomm Atheros 802.11ax WiSoC-s +FEATURES:=squashfs ramdisk fpu nand rtc emmc +KERNELNAME:=Image dtbs +CPU_TYPE:=cortex-a53 +SUBTARGETS:=generic + +KERNEL_PATCHVER:=6.1 + +include $(INCLUDE_DIR)/target.mk +DEFAULT_PACKAGES += \ + kmod-usb3 kmod-usb-dwc3 kmod-usb-dwc3-qcom \ + kmod-leds-gpio kmod-gpio-button-hotplug \ + kmod-phy-aquantia kmod-qca-nss-dp \ + ath11k-firmware-ipq8074 kmod-ath11k-ahb \ + wpad-basic-mbedtls uboot-envtools \ + e2fsprogs kmod-fs-ext4 losetup + +$(eval $(call BuildTarget)) diff --git a/target/linux/qualcommax/base-files/etc/board.d/01_leds b/target/linux/qualcommax/base-files/etc/board.d/01_leds new file mode 100644 index 0000000000..a89b4c1564 --- /dev/null +++ b/target/linux/qualcommax/base-files/etc/board.d/01_leds @@ -0,0 +1,33 @@ + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +edgecore,eap102) + ucidef_set_led_netdev "wan" "WAN" "green:wanpoe" "wan" + ;; +netgear,wax218) + ucidef_set_led_netdev "lan" "LAN" "blue:lan" "lan" + ucidef_set_led_wlan "wlan5g" "WIFI 5GHz" "blue:wlan5g" "phy0radio" + ucidef_set_led_wlan "wlan2g" "WIFI 2.4GHz" "blue:wlan2g" "phy1radio" + ;; +redmi,ax6|\ +xiaomi,ax3600) + ucidef_set_led_netdev "wan" "WAN" "blue:network" "wan" + ;; +qnap,301w) + ucidef_set_led_netdev "lan1" "LAN1" "green:lan1" "lan1" + ucidef_set_led_netdev "lan2" "LAN2" "green:lan2" "lan2" + ucidef_set_led_netdev "lan3" "LAN3" "green:lan3" "lan3" + ucidef_set_led_netdev "lan4" "LAN4" "green:lan4" "lan4" + ucidef_set_led_netdev "10G_1" "10G_1" "green:10g_1" "10g-1" + ucidef_set_led_netdev "10G_2" "10G_2" "green:10g_2" "10g-2" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/qualcommax/base-files/etc/board.d/02_network b/target/linux/qualcommax/base-files/etc/board.d/02_network new file mode 100644 index 0000000000..8175a99f97 --- /dev/null +++ b/target/linux/qualcommax/base-files/etc/board.d/02_network @@ -0,0 +1,53 @@ +# +# Copyright (c) 2015 The Linux Foundation. All rights reserved. +# Copyright (c) 2011-2015 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +ipq807x_setup_interfaces() +{ + local board="$1" + + case "$board" in + buffalo,wxr-5950ax12|\ + dynalink,dl-wrx36|\ + xiaomi,ax9000) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "wan" + ;; + edgecore,eap102) + ucidef_set_interfaces_lan_wan "lan" "wan" + ;; + edimax,cax1800) + ucidef_set_interfaces_lan_wan "lan" + ;; + netgear,wax218) + ucidef_set_interface_lan "lan" "dhcp" + ;; + prpl,haze) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" + ;; + qnap,301w) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 10g-2" "10g-1" + ;; + compex,wpq873|\ + redmi,ax6|\ + xiaomi,ax3600) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" + ;; + zyxel,nbg7815) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 10g" "wan" + ;; + *) + echo "Unsupported hardware. Network interfaces not initialized" + ;; + esac +} + +board_config_update +board=$(board_name) +ipq807x_setup_interfaces $board +board_config_flush + +exit 0 diff --git a/target/linux/qualcommax/base-files/etc/hotplug.d/firmware/11-ath11k-caldata b/target/linux/qualcommax/base-files/etc/hotplug.d/firmware/11-ath11k-caldata new file mode 100644 index 0000000000..f9315573ec --- /dev/null +++ b/target/linux/qualcommax/base-files/etc/hotplug.d/firmware/11-ath11k-caldata @@ -0,0 +1,44 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/functions/caldata.sh + +board=$(board_name) + +case "$FIRMWARE" in +"ath11k/IPQ8074/hw2.0/cal-ahb-c000000.wifi.bin") + case "$board" in + buffalo,wxr-5950ax12|\ + compex,wpq873|\ + edgecore,eap102|\ + edimax,cax1800|\ + dynalink,dl-wrx36|\ + netgear,wax218|\ + qnap,301w|\ + redmi,ax6|\ + xiaomi,ax3600|\ + xiaomi,ax9000|\ + zyxel,nbg7815) + caldata_extract "0:art" 0x1000 0x20000 + ;; + prpl,haze) + caldata_extract_mmc "0:ART" 0x1000 0x20000 + ;; + esac + ;; +"ath11k/QCN9074/hw1.0/cal-pci-0000:01:00.0.bin"|\ +"ath11k/QCN9074/hw1.0/cal-pci-0001:01:00.0.bin") + case "$board" in + prpl,haze) + caldata_extract_mmc "0:ART" 0x26800 0x20000 + ;; + xiaomi,ax9000) + caldata_extract "0:art" 0x26800 0x20000 + ;; + esac + ;; +*) + exit 1 + ;; +esac diff --git a/target/linux/qualcommax/base-files/etc/init.d/bootcount b/target/linux/qualcommax/base-files/etc/init.d/bootcount new file mode 100755 index 0000000000..6917446a9b --- /dev/null +++ b/target/linux/qualcommax/base-files/etc/init.d/bootcount @@ -0,0 +1,13 @@ +#!/bin/sh /etc/rc.common + +START=99 + +boot() { + case $(board_name) in + edgecore,eap102) + fw_setenv upgrade_available 0 + # Unset changed flag after sysupgrade complete + fw_setenv changed + ;; + esac +} diff --git a/target/linux/qualcommax/base-files/lib/upgrade/buffalo.sh b/target/linux/qualcommax/base-files/lib/upgrade/buffalo.sh new file mode 100644 index 0000000000..d0ed258890 --- /dev/null +++ b/target/linux/qualcommax/base-files/lib/upgrade/buffalo.sh @@ -0,0 +1,55 @@ +. /lib/functions.sh + +# Prepare UBI devices for OpenWrt installation +# - rootfs (mtd22) +# - remove "ubi_rootfs" volume (rootfs on stock) +# - remove "fw_hash" volume (firmware hash) +# - user_property (mtd24) +# - remove "user_property_ubi" volume (user configuration) +# - remove "extra_property" volume (gzipped syslog) +buffalo_upgrade_prepare() { + local ubi_rootdev ubi_propdev + + if ! ubi_rootdev="$(nand_attach_ubi rootfs)" || \ + ! ubi_propdev="$(nand_attach_ubi user_property)"; then + echo "failed to attach UBI volume \"rootfs\" or \"user_property\", rebooting..." + reboot -f + fi + + ubirmvol /dev/$ubi_rootdev -N ubi_rootfs &> /dev/null || true + ubirmvol /dev/$ubi_rootdev -N fw_hash &> /dev/null || true + + ubirmvol /dev/$ubi_propdev -N user_property_ubi &> /dev/null || true + ubirmvol /dev/$ubi_propdev -N extra_property &> /dev/null || true +} + +# Re-create small dummy ubi_rootfs volume and update +# fw_hash volume to pass the checking by U-Boot +# - rootfs (mtd22) +# - re-create "ubi_rootfs" volume +# - re-create and update "fw_hash" volume +# - rootfs_recover (mtd23) +# - update "fw_hash" volume +buffalo_upgrade_optvol() { + local ubi_rootdev ubi_rcvrdev + local hashvol_root hashvol_rcvr + + if ! ubi_rootdev="$(nand_attach_ubi rootfs)" || \ + ! ubi_rcvrdev="$(nand_attach_ubi rootfs_recover)"; then + echo "failed to attach UBI volume \"rootfs\" or \"rootfs_recover\", rebooting..." + reboot -f + fi + + ubimkvol /dev/$ubi_rootdev -N ubi_rootfs -S 1 + ubimkvol /dev/$ubi_rootdev -N fw_hash -S 1 -t static + + if ! hashvol_root="$(nand_find_volume $ubi_rootdev fw_hash)" || \ + ! hashvol_rcvr="$(nand_find_volume $ubi_rcvrdev fw_hash)"; then + echo "\"fw_hash\" volume in \"rootfs\" or \"rootfs_recover\" not found, rebooting..." + reboot -f + fi + + echo -n "00000000000000000000000000000000" > /tmp/dummyhash.txt + ubiupdatevol /dev/$hashvol_root /tmp/dummyhash.txt + ubiupdatevol /dev/$hashvol_rcvr /tmp/dummyhash.txt +} diff --git a/target/linux/qualcommax/base-files/lib/upgrade/mmc.sh b/target/linux/qualcommax/base-files/lib/upgrade/mmc.sh new file mode 100644 index 0000000000..dac9ddd568 --- /dev/null +++ b/target/linux/qualcommax/base-files/lib/upgrade/mmc.sh @@ -0,0 +1,83 @@ +# +# Copyright (C) 2016 lede-project.org +# + +# this can be used as a generic mmc upgrade script +# just add a device entry in platform.sh, +# define "kernelname" and "rootfsname" and call mmc_do_upgrade +# after the kernel and rootfs flash a loopdev (as overlay) is +# setup on top of the rootfs partition +# for the proper function a padded rootfs image is needed, basically +# append "pad-to 64k" to the image definition +# this is based on the ipq806x zyxel.sh mmc upgrade + +. /lib/functions.sh + +mmc_do_upgrade() { + local tar_file="$1" + local rootfs= + local kernel= + + [ -z "$kernel" ] && kernel=$(find_mmc_part ${kernelname}) + [ -z "$rootfs" ] && rootfs=$(find_mmc_part ${rootfsname}) + + [ -z "$kernel" ] && echo "Upgrade failed: kernel partition not found! Rebooting..." && reboot -f + [ -z "$rootfs" ] && echo "Upgrade failed: rootfs partition not found! Rebooting..." && reboot -f + + mmc_do_flash $tar_file $kernel $rootfs + + return 0 +} + +mmc_do_flash() { + local tar_file=$1 + local kernel=$2 + local rootfs=$3 + + # keep sure its unbound + losetup --detach-all || { + echo Failed to detach all loop devices. Skip this try. + reboot -f + } + + # use the first found directory in the tar archive + local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') + board_dir=${board_dir%/} + + echo "flashing kernel to $kernel" + tar xf $tar_file ${board_dir}/kernel -O >$kernel + + echo "flashing rootfs to ${rootfs}" + tar xf $tar_file ${board_dir}/root -O >"${rootfs}" + + # a padded rootfs is needed for overlay fs creation + local offset=$(tar xf $tar_file ${board_dir}/root -O | wc -c) + [ $offset -lt 65536 ] && { + echo Wrong size for rootfs: $offset + sleep 10 + reboot -f + } + + # Mount loop for rootfs_data + local loopdev="$(losetup -f)" + losetup -o $offset $loopdev $rootfs || { + echo "Failed to mount looped rootfs_data." + sleep 10 + reboot -f + } + + echo "Format new rootfs_data at position ${offset}." + mkfs.ext4 -F -L rootfs_data $loopdev + mkdir /tmp/new_root + mount -t ext4 $loopdev /tmp/new_root && { + echo "Saving config to rootfs_data at position ${offset}." + cp -v "$UPGRADE_BACKUP" "/tmp/new_root/$BACKUP_FILE" + umount /tmp/new_root + } + + # Cleanup + losetup -d $loopdev >/dev/null 2>&1 + sync + umount -a + reboot -f +} diff --git a/target/linux/qualcommax/base-files/lib/upgrade/platform.sh b/target/linux/qualcommax/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..458eb16b79 --- /dev/null +++ b/target/linux/qualcommax/base-files/lib/upgrade/platform.sh @@ -0,0 +1,117 @@ +PART_NAME=firmware +REQUIRE_IMAGE_METADATA=1 + +RAMFS_COPY_BIN='fw_printenv fw_setenv head' +RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' + +xiaomi_initramfs_prepare() { + # Wipe UBI if running initramfs + [ "$(rootfs_type)" = "tmpfs" ] || return 0 + + local rootfs_mtdnum="$( find_mtd_index rootfs )" + if [ ! "$rootfs_mtdnum" ]; then + echo "unable to find mtd partition rootfs" + return 1 + fi + + local kern_mtdnum="$( find_mtd_index ubi_kernel )" + if [ ! "$kern_mtdnum" ]; then + echo "unable to find mtd partition ubi_kernel" + return 1 + fi + + ubidetach -m "$rootfs_mtdnum" + ubiformat /dev/mtd$rootfs_mtdnum -y + + ubidetach -m "$kern_mtdnum" + ubiformat /dev/mtd$kern_mtdnum -y +} + +platform_check_image() { + return 0; +} + +platform_pre_upgrade() { + case "$(board_name)" in + redmi,ax6|\ + xiaomi,ax3600|\ + xiaomi,ax9000) + xiaomi_initramfs_prepare + ;; + esac +} + +platform_do_upgrade() { + case "$(board_name)" in + buffalo,wxr-5950ax12) + CI_KERN_UBIPART="rootfs" + CI_ROOT_UBIPART="user_property" + buffalo_upgrade_prepare + nand_do_flash_file "$1" || nand_do_upgrade_failed + nand_do_restore_config || nand_do_upgrade_failed + buffalo_upgrade_optvol + ;; + dynalink,dl-wrx36) + nand_do_upgrade "$1" + ;; + edgecore,eap102) + active="$(fw_printenv -n active)" + if [ "$active" -eq "1" ]; then + CI_UBIPART="rootfs2" + else + CI_UBIPART="rootfs1" + fi + # force altbootcmd which handles partition change in u-boot + fw_setenv bootcount 3 + fw_setenv upgrade_available 1 + nand_do_upgrade "$1" + ;; + compex,wpq873|\ + edimax,cax1800|\ + netgear,wax218) + nand_do_upgrade "$1" + ;; + prpl,haze|\ + qnap,301w) + kernelname="0:HLOS" + rootfsname="rootfs" + mmc_do_upgrade "$1" + ;; + zyxel,nbg7815) + local config_mtdnum="$(find_mtd_index 0:bootconfig)" + [ -z "$config_mtdnum" ] && reboot + part_num="$(hexdump -e '1/1 "%01x|"' -n 1 -s 168 -C /dev/mtd$config_mtdnum | cut -f 1 -d "|" | head -n1)" + if [ "$part_num" -eq "0" ]; then + kernelname="0:HLOS" + rootfsname="rootfs" + mmc_do_upgrade "$1" + else + kernelname="0:HLOS_1" + rootfsname="rootfs_1" + mmc_do_upgrade "$1" + fi + ;; + redmi,ax6|\ + xiaomi,ax3600|\ + xiaomi,ax9000) + # Make sure that UART is enabled + fw_setenv boot_wait on + fw_setenv uart_en 1 + + # Enforce single partition. + fw_setenv flag_boot_rootfs 0 + fw_setenv flag_last_success 0 + fw_setenv flag_boot_success 1 + fw_setenv flag_try_sys1_failed 8 + fw_setenv flag_try_sys2_failed 8 + + # Kernel and rootfs are placed in 2 different UBI + CI_KERN_UBIPART="ubi_kernel" + CI_ROOT_UBIPART="rootfs" + nand_do_upgrade "$1" + ;; + *) + default_do_upgrade "$1" + ;; + esac +} diff --git a/target/linux/qualcommax/config-6.1 b/target/linux/qualcommax/config-6.1 new file mode 100644 index 0000000000..2fa4aa30bb --- /dev/null +++ b/target/linux/qualcommax/config-6.1 @@ -0,0 +1,560 @@ +CONFIG_64BIT=y +# CONFIG_APQ_GCC_8084 is not set +# CONFIG_APQ_MMCC_8084 is not set +CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y +CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +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_NR_GPIO=0 +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_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_PAGE_SHIFT=12 +CONFIG_ARM64_PA_BITS=48 +CONFIG_ARM64_PA_BITS_48=y +CONFIG_ARM64_PTR_AUTH=y +CONFIG_ARM64_PTR_AUTH_KERNEL=y +CONFIG_ARM64_SME=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_GIC_V3_ITS_PCI=y +# CONFIG_ARM_MHU_V2 is not set +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_PSCI_FW=y +# CONFIG_ARM_QCOM_CPUFREQ_HW is not set +CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y +CONFIG_AUDIT_ARCH_COMPAT_GENERIC=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_CAVIUM_TX2_ERRATUM_219=y +CONFIG_CC_HAVE_SHADOW_CALL_STACK=y +CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5" +CONFIG_CC_NO_ARRAY_BOUNDS=y +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_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_THERMAL=y +CONFIG_CRC16=y +CONFIG_CRC8=y +CONFIG_CRYPTO_AUTHENC=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_DEV_QCE=y +CONFIG_CRYPTO_DEV_QCE_AEAD=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set +CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set +# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set +CONFIG_CRYPTO_DEV_QCE_SHA=y +CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y +CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 +CONFIG_CRYPTO_DEV_QCOM_RNG=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_UTILS=y +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_ZSTD=y +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEV_COREDUMP=y +CONFIG_DMADEVICES=y +CONFIG_DMA_DIRECT_REMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DTC=y +CONFIG_DT_IDLE_GENPD=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_FUJITSU_ERRATUM_010001=y +CONFIG_FWNODE_MDIO=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_FW_LOADER_SYSFS=y +CONFIG_GCC11_NO_ARRAY_BOUNDS=y +CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=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_VULNERABILITIES=y +CONFIG_GENERIC_CSUM=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IOREMAP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y +CONFIG_GENERIC_MSI_IRQ=y +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GLOB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_QCOM=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_IPQ_APSS_6018=y +CONFIG_IPQ_APSS_PLL=y +# CONFIG_IPQ_GCC_4019 is not set +# CONFIG_IPQ_GCC_6018 is not set +# CONFIG_IPQ_GCC_806X is not set +CONFIG_IPQ_GCC_8074=y +# CONFIG_IPQ_LCC_806X 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_WORK=y +# CONFIG_KPSS_XCC is not set +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_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_MDM_GCC_9615 is not set +# CONFIG_MDM_LCC_9615 is not set +CONFIG_MEMFD_CREATE=y +# CONFIG_MFD_HI6421_SPMI is not set +# CONFIG_MFD_QCOM_RPM is not set +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_CQHCI=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +CONFIG_MMC_SDHCI_MSM=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MODULES_USE_ELF_RELA=y +# CONFIG_MSM_GCC_8660 is not set +# CONFIG_MSM_GCC_8909 is not set +# CONFIG_MSM_GCC_8916 is not set +# CONFIG_MSM_GCC_8939 is not set +# CONFIG_MSM_GCC_8960 is not set +# CONFIG_MSM_GCC_8974 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_LCC_8960 is not set +# CONFIG_MSM_MMCC_8960 is not set +# CONFIG_MSM_MMCC_8974 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_FLOW_LIMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NLS=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NR_CPUS=4 +CONFIG_NVIDIA_CARMEL_CNP_ERRATUM=y +CONFIG_NVMEM=y +CONFIG_NVMEM_QCOM_QFPROM=y +# CONFIG_NVMEM_SPMI_SDAM is not set +CONFIG_NVMEM_SYSFS=y +CONFIG_NVMEM_U_BOOT_ENV=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_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +CONFIG_PGTABLE_LEVELS=3 +CONFIG_PHYLIB=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_IPQ4019_USB is not set +# CONFIG_PHY_QCOM_IPQ806X_SATA is not set +# CONFIG_PHY_QCOM_IPQ806X_USB is not set +# CONFIG_PHY_QCOM_PCIE2 is not set +CONFIG_PHY_QCOM_QMP=y +CONFIG_PHY_QCOM_QUSB2=y +# 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_IPQ6018 is not set +CONFIG_PINCTRL_IPQ8074=y +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_SPMI_PMIC=y +# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set +# CONFIG_PINCTRL_QCS404 is not set +# CONFIG_PINCTRL_SC7180 is not set +# CONFIG_PINCTRL_SC8280XP is not set +# CONFIG_PINCTRL_SDM660 is not set +# CONFIG_PINCTRL_SDM845 is not set +# CONFIG_PINCTRL_SM6350 is not set +# CONFIG_PINCTRL_SM6375 is not set +# CONFIG_PINCTRL_SM8150 is not set +# CONFIG_PINCTRL_SM8250 is not set +# CONFIG_PINCTRL_SM8450 is not set +CONFIG_PM=y +# CONFIG_PM8916_WATCHDOG is not set +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_PM_OPP=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_MSM is not set +# CONFIG_POWER_RESET_QCOM_PON is not set +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PRINTK_TIME=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +# CONFIG_QCM_DISPCC_2290 is not set +# CONFIG_QCM_GCC_2290 is not set +# CONFIG_QCOM_A53PLL is not set +# CONFIG_QCOM_AOSS_QMP is not set +CONFIG_QCOM_APCS_IPC=y +CONFIG_QCOM_APM=y +# CONFIG_QCOM_APR is not set +CONFIG_QCOM_BAM_DMA=y +# CONFIG_QCOM_CLK_APCC_MSM8996 is not set +# CONFIG_QCOM_CLK_APCS_MSM8916 is not set +# CONFIG_QCOM_CLK_APCS_SDX55 is not set +# CONFIG_QCOM_COINCELL is not set +# CONFIG_QCOM_COMMAND_DB is not set +# CONFIG_QCOM_CPR is not set +# CONFIG_QCOM_EBI2 is not set +# CONFIG_QCOM_FASTRPC is not set +CONFIG_QCOM_GDSC=y +# 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_IPCC is not set +# CONFIG_QCOM_LLCC is not set +CONFIG_QCOM_MDT_LOADER=y +# CONFIG_QCOM_MPM is not set +# 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_MSS is not set +# CONFIG_QCOM_Q6V5_PAS is not set +CONFIG_QCOM_Q6V5_WCSS=y +# CONFIG_QCOM_RMTFS_MEM is not set +# CONFIG_QCOM_RPMH is not set +CONFIG_QCOM_RPROC_COMMON=y +CONFIG_QCOM_SCM=y +# CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT is not set +# 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_SPMI_ADC5=y +# CONFIG_QCOM_SPMI_RRADC is not set +# CONFIG_QCOM_STATS is not set +# CONFIG_QCOM_SYSMON is not set +CONFIG_QCOM_TSENS=y +CONFIG_QCOM_VADC_COMMON=y +# 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_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y +CONFIG_RANDSTRUCT_NONE=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_SPMI=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_CPR3=y +# CONFIG_REGULATOR_CPR3_NPU is not set +CONFIG_REGULATOR_CPR4_APSS=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_QCOM_LABIBB is not set +CONFIG_REGULATOR_QCOM_SPMI=y +# CONFIG_REGULATOR_QCOM_USB_VBUS is not set +# 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_DRV_PM8XXX=y +CONFIG_RTC_I2C_AND_SPI=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +# CONFIG_SCHED_CORE is not set +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_SCHED_THERMAL_PRESSURE=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_DISPCC_7180 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_LPASS_CORECC_7180 is not set +# CONFIG_SC_LPASS_CORECC_7280 is not set +# CONFIG_SC_MSS_7180 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_65 is not set +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SGL_ALLOC=y +CONFIG_SG_POOL=y +CONFIG_SMP=y +# CONFIG_SM_CAMCC_8450 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_GPUCC_6350 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_VIDEOCC_8150 is not set +# CONFIG_SM_VIDEOCC_8250 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_QUP=y +CONFIG_SPMI=y +# CONFIG_SPMI_HISI3670 is not set +CONFIG_SPMI_MSM_PMIC_ARB=y +# CONFIG_SPMI_PMIC_CLKDIV is not set +CONFIG_SRCU=y +CONFIG_SWIOTLB=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_OF=y +CONFIG_THREAD_INFO_IN_TASK=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_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_VIRTIO=y +CONFIG_VIRTIO_ANCHOR=y +# CONFIG_VIRTIO_BLK 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/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts new file mode 100644 index 0000000000..2c9cbd5b3c --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Dirk Buchwalder */ + +/dts-v1/; + +#include "ipq8074-512m.dtsi" +#include "ipq8074-ac-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include + +/ { + model = "Edimax CAX1800"; + compatible = "edimax,cax1800", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_system_red; + led-failsafe = &led_system_red; + led-running = &led_system_green; + led-upgrade = &led_system_red; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet0 = &dp5; + label-mac-device = &dp5; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 32 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_system_red: system-red { + label = "red:system"; + gpios = <&tlmm 25 GPIO_ACTIVE_HIGH>; + }; + + led_system_green: system-green { + label = "green:system"; + gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; + }; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 27 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "rootfs"; + reg = <0x0000000 0x3400000>; + }; + }; + }; +}; + +&blsp1_spi1 { + pinctrl-0 = <&spi_0_pins>; + pinctrl-names = "default"; + cs-select = <0>; + status = "ok"; + + m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + use-default-sizes; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsblenv"; + reg = <0x480000 0x10000>; + }; + + partition@490000 { + label = "0:appsbl"; + reg = <0x490000 0xa0000>; + read-only; + }; + + partition@530000 { + label = "0:appsbl_1"; + reg = <0x530000 0xa0000>; + read-only; + }; + + partition@5d0000 { + label = "0:art"; + reg = <0x5d0000 0x40000>; + read-only; + }; + + partition@610000 { + label = "0:ethphyfw"; + reg = <0x610000 0x80000>; + read-only; + }; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x20>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <5>; + phy_address = <4>; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp5 { + status = "okay"; + phy-handle = <&qca8075>; + label = "lan"; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Edimax-CAX1800"; + qcom,ath11k-fw-memory-mode = <1>; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts new file mode 100644 index 0000000000..f3e82e2251 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dts @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Robert Marko */ + +/dts-v1/; + +#include "ipq8071-ax3600.dtsi" + +/ { + model = "Xiaomi AX3600"; + compatible = "xiaomi,ax3600", "qcom,ipq8074"; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + }; + + led_system_yellow: system-yellow { + label = "yellow:system"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + }; + + network-yellow { + label = "yellow:network"; + gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; + }; + + network-blue { + label = "blue:network"; + gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; + }; + + aiot { + label = "blue:aiot"; + gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0tpt"; + }; + }; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 52 GPIO_ACTIVE_HIGH>; + + bridge@0,0 { + reg = <0x00000000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi0: wifi@1,0 { + status = "okay"; + + compatible = "qcom,ath10k"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath10k-calibration-variant = "Xiaomi-AX3600"; + nvmem-cell-names = "calibration"; + nvmem-cells = <&caldata_qca9889>; + }; + }; +}; + +&wifi { + qcom,ath11k-calibration-variant = "Xiaomi-AX3600"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi new file mode 100644 index 0000000000..c18cef52f3 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Robert Marko */ + +#include "ipq8074-512m.dtsi" +#include "ipq8074-ac-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include + +/ { + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_system_yellow; + led-failsafe = &led_system_yellow; + led-running = &led_system_blue; + led-upgrade = &led_system_yellow; + label-mac-device = &dp2; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_0"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 34 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + /* + * Bootloader will find the NAND DT node by the compatible and + * then "fixup" it by adding the partitions from the SMEM table + * using the legacy bindings thus making it impossible for us + * to change the partition table or utilize NVMEM for calibration. + * So add a dummy partitions node that bootloader will populate + * and set it as disabled so the kernel ignores it instead of + * printing warnings due to the broken way bootloader adds the + * partitions. + */ + partitions { + status = "disabled"; + }; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x100000>; + read-only; + }; + + partition@100000 { + label = "0:mibib"; + reg = <0x100000 0x100000>; + read-only; + }; + + partition@200000 { + label = "0:qsee"; + reg = <0x200000 0x300000>; + read-only; + }; + + partition@500000 { + label = "0:devcfg"; + reg = <0x500000 0x80000>; + read-only; + }; + + partition@580000 { + label = "0:rpm"; + reg = <0x580000 0x80000>; + read-only; + }; + + partition@600000 { + label = "0:cdt"; + reg = <0x600000 0x80000>; + read-only; + }; + + partition@680000 { + label = "0:appsblenv"; + reg = <0x680000 0x80000>; + }; + + partition@700000 { + label = "0:appsbl"; + reg = <0x700000 0x100000>; + read-only; + }; + + partition@800000 { + label = "0:art"; + reg = <0x800000 0x80000>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_dp2: macaddr@6 { + reg = <0x6 0x6>; + }; + + macaddr_dp3: macaddr@c { + reg = <0xc 0x6>; + }; + + macaddr_dp4: macaddr@12 { + reg = <0x12 0x6>; + }; + + macaddr_dp5: macaddr@18 { + reg = <0x18 0x6>; + }; + + caldata_qca9889: caldata@4d000 { + reg = <0x33000 0x844>; + }; + }; + + partition@880000 { + label = "bdata"; + reg = <0x880000 0x80000>; + }; + + partition@900000 { + /* This is crash + crash_syslog parts combined */ + label = "pstore"; + reg = <0x900000 0x100000>; + }; + + /* Make the first rootfs a dedicated ubi partition for kernel */ + partition@a00000 { + label = "ubi_kernel"; + reg = <0xa00000 0x23c0000>; + }; + + /* Place the real rootfs in the original second rootfs and + * expand it to the end of the nand + */ + rootfs: partition@2dc0000 { + label = "rootfs"; + reg = <0x2dc0000 0xd240000>; + }; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8075_4: ethernet-phy@4 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <4>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x20>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <5>; + phy_address = <4>; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_1>; + label = "wan"; + nvmem-cells = <&macaddr_dp2>; + nvmem-cell-names = "mac-address"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan1"; + nvmem-cells = <&macaddr_dp3>; + nvmem-cell-names = "mac-address"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan2"; + nvmem-cells = <&macaddr_dp4>; + nvmem-cell-names = "mac-address"; +}; + +&dp5 { + status = "okay"; + phy-handle = <&qca8075_4>; + label = "lan3"; + nvmem-cells = <&macaddr_dp5>; + nvmem-cell-names = "mac-address"; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-fw-memory-mode = <1>; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts new file mode 100644 index 0000000000..6611a8fe27 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax6.dts @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Zhijun You */ + +/dts-v1/; + +#include "ipq8071-ax3600.dtsi" + +/ { + model = "Redmi AX6"; + compatible = "redmi,ax6", "qcom,ipq8074"; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; + }; + + led_system_yellow: system-yellow { + label = "yellow:system"; + gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; + }; + + network-blue { + label = "blue:network"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + }; + + network-yellow { + label = "yellow:network"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +/* AX6 can both have NAND of 256MiB or 128MiB. + * To be on the safe side, assume 128MiB of NAND. + */ +&rootfs { + reg = <0x2dc0000 0x5220000>; +}; + +&wifi { + qcom,ath11k-calibration-variant = "Redmi-AX6"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts new file mode 100644 index 0000000000..357b6368d9 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2022, Matthew Hagan */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-ac-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include + +/ { + model = "Edgecore EAP102"; + compatible = "edgecore,eap102", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + serial1 = &blsp1_uart3; + led-boot = &led_system_green; + led-failsafe = &led_system_green; + led-running = &led_system_green; + led-upgrade = &led_system_green; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet0 = &dp5; + ethernet1 = &dp6; + label-mac-device = &dp5; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + reset { + label = "reset"; + gpios = <&tlmm 66 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_wanpoe { + label = "green:wanpoe"; + gpios = <&tlmm 46 GPIO_ACTIVE_HIGH>; + }; + + led_wlan2g { + label = "green:wlan2g"; + gpio = <&tlmm 47 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy1radio"; + }; + + led_wlan5g { + label = "green:wlan5g"; + gpio = <&tlmm 48 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0radio"; + }; + + led_system_green: led_system { + label = "green:power"; + gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button_pins { + reset_button { + pins = "gpio66"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_spi1 { + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsblenv"; + reg = <0x480000 0x10000>; + }; + + partition@490000 { + label = "0:appsbl"; + reg = <0x490000 0xc0000>; + read-only; + }; + + partition@550000 { + label = "0:appsbl_1"; + reg = <0x530000 0xc0000>; + read-only; + }; + + partition@610000 { + label = "0:art"; + reg = <0x610000 0x40000>; + read-only; + }; + + partition@650000 { + label = "0:ethphyfw"; + reg = <0x650000 0x80000>; + read-only; + }; + + partition@6d0000 { + label = "0:product_info"; + reg = <0x6d0000 0x80000>; + read-only; + }; + + partition@750000 { + label = "priv_data1"; + reg = <0x750000 0x10000>; + read-only; + }; + + partition@760000 { + label = "priv_data2"; + reg = <0x760000 0x10000>; + read-only; + }; + }; + }; +}; + +&blsp1_uart3 { + status = "okay"; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "rootfs1"; + reg = <0x0000000 0x3400000>; + }; + + partition@3400000 { + label = "0:wififw"; + reg = <0x3400000 0x800000>; + read-only; + }; + + partition@3c00000 { + label = "rootfs2"; + reg = <0x3c00000 0x3400000>; + }; + + partition@7000000 { + label = "0:wififw_1"; + reg = <0x7000000 0x800000>; + read-only; + }; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + + qca8081_24: ethernet-phy@24 { + compatible = "ethernet-phy-id004d.d101"; + reg = <24>; + reset-gpios = <&tlmm 33 GPIO_ACTIVE_LOW>; + }; + + qca8081_28: ethernet-phy@28 { + compatible = "ethernet-phy-id004d.d101"; + reg = <28>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0x40>; /* wan port bitmap */ + switch_mac_mode = <0xff>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xf>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xf>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@4 { + port_id = <5>; + phy_address = <24>; + port_mac_sel = "QGMAC_PORT"; + }; + port@5 { + port_id = <6>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp5 { + status = "okay"; + phy-handle = <&qca8081_28>; + label = "wan"; +}; + +&dp6 { + status = "okay"; + phy-handle = <&qca8081_24>; + label = "lan"; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Edgecore-EAP102"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts new file mode 100644 index 0000000000..5521a480b8 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Dirk Buchwalder */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "QNAP 301w"; + compatible = "qnap,301w", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + /* + * Aliases as required by u-boot + * to patch MAC addresses + */ + led-boot = &led_system_red; + led-failsafe = &led_system_red; + led-running = &led_pwr_green; + led-upgrade = &led_system_red; + ethernet0 = &dp1; + ethernet1 = &dp2; + ethernet2 = &dp3; + ethernet3 = &dp4; + ethernet4 = &dp5; + ethernet5 = &dp6_syn; + label-mac-device = &dp1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wps-button { + label = "wps"; + gpios = <&tlmm 57 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset-button { + label = "reset"; + gpios = <&tlmm 67 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-0 = <&leds_pins>; + pinctrl-names = "default"; + + led_system_green: led-system-green { + label = "green:system"; + gpios = <&tlmm 1 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_red: led-system-red { + label = "red:system"; + gpios = <&tlmm 3 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_pwr_green: led-pwr-green { + label = "green:pwr"; + gpios = <&tlmm 4 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-wifi-green { + label = "green:wifi"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan4-green { + label = "green:lan4"; + gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan4-amber { + label = "amber:lan4"; + gpios = <&tlmm 7 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan3-green { + label = "green:lan3"; + gpios = <&tlmm 8 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan3-amber { + label = "amber:lan3"; + gpios = <&tlmm 11 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan2-green { + label = "green:lan2"; + gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan2-amber { + label = "amber:lan2"; + gpios = <&tlmm 13 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan1-green { + label = "green:lan1"; + gpios = <&tlmm 14 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-lan1-amber { + label = "amber:lan1"; + gpios = <&tlmm 15 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-10g-1-green { + label = "green:10g_1"; + gpios = <&tlmm 54 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-10g-1-amber { + label = "amber:10g_1"; + gpios = <&tlmm 56 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-10g-2-green { + label = "green:10g_2"; + gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-10g-2-amber { + label = "amber:10g_2"; + gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; + color = ; + }; + }; +}; + +&tlmm { + + mdio_pins: mdio-state { + mdc-pins { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio-pins { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button-state { + wps-pins { + pins = "gpio57"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + rst-pins { + pins = "gpio67"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + leds_pins: leds-state { + pins = "gpio1", "gpio3", "gpio4", "gpio6", "gpio7", "gpio8", + "gpio11", "gpio12", "gpio13", "gpio14", "gpio15", "gpio42", + "gpio51", "gpio52", "gpio54", "gpio56"; + function = "gpio"; + drive-strength = <8>; + bias-pull-down; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_1 { + status = "okay"; +}; + +&qusb_phy_1 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&blsp1_spi1 { /* BLSP1 QUP1 */ + pinctrl-0 = <&spi_0_pins>; + pinctrl-names = "default"; + cs-gpios = <0>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "qcom,smem-part"; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + aqr113c_0: ethernet-phy@0 { + compatible ="ethernet-phy-ieee802.3-c45"; + reg = <0>; + reset-gpios = <&tlmm 59 GPIO_ACTIVE_LOW>; + }; + + aqr113c_8: ethernet-phy@8 { + compatible ="ethernet-phy-ieee802.3-c45"; + reg = <8>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; + + qca8075_16: ethernet-phy@16 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <16>; + }; + + qca8075_17: ethernet-phy@17 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <17>; + }; + + qca8075_18: ethernet-phy@18 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <18>; + }; + + qca8075_19: ethernet-phy@19 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <19>; + }; +}; + +&sdhc_1 { + status = "okay"; + + /* According to the stock dts from the QNAP gpl drop + * the emmc has a problem with the hs400 > hs200 speed switch. + * Therefore remove the mmc-hs400-1_8v property + */ + /delete-property/ mmc-hs400-1_8v; + mmc-hs200-1_8v; + mmc-ddr-1_8v; + vqmmc-supply = <&l11>; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0xc0>; /* wan port bitmap */ + switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xd>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xd>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <16>; + }; + port@1 { + port_id = <2>; + phy_address = <17>; + }; + port@2 { + port_id = <3>; + phy_address = <18>; + }; + port@3 { + port_id = <4>; + phy_address = <19>; + }; + port@4 { + port_id = <5>; + phy_address = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + ethernet-phy-ieee802.3-c45; + }; + port@5 { + port_id = <6>; + phy_address = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + ethernet-phy-ieee802.3-c45; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp1 { + status = "okay"; + phy-handle = <&qca8075_16>; + label = "lan4"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_17>; + label = "lan3"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_18>; + label = "lan2"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_19>; + label = "lan1"; +}; + +&dp5 { + status = "okay"; + qcom,mactype = <1>; + phy-handle = <&aqr113c_8>; + label = "10g-1"; +}; + +&dp6_syn { + status = "okay"; + phy-handle = <&aqr113c_0>; + label = "10g-2"; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "QNAP-301w"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts new file mode 100644 index 0000000000..801aa05604 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2021, Robert Marko */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "Xiaomi AX9000"; + compatible = "xiaomi,ax9000", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_system_yellow; + led-failsafe = &led_system_yellow; + led-running = &led_system_blue; + led-upgrade = &led_system_yellow; + label-mac-device = &dp5; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_0"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 47 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; /* Labeled Mesh on the device */ + gpios = <&tlmm 46 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 48 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_yellow: system-yellow { + label = "yellow:system"; + gpios = <&tlmm 52 GPIO_ACTIVE_HIGH>; + color = ; + }; + + network-yellow { + label = "yellow:network"; + gpios = <&tlmm 50 GPIO_ACTIVE_HIGH>; + color = ; + }; + + network-blue { + label = "blue:network"; + gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + color = ; + }; + + top-red { + label = "red:top"; + gpios = <&tlmm 63 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "keep"; + }; + + top-green { + label = "green:top"; + gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "keep"; + }; + + top-blue { + label = "blue:top"; + gpios = <&tlmm 66 GPIO_ACTIVE_HIGH>; + color = ; + default-state = "keep"; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + i2c_pins: i2c-pins { + pins = "gpio0", "gpio2"; + function = "blsp5_i2c"; + drive-strength = <8>; + bias-disable; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&blsp1_i2c6 { + status = "okay"; + + pinctrl-0 = <&i2c_pins>; + pinctrl-names = "default"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + /* + * Bootloader will find the NAND DT node by the compatible and + * then "fixup" it by adding the partitions from the SMEM table + * using the legacy bindings thus making it impossible for us + * to change the partition table or utilize NVMEM for calibration. + * So add a dummy partitions node that bootloader will populate + * and set it as disabled so the kernel ignores it instead of + * printing warnings due to the broken way bootloader adds the + * partitions. + */ + partitions { + status = "disabled"; + }; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x100000>; + read-only; + }; + + partition@100000 { + label = "0:mibib"; + reg = <0x100000 0x100000>; + read-only; + }; + + partition@200000 { + label = "0:bootconfig"; + reg = <0x200000 0x80000>; + read-only; + }; + + partition@280000 { + label = "0:bootconfig1"; + reg = <0x280000 0x80000>; + read-only; + }; + + partition@300000 { + label = "0:qsee"; + reg = <0x300000 0x300000>; + read-only; + }; + + partition@600000 { + label = "0:qsee_1"; + reg = <0x600000 0x300000>; + read-only; + }; + + partition@900000 { + label = "0:devcfg"; + reg = <0x900000 0x80000>; + read-only; + }; + + partition@980000 { + label = "0:devcfg_1"; + reg = <0x980000 0x80000>; + read-only; + }; + + partition@a00000 { + label = "0:apdp"; + reg = <0xa00000 0x80000>; + read-only; + }; + + partition@a80000 { + label = "0:apdp_1"; + reg = <0xa80000 0x80000>; + read-only; + }; + + partition@b00000 { + label = "0:rpm"; + reg = <0xb00000 0x80000>; + read-only; + }; + + partition@b80000 { + label = "0:rpm_1"; + reg = <0xb80000 0x80000>; + read-only; + }; + + partition@c00000 { + label = "0:cdt"; + reg = <0xc00000 0x80000>; + read-only; + }; + + partition@c80000 { + label = "0:cdt_1"; + reg = <0xc80000 0x80000>; + read-only; + }; + + partition@d00000 { + label = "0:appsblenv"; + reg = <0xd00000 0x80000>; + }; + + partition@d80000 { + label = "0:appsbl"; + reg = <0xd80000 0x100000>; + read-only; + }; + + partition@e80000 { + label = "0:appsbl_1"; + reg = <0xe80000 0x100000>; + read-only; + }; + + partition@f80000 { + label = "0:art"; + reg = <0xf80000 0x80000>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_dp1: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_dp2: macaddr@6 { + reg = <0x6 0x6>; + }; + + macaddr_dp3: macaddr@c { + reg = <0xc 0x6>; + }; + + macaddr_dp4: macaddr@12 { + reg = <0x12 0x6>; + }; + + macaddr_dp5: macaddr@18 { + reg = <0x18 0x6>; + }; + + caldata_qca9889: caldata@4d000 { + reg = <0x4d000 0x844>; + }; + }; + + partition@1000000 { + label = "bdata"; + reg = <0x1000000 0x80000>; + }; + + partition@1080000 { + /* This is crash + crash_syslog parts combined */ + label = "pstore"; + reg = <0x1080000 0x100000>; + }; + + partition@1180000 { + label = "ubi_kernel"; + reg = <0x1180000 0x3800000>; + }; + + partition@4980000 { + label = "rootfs"; + reg = <0x4980000 0xb680000>; + }; + }; + }; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + + qca8075_1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8081: ethernet-phy@24 { + compatible = "ethernet-phy-id004d.d101"; + reg = <24>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x20>; /* wan port bitmap */ + switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xc>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <5>; + phy_address = <24>; + port_mac_sel = "QGMAC_PORT"; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp1 { + status = "okay"; + phy-handle = <&qca8075_0>; + label = "lan4"; + nvmem-cells = <&macaddr_dp1>; + nvmem-cell-names = "mac-address"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_1>; + label = "lan3"; + nvmem-cells = <&macaddr_dp2>; + nvmem-cell-names = "mac-address"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan2"; + nvmem-cells = <&macaddr_dp3>; + nvmem-cell-names = "mac-address"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan1"; + nvmem-cells = <&macaddr_dp4>; + nvmem-cell-names = "mac-address"; +}; + +&dp5 { + status = "okay"; + phy-handle = <&qca8081>; + label = "wan"; + nvmem-cells = <&macaddr_dp5>; + nvmem-cell-names = "mac-address"; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00000000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + /* ath11k has no DT compatible for PCI cards */ + compatible = "pci17cb,1104"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath11k-calibration-variant = "Xiaomi-AX9000"; + }; + }; +}; + +&pcie_qmp1 { + status = "okay"; +}; + +&pcie1 { + status = "okay"; + + perst-gpio = <&tlmm 62 GPIO_ACTIVE_HIGH>; + + bridge@1,0 { + reg = <0x00010000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + compatible = "qcom,ath10k"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath10k-calibration-variant = "Xiaomi-AX9000"; + nvmem-cell-names = "calibration"; + nvmem-cells = <&caldata_qca9889>; + }; + }; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Xiaomi-AX9000"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts new file mode 100644 index 0000000000..5468e9e1fb --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright (c) 2022, Robert Marko */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "Dynalink DL-WRX36"; + compatible = "dynalink,dl-wrx36", "qcom,ipq8074"; + + aliases { + led-boot = &led_system_red; + led-failsafe = &led_system_red; + led-running = &led_system_blue; + led-upgrade = &led_system_red; + serial0 = &blsp1_uart5; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet0 = &dp6_syn; + ethernet1 = &dp4; + ethernet2 = &dp3; + ethernet3 = &dp2; + ethernet4 = &dp1; + label-mac-device = &dp6_syn; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 34 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&tlmm 63 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_red: system-red { + label = "red:system"; + gpios = <&tlmm 25 GPIO_ACTIVE_HIGH>; + color = ; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "qcom,smem-part"; + }; + }; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + + qca8075_1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8081: ethernet-phy@28 { + compatible = "ethernet-phy-id004d.d101"; + reg = <28>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0x40>; /* wan port bitmap */ + switch_mac_mode = <0xb>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xff>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xc>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@5 { + port_id = <6>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp1 { + status = "okay"; + phy-handle = <&qca8075_0>; + label = "lan4"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_1>; + label = "lan3"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan2"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan1"; +}; + +&dp6_syn { + status = "okay"; + phy-handle = <&qca8081>; + label = "wan"; +}; + +&wifi { + status = "okay"; + qcom,ath11k-calibration-variant = "Dynalink-DL-WRX36"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts new file mode 100644 index 0000000000..8a5200b4eb --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "prpl Foundation Haze"; + compatible = "prpl,haze", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + /* Aliases are required by U-Boot to patch MAC addresses */ + ethernet0 = &dp6_syn; + ethernet1 = &dp4; + ethernet2 = &dp3; + ethernet3 = &dp2; + label-mac-device = &dp6_syn; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wps-button { + label = "wps"; + gpios = <&tlmm 42 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset-button { + label = "reset"; + gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-state { + mdc-pins { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio-pins { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button-state { + wps-pins { + pins = "gpio42"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + rst-pins { + pins = "gpio44"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_1 { + status = "okay"; +}; + +&qusb_phy_1 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&blsp1_spi1 { /* BLSP1 QUP1 */ + pinctrl-0 = <&spi_0_pins>; + pinctrl-names = "default"; + cs-gpios = <0>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "qcom,smem-part"; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 22 GPIO_ACTIVE_LOW>; + + qca8075_1: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + + qca8075_2: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_3: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_4: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + aqr113c: ethernet-phy@5 { + compatible ="ethernet-phy-ieee802.3-c45"; + reg = <8>; + reset-gpios = <&tlmm 43 GPIO_ACTIVE_LOW>; + }; +}; + +&sdhc_1 { + status = "okay"; + + vqmmc-supply = <&l11>; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x1e>; /* lan port bitmap */ + switch_wan_bmp = <0x60>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xe>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xd>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + port@1 { + port_id = <2>; + phy_address = <1>; + }; + port@2 { + port_id = <3>; + phy_address = <2>; + }; + port@3 { + port_id = <4>; + phy_address = <3>; + }; + port@4 { + port_id = <6>; + phy_address = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + ethernet-phy-ieee802.3-c45; + }; + }; +}; + +&edma { + status = "okay"; +}; + +/* Dummy LAN port */ +&dp1 { + status = "disabled"; + phy-handle = <&qca8075_1>; + label = "lan4"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan3"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan2"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_4>; + label = "lan1"; +}; + +&dp6_syn { + status = "okay"; + qcom,mactype = <1>; + phy-handle = <&aqr113c>; + label = "wan"; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00020000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; +}; + +&pcie_qmp1 { + status = "okay"; +}; + +&pcie1 { + status = "okay"; + + perst-gpio = <&tlmm 61 GPIO_ACTIVE_LOW>; + + bridge@1,0 { + reg = <0x00010000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + /* ath11k has no DT compatible for PCI cards */ + compatible = "pci17cb,1104"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath11k-calibration-variant = "prpl-Haze"; + }; + }; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "prpl-Haze"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts new file mode 100644 index 0000000000..5bfcdcc8ca --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts @@ -0,0 +1,191 @@ +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" + +#include +#include + +/ { + model = "Netgear WAX218"; + compatible = "netgear,wax218", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_power_amber; + led-failsafe = &led_power_amber; + led-running = &led_power_amber; + led-upgrade = &led_power_amber; + }; + + chosen { + stdout-path = "serial0:115200n8"; + /* + * Netgear's U-Boot adds "ubi.mtd=rootfs root=mtd:ubi_rootfs" + * That fails to create a UBI block device, so add it here. + */ + bootargs-append = " ubi.block=0,rootfs root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 52 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + led_spi { + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + sck-gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>; + mosi-gpios = <&tlmm 19 GPIO_ACTIVE_HIGH>; + + led_gpio: led_gpio@0 { + compatible = "fairchild,74hc595"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + registers-number = <1>; + enable-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>; + spi-max-frequency = <1000000>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_power_amber: led_power { + label = "amber:power"; + gpios = <&led_gpio 1 GPIO_ACTIVE_HIGH>; + }; + + led_lan { + label = "blue:lan"; + gpios = <&led_gpio 2 GPIO_ACTIVE_HIGH>; + }; + + led_wlan_2g { + label = "blue:wlan2g"; + gpios = <&led_gpio 3 GPIO_ACTIVE_HIGH>; + }; + + led_wlan_5g { + label = "blue:wlan5g"; + gpios = <&led_gpio 4 GPIO_ACTIVE_HIGH>; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; + switch_lan_bmp = <0x3e>; + switch_wan_bmp = <0x40>; + switch_mac_mode = <0x00>; + switch_mac_mode1 = <0xff>; + switch_mac_mode2 = <0x0f>; + bm_tick_mode = <0>; + tm_tick_mode = <0>; + + qcom,port_phyinfo { + port@5 { + port_id = <6>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&mdio { + status = "okay"; + + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + + qca8081_28: ethernet-phy@28 { + reg = <28>; + }; +}; + +&dp6_syn { + status = "okay"; + phy-handle = <&qca8081_28>; + label = "lan"; + nvmem-cells = <&macaddr_ubootenv_ethaddr>; + nvmem-cell-names = "mac-address"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "qcom,smem-part"; + + partition-0-appsblenv { + compatible = "fixed-partitions"; + label = "0:appsblenv"; + read-only; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + compatible = "u-boot,env"; + label = "env-data"; + reg = <0x0 0x40000>; + + macaddr_ubootenv_ethaddr: ethaddr {}; + }; + }; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Netgear-WAX218"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts new file mode 100644 index 0000000000..9779070725 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* Copyright 2023 Nokia */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "Compex WPQ873"; + compatible = "compex,wpq873", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_power_blue; + led-failsafe = &led_power_red; + led-running = &led_system_green; + led-upgrade = &led_system_blue; + /* Aliases as required by u-boot to patch MAC addresses */ + ethernet0 = &dp6; + ethernet1 = &dp2; + ethernet2 = &dp3; + ethernet3 = &dp4; + label-mac-device = &dp6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " root=/dev/ubiblock0_1"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&tlmm 21 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_power_red: power-red { + label = "red:power"; + gpios = <&tlmm 42 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_power_blue: power-blue { + label = "blue:power"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_red: system-red { + label = "red:system"; + gpios = <&tlmm 28 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_green: system-green { + label = "green:system"; + gpios = <&tlmm 18 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_system_blue: system-blue { + label = "blue:system"; + gpios = <&tlmm 19 GPIO_ACTIVE_HIGH>; + color = ; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + i2c_pins: i2c-pins { + pins = "gpio0", "gpio2"; + function = "blsp5_i2c"; + drive-strength = <8>; + bias-disable; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&blsp1_i2c6 { + status = "okay"; + + pinctrl-0 = <&i2c_pins>; + pinctrl-names = "default"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&blsp1_spi1 { + status = "okay"; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + button_pins: button_pins { + reset_button { + pins = "gpio66"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_spi1 { + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsblenv"; + reg = <0x480000 0x10000>; + }; + + partition@490000 { + label = "0:appsbl"; + reg = <0x490000 0xa0000>; + read-only; + }; + + partition@550000 { + label = "0:appsbl_1"; + reg = <0x530000 0xa0000>; + read-only; + }; + + partition@610000 { + label = "0:art"; + reg = <0x5d0000 0x40000>; + read-only; + }; + + partition@650000 { + label = "0:ethphyfw"; + reg = <0x610000 0x80000>; + read-only; + }; + }; + }; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "rootfs"; + reg = <0x0000000 0x3400000>; + }; + + partition@3400000 { + label = "0:wififw"; + reg = <0x3400000 0x800000>; + read-only; + }; + + partition@3c00000 { + label = "rootfs_1"; + reg = <0x3c00000 0x3400000>; + }; + + partition@7000000 { + label = "0:wififw_1"; + reg = <0x7000000 0x800000>; + read-only; + }; + }; + }; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&qusb_phy_1 { + status = "okay"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&ssphy_1 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + + +&mdio { + status = "okay"; + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_2: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_3: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8081: ethernet-phy@28 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <28>; + reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>; + }; + +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x3e>; /* lan port bitmap */ + switch_wan_bmp = <0x40>; /* wan port bitmap */ + switch_mac_mode = <0x0>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0x0f>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0x0f>; /* mac mode for uniphy instance2*/ + bm_tick_mode = <0>; /* bm tick mode */ + tm_tick_mode = <0>; /* tm tick mode */ + + qcom,port_phyinfo { + port@2 { + port_id = <2>; + phy_address = <1>; + }; + port@3 { + port_id = <3>; + phy_address = <2>; + }; + port@4 { + port_id = <4>; + phy_address = <3>; + }; + port@5 { + port_id = <6>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_1>; + label = "lan1"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan2"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan3"; +}; + +&dp6 { + status = "okay"; + phy-handle = <&qca8081>; + label = "wan"; +}; + +&pcie_qmp0 { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpio = <&tlmm 58 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00020000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; +}; + +&pcie_qmp1 { + status = "okay"; +}; + +&pcie1 { + status = "okay"; + + perst-gpio = <&tlmm 62 GPIO_ACTIVE_HIGH>; + + bridge@1,0 { + reg = <0x00010000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Compex-WPQ873"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi new file mode 100644 index 0000000000..dace4008b3 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "ipq8074.dtsi" + +&tzapp_region { + reg = <0x0 0x4a400000 0x0 0x100000>; +}; + +&q6_region { + reg = <0x0 0x4b000000 0x0 0x3700000>; +}; + +&q6_etr_region { + reg = <0x0 0x4e700000 0x0 0x100000>; +}; + +&m3_dump_region { + reg = <0x0 0x4e800000 0x0 0x100000>; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi new file mode 100644 index 0000000000..057d592031 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-ac-cpu.dtsi @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "ipq8074-cpr-regulator.dtsi" + +&CPU0 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; + +&CPU1 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; + +&CPU2 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; + +&CPU3 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; +&cpu0_thermal { + trips { + cpu0_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu0_passive>; + 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>; + }; + }; +}; + +&cpu1_thermal { + trips { + cpu1_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu1_passive>; + 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>; + }; + }; +}; + +&cpu2_thermal { + trips { + cpu2_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu2_passive>; + 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>; + }; + }; +}; + +&cpu3_thermal { + trips { + cpu3_passive: cpu-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu3_passive>; + 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>; + }; + }; +}; + +&cluster_thermal { + trips { + cluster_passive: cluster-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cluster_passive>; + 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/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi new file mode 100644 index 0000000000..e351a2e759 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-cpr-regulator.dtsi @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "pmp8074.dtsi" + +&soc { + apc_apm: apm@b111000 { + compatible = "qcom,ipq807x-apm"; + reg = <0xb111000 0x1000>; + reg-names = "pm-apcc-glb"; + qcom,apm-post-halt-delay = <0x2>; + qcom,apm-halt-clk-delay = <0x11>; + qcom,apm-resume-clk-delay = <0x10>; + qcom,apm-sel-switch-delay = <0x01>; + }; + + apc_cpr: cpr4-ctrl@b018000 { + compatible = "qcom,cpr4-ipq807x-apss-regulator"; + reg = <0xb018000 0x4000>, <0xa4000 0x1000>, <0x0193d008 0x4>; + reg-names = "cpr_ctrl", "fuse_base", "cpr_tcsr_reg"; + interrupts = ; + interrupt-names = "cpr"; + qcom,cpr-ctrl-name = "apc"; + qcom,cpr-sensor-time = <1000>; + qcom,cpr-loop-time = <5000000>; + qcom,cpr-idle-cycles = <15>; + qcom,cpr-step-quot-init-min = <12>; + qcom,cpr-step-quot-init-max = <14>; + qcom,cpr-count-mode = <0>; /* All-at-once */ + qcom,cpr-count-repeat = <14>; + qcom,cpr-down-error-step-limit = <1>; + qcom,cpr-up-error-step-limit = <1>; + qcom,apm-ctrl = <&apc_apm>; + qcom,apm-threshold-voltage = <848000>; + vdd-supply = <&s3>; + qcom,voltage-step = <8000>; + + thread@0 { + qcom,cpr-thread-id = <0>; + qcom,cpr-consecutive-up = <0>; + qcom,cpr-consecutive-down = <0>; + qcom,cpr-up-threshold = <4>; + qcom,cpr-down-threshold = <1>; + + apc_vreg: regulator { + regulator-name = "apc_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <6>; + qcom,cpr-part-types = <2>; + qcom,cpr-parts-voltage = <1048000>; + qcom,cpr-parts-voltage-v2 = <992000>; + qcom,cpr-fuse-corners = <4>; + qcom,cpr-fuse-combos = <8>; + qcom,cpr-corners = <6>; + qcom,cpr-speed-bins = <1>; + qcom,cpr-speed-bin-corners = <6>; + qcom,cpr-corner-fmax-map = <1 3 5 6>; + qcom,allow-voltage-interpolation; + qcom,allow-quotient-interpolation; + qcom,cpr-scaled-open-loop-voltage-as-ceiling; + qcom,cpr-voltage-ceiling = + <840000 904000 944000 + 984000 992000 1064000>; + qcom,cpr-voltage-floor = + <592000 648000 712000 + 744000 784000 848000>; + qcom,corner-frequencies = + <1017600000 1382400000 1651200000 + 1843200000 1920000000 2208000000>; + + /* TT/FF parts i.e. turbo L1 OL voltage < 1048 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-0 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 12000>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* SS parts i.e turbo L1 OL voltage >= 1048 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-1 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 20000 26000 0 20000>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - FF parts i.e. turbo L1 OL voltage < 992 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-0 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - SS/TT parts i.e turbo L1 OL voltage >= 992 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-1 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 7000 36000 4000>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - FF parts i.e. turbo L1 OL voltage < 992 mV */ + qcom,cpr-closed-loop-voltage-adjustment-v2-0 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + /* v2 - SS/TT parts i.e turbo L1 OL voltage >= 992 mV */ + qcom,cpr-closed-loop-voltage-adjustment-v2-1 = + /* Speed bin 0; CPR rev 0..7 */ + < 0 0 0 0>, + < 0 0 19000 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>, + < 0 0 0 0>; + + qcom,cpr-ro-scaling-factor = + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >, + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >, + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >, + < 3970 4150 0 2280 2520 2470 2250 2280 + 2390 2330 2530 2500 850 2900 2510 2170 >; + + qcom,cpr-floor-to-ceiling-max-range = + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>, + < 40000 40000 40000 40000 40000 40000>; + regulator-always-on; + }; + }; + }; + + npu_cpr: npu-cpr { + compatible = "qcom,cpr3-ipq807x-npu-regulator"; + reg = <0xa4000 0x1000>, <0x0193d008 0x4>; + reg-names = "fuse_base", "cpr_tcsr_reg"; + qcom,cpr-ctrl-name = "npu"; + vdd-supply = <&s4>; + qcom,voltage-step = <8000>; + thread@0 { + qcom,cpr-thread-id = <0>; + qcom,cpr-consecutive-up = <0>; + qcom,cpr-consecutive-down = <2>; + qcom,cpr-up-threshold = <2>; + qcom,cpr-down-threshold = <1>; + + npu_vreg: regulator { + regulator-name = "npu_corner"; + regulator-min-microvolt = <1>; + regulator-max-microvolt = <3>; + qcom,cpr-part-types = <2>; + qcom,cpr-parts-voltage = <968000>; + qcom,cpr-parts-voltage-v2 = <832001>; + qcom,cpr-cold-temp-threshold-v2 = <30>; + qcom,cpr-fuse-corners = <2>; + qcom,cpr-fuse-combos = <1>; + qcom,cpr-corners = <2>; + qcom,cpr-speed-bins = <1>; + qcom,cpr-speed-bin-corners = <2>; + qcom,allow-voltage-interpolation; + qcom,cpr-corner-fmax-map = <1 2>; + qcom,cpr-voltage-ceiling = + <912000 992000>; + qcom,cpr-voltage-floor = + <752000 792000>; + qcom,corner-frequencies = + <1497600000 1689600000>; + + /* TT/FF parts i.e. turbo OL voltage < 968 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-0 = + < 40000 40000>; + + /* SS parts i.e turbo OL voltage >= 968 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-1 = + < 24000 24000>; + + /* FF parts i.e. turbo OL voltage <= 832 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-0= + <40000 40000>; + + /* TT/SS parts i.e turbo OL voltage > 832 mV */ + qcom,cpr-open-loop-voltage-fuse-adjustment-v2-1= + <40000 40000>; + + /* FF parts i.e. turbo OL voltage <= 832 mV */ + qcom,cpr-cold-temp-voltage-adjustment-v2-0 = + <0 0>; + + /* TT/SS parts i.e turbo OL voltage > 832 mV */ + qcom,cpr-cold-temp-voltage-adjustment-v2-1 = + <35000 27000>; + }; + }; + }; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi new file mode 100644 index 0000000000..129266c50d --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-ess.dtsi @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-2.0-only + +&clocks { + bias_pll_cc_clk { + compatible = "fixed-clock"; + clock-frequency = <300000000>; + #clock-cells = <0>; + }; + + bias_pll_nss_noc_clk { + compatible = "fixed-clock"; + clock-frequency = <416500000>; + #clock-cells = <0>; + }; +}; + +&soc { + switch: ess-switch@3a000000 { + compatible = "qcom,ess-switch-ipq807x"; + reg = <0x3a000000 0x1000000>; + switch_access_mode = "local bus"; + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_inner_bmp = <0x80>; /*inner port bitmap*/ + clocks = <&gcc GCC_CMN_12GPLL_AHB_CLK>, + <&gcc GCC_CMN_12GPLL_SYS_CLK>, + <&gcc GCC_UNIPHY0_AHB_CLK>, + <&gcc GCC_UNIPHY0_SYS_CLK>, + <&gcc GCC_UNIPHY1_AHB_CLK>, + <&gcc GCC_UNIPHY1_SYS_CLK>, + <&gcc GCC_UNIPHY2_AHB_CLK>, + <&gcc GCC_UNIPHY2_SYS_CLK>, + <&gcc GCC_PORT1_MAC_CLK>, + <&gcc GCC_PORT2_MAC_CLK>, + <&gcc GCC_PORT3_MAC_CLK>, + <&gcc GCC_PORT4_MAC_CLK>, + <&gcc GCC_PORT5_MAC_CLK>, + <&gcc GCC_PORT6_MAC_CLK>, + <&gcc GCC_NSS_PPE_CLK>, + <&gcc GCC_NSS_PPE_CFG_CLK>, + <&gcc GCC_NSSNOC_PPE_CLK>, + <&gcc GCC_NSSNOC_PPE_CFG_CLK>, + <&gcc GCC_NSS_EDMA_CLK>, + <&gcc GCC_NSS_EDMA_CFG_CLK>, + <&gcc GCC_NSS_PPE_IPE_CLK>, + <&gcc GCC_NSS_PPE_BTQ_CLK>, + <&gcc GCC_MDIO_AHB_CLK>, + <&gcc GCC_NSS_NOC_CLK>, + <&gcc GCC_NSSNOC_SNOC_CLK>, + <&gcc GCC_MEM_NOC_NSS_AXI_CLK>, + <&gcc GCC_NSS_CRYPTO_CLK>, + <&gcc GCC_NSS_IMEM_CLK>, + <&gcc GCC_NSS_PTP_REF_CLK>, + <&gcc GCC_NSS_PORT1_RX_CLK>, + <&gcc GCC_NSS_PORT1_TX_CLK>, + <&gcc GCC_NSS_PORT2_RX_CLK>, + <&gcc GCC_NSS_PORT2_TX_CLK>, + <&gcc GCC_NSS_PORT3_RX_CLK>, + <&gcc GCC_NSS_PORT3_TX_CLK>, + <&gcc GCC_NSS_PORT4_RX_CLK>, + <&gcc GCC_NSS_PORT4_TX_CLK>, + <&gcc GCC_NSS_PORT5_RX_CLK>, + <&gcc GCC_NSS_PORT5_TX_CLK>, + <&gcc GCC_NSS_PORT6_RX_CLK>, + <&gcc GCC_NSS_PORT6_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT1_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT1_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT2_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT2_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT3_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT3_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT4_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT4_TX_CLK>, + <&gcc GCC_UNIPHY0_PORT5_RX_CLK>, + <&gcc GCC_UNIPHY0_PORT5_TX_CLK>, + <&gcc GCC_UNIPHY1_PORT5_RX_CLK>, + <&gcc GCC_UNIPHY1_PORT5_TX_CLK>, + <&gcc GCC_UNIPHY2_PORT6_RX_CLK>, + <&gcc GCC_UNIPHY2_PORT6_TX_CLK>, + <&gcc NSS_PORT5_RX_CLK_SRC>, + <&gcc NSS_PORT5_TX_CLK_SRC>; + clock-names = "cmn_ahb_clk", "cmn_sys_clk", + "uniphy0_ahb_clk", "uniphy0_sys_clk", + "uniphy1_ahb_clk", "uniphy1_sys_clk", + "uniphy2_ahb_clk", "uniphy2_sys_clk", + "port1_mac_clk", "port2_mac_clk", + "port3_mac_clk", "port4_mac_clk", + "port5_mac_clk", "port6_mac_clk", + "nss_ppe_clk", "nss_ppe_cfg_clk", + "nssnoc_ppe_clk", "nssnoc_ppe_cfg_clk", + "nss_edma_clk", "nss_edma_cfg_clk", + "nss_ppe_ipe_clk", "nss_ppe_btq_clk", + "gcc_mdio_ahb_clk", "gcc_nss_noc_clk", + "gcc_nssnoc_snoc_clk", + "gcc_mem_noc_nss_axi_clk", + "gcc_nss_crypto_clk", + "gcc_nss_imem_clk", + "gcc_nss_ptp_ref_clk", + "nss_port1_rx_clk", "nss_port1_tx_clk", + "nss_port2_rx_clk", "nss_port2_tx_clk", + "nss_port3_rx_clk", "nss_port3_tx_clk", + "nss_port4_rx_clk", "nss_port4_tx_clk", + "nss_port5_rx_clk", "nss_port5_tx_clk", + "nss_port6_rx_clk", "nss_port6_tx_clk", + "uniphy0_port1_rx_clk", + "uniphy0_port1_tx_clk", + "uniphy0_port2_rx_clk", + "uniphy0_port2_tx_clk", + "uniphy0_port3_rx_clk", + "uniphy0_port3_tx_clk", + "uniphy0_port4_rx_clk", + "uniphy0_port4_tx_clk", + "uniphy0_port5_rx_clk", + "uniphy0_port5_tx_clk", + "uniphy1_port5_rx_clk", + "uniphy1_port5_tx_clk", + "uniphy2_port6_rx_clk", + "uniphy2_port6_tx_clk", + "nss_port5_rx_clk_src", + "nss_port5_tx_clk_src"; + resets = <&gcc GCC_PPE_FULL_RESET>, + <&gcc GCC_UNIPHY0_SOFT_RESET>, + <&gcc GCC_UNIPHY0_XPCS_RESET>, + <&gcc GCC_UNIPHY1_SOFT_RESET>, + <&gcc GCC_UNIPHY1_XPCS_RESET>, + <&gcc GCC_UNIPHY2_SOFT_RESET>, + <&gcc GCC_UNIPHY2_XPCS_RESET>, + <&gcc GCC_NSSPORT1_RESET>, + <&gcc GCC_NSSPORT2_RESET>, + <&gcc GCC_NSSPORT3_RESET>, + <&gcc GCC_NSSPORT4_RESET>, + <&gcc GCC_NSSPORT5_RESET>, + <&gcc GCC_NSSPORT6_RESET>; + reset-names = "ppe_rst", "uniphy0_soft_rst", + "uniphy0_xpcs_rst", "uniphy1_soft_rst", + "uniphy1_xpcs_rst", "uniphy2_soft_rst", + "uniphy2_xpcs_rst", "nss_port1_rst", + "nss_port2_rst", "nss_port3_rst", + "nss_port4_rst", "nss_port5_rst", + "nss_port6_rst"; + mdio-bus = <&mdio>; + status = "disabled"; + + port_scheduler_resource { + port@0 { + port_id = <0>; + ucast_queue = <0 143>; + mcast_queue = <256 271>; + l0sp = <0 35>; + l0cdrr = <0 47>; + l0edrr = <0 47>; + l1cdrr = <0 7>; + l1edrr = <0 7>; + }; + port@1 { + port_id = <1>; + ucast_queue = <144 159>; + mcast_queue = <272 275>; + l0sp = <36 39>; + l0cdrr = <48 63>; + l0edrr = <48 63>; + l1cdrr = <8 11>; + l1edrr = <8 11>; + }; + port@2 { + port_id = <2>; + ucast_queue = <160 175>; + mcast_queue = <276 279>; + l0sp = <40 43>; + l0cdrr = <64 79>; + l0edrr = <64 79>; + l1cdrr = <12 15>; + l1edrr = <12 15>; + }; + port@3 { + port_id = <3>; + ucast_queue = <176 191>; + mcast_queue = <280 283>; + l0sp = <44 47>; + l0cdrr = <80 95>; + l0edrr = <80 95>; + l1cdrr = <16 19>; + l1edrr = <16 19>; + }; + port@4 { + port_id = <4>; + ucast_queue = <192 207>; + mcast_queue = <284 287>; + l0sp = <48 51>; + l0cdrr = <96 111>; + l0edrr = <96 111>; + l1cdrr = <20 23>; + l1edrr = <20 23>; + }; + port@5 { + port_id = <5>; + ucast_queue = <208 223>; + mcast_queue = <288 291>; + l0sp = <52 55>; + l0cdrr = <112 127>; + l0edrr = <112 127>; + l1cdrr = <24 27>; + l1edrr = <24 27>; + }; + port@6 { + port_id = <6>; + ucast_queue = <224 239>; + mcast_queue = <292 295>; + l0sp = <56 59>; + l0cdrr = <128 143>; + l0edrr = <128 143>; + l1cdrr = <28 31>; + l1edrr = <28 31>; + }; + port@7 { + port_id = <7>; + ucast_queue = <240 255>; + mcast_queue = <296 299>; + l0sp = <60 63>; + l0cdrr = <144 159>; + l0edrr = <144 159>; + l1cdrr = <32 35>; + l1edrr = <32 35>; + }; + }; + port_scheduler_config { + port@0 { + port_id = <0>; + l1scheduler { + group@0 { + sp = <0 1>; /*L0 SPs*/ + /*cpri cdrr epri edrr*/ + cfg = <0 0 0 0>; + }; + }; + l0scheduler { + group@0 { + /*unicast queues*/ + ucast_queue = <0 4 8>; + /*multicast queues*/ + mcast_queue = <256 260>; + /*sp cpri cdrr epri edrr*/ + cfg = <0 0 0 0 0>; + }; + group@1 { + ucast_queue = <1 5 9>; + mcast_queue = <257 261>; + cfg = <0 1 1 1 1>; + }; + group@2 { + ucast_queue = <2 6 10>; + mcast_queue = <258 262>; + cfg = <0 2 2 2 2>; + }; + group@3 { + ucast_queue = <3 7 11>; + mcast_queue = <259 263>; + cfg = <0 3 3 3 3>; + }; + }; + }; + port@1 { + port_id = <1>; + l1scheduler { + group@0 { + sp = <36>; + cfg = <0 8 0 8>; + }; + group@1 { + sp = <37>; + cfg = <1 9 1 9>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <144>; + ucast_loop_pri = <16>; + mcast_queue = <272>; + mcast_loop_pri = <4>; + cfg = <36 0 48 0 48>; + }; + }; + }; + port@2 { + port_id = <2>; + l1scheduler { + group@0 { + sp = <40>; + cfg = <0 12 0 12>; + }; + group@1 { + sp = <41>; + cfg = <1 13 1 13>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <160>; + ucast_loop_pri = <16>; + mcast_queue = <276>; + mcast_loop_pri = <4>; + cfg = <40 0 64 0 64>; + }; + }; + }; + port@3 { + port_id = <3>; + l1scheduler { + group@0 { + sp = <44>; + cfg = <0 16 0 16>; + }; + group@1 { + sp = <45>; + cfg = <1 17 1 17>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <176>; + ucast_loop_pri = <16>; + mcast_queue = <280>; + mcast_loop_pri = <4>; + cfg = <44 0 80 0 80>; + }; + }; + }; + port@4 { + port_id = <4>; + l1scheduler { + group@0 { + sp = <48>; + cfg = <0 20 0 20>; + }; + group@1 { + sp = <49>; + cfg = <1 21 1 21>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <192>; + ucast_loop_pri = <16>; + mcast_queue = <284>; + mcast_loop_pri = <4>; + cfg = <48 0 96 0 96>; + }; + }; + }; + port@5 { + port_id = <5>; + l1scheduler { + group@0 { + sp = <52>; + cfg = <0 24 0 24>; + }; + group@1 { + sp = <53>; + cfg = <1 25 1 25>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <208>; + ucast_loop_pri = <16>; + mcast_queue = <288>; + mcast_loop_pri = <4>; + cfg = <52 0 112 0 112>; + }; + }; + }; + port@6 { + port_id = <6>; + l1scheduler { + group@0 { + sp = <56>; + cfg = <0 28 0 28>; + }; + group@1 { + sp = <57>; + cfg = <1 29 1 29>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <224>; + ucast_loop_pri = <16>; + mcast_queue = <292>; + mcast_loop_pri = <4>; + cfg = <56 0 128 0 128>; + }; + }; + }; + port@7 { + port_id = <7>; + l1scheduler { + group@0 { + sp = <60>; + cfg = <0 32 0 32>; + }; + group@1 { + sp = <61>; + cfg = <1 33 1 33>; + }; + }; + l0scheduler { + group@0 { + ucast_queue = <240>; + ucast_loop_pri = <16>; + mcast_queue = <296>; + cfg = <60 0 144 0 144>; + }; + }; + }; + }; + }; + + ess-uniphy@7a00000 { + compatible = "qcom,ess-uniphy"; + reg = <0x7a00000 0x30000>; + uniphy_access_mode = "local bus"; + }; + + edma: edma@3ab00000 { + compatible = "qcom,edma"; + reg = <0x3ab00000 0x76900>; + reg-names = "edma-reg-base"; + qcom,txdesc-ring-start = <23>; + qcom,txdesc-rings = <1>; + qcom,txcmpl-ring-start = <7>; + qcom,txcmpl-rings = <1>; + qcom,rxfill-ring-start = <7>; + qcom,rxfill-rings = <1>; + qcom,rxdesc-ring-start = <15>; + qcom,rxdesc-rings = <1>; + interrupts = , + , + , + ; + resets = <&gcc GCC_EDMA_HW_RESET>; + reset-names = "edma_rst"; + status = "disabled"; + }; + + dp1: dp1 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <1>; + reg = <0x3a001000 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp2: dp2 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <2>; + reg = <0x3a001200 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp3: dp3 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <3>; + reg = <0x3a001400 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp4: dp4 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <4>; + reg = <0x3a001600 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp5: dp5 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a001800 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp6: dp6 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <6>; + reg = <0x3a001a00 0x200>; + qcom,mactype = <0>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp5_syn: dp5-syn { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <5>; + reg = <0x3a003000 0x3fff>; + qcom,mactype = <1>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; + + dp6_syn: dp6-syn { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <6>; + reg = <0x3a007000 0x3fff>; + qcom,mactype = <1>; + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi new file mode 100644 index 0000000000..37af7bbc1f --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-hk-cpu.dtsi @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "ipq8074-cpr-regulator.dtsi" + +&CPU0 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; + +&CPU1 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; + +&CPU2 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; + +&CPU3 { + cpu-supply = <&apc_vreg>; + voltage-tolerance = <1>; +}; +&cpu0_thermal { + trips { + cpu0_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu0_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu0_passive_low>; + 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>; + }; + map1 { + trip = <&cpu0_passive_high>; + 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>; + }; + }; +}; + +&cpu1_thermal { + trips { + cpu1_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu1_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu1_passive_low>; + 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>; + }; + map1 { + trip = <&cpu1_passive_high>; + 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>; + }; + }; +}; + +&cpu2_thermal { + trips { + cpu2_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu2_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu2_passive_low>; + 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>; + }; + map1 { + trip = <&cpu2_passive_high>; + 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>; + }; + }; +}; + +&cpu3_thermal { + trips { + cpu3_passive_low: cpu-passive-low { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu3_passive_high: cpu-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu3_passive_low>; + 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>; + }; + map1 { + trip = <&cpu3_passive_high>; + 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>; + }; + }; +}; + +&cluster_thermal { + trips { + cluster_passive_low: cluster-passive { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cluster_passive_high: cluster-passive-high { + temperature = <100000>; + hysteresis = <2000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cluster_passive_low>; + 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>; + }; + map1 { + trip = <&cluster_passive_high>; + 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/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts new file mode 100644 index 0000000000..d113b233ec --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/* + * Copyright (c) 2022, Karol Przybylski + * Copyright (c) 2023, Andre Valentin + */ + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + + +/ { + model = "Zyxel NBG7815"; + compatible = "zyxel,nbg7815", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + serial1 = &blsp1_uart3; + /* Alias as required by u-boot to patch MAC addresses */ + ethernet0 = &dp1; + label-mac-device = &dp1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = ; + gpios = <&tlmm 54 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + + +&blsp1_uart3 { + status = "okay"; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + + +&blsp1_spi1 { + pinctrl-0 = <&spi_0_pins>; + pinctrl-names = "default"; + cs-gpios = <0>; + status = "okay"; + + /* + * Bootloader will find the NAND DT node by the compatible and + * then "fixup" it by adding the partitions from the SMEM table + * using the legacy bindings thus making it impossible for us + * to change the partition table or utilize NVMEM for calibration. + * So add a dummy partitions node that bootloader will populate + * and set it as disabled so the kernel ignores it instead of + * printing warnings due to the broken way bootloader adds the + * partitions. + */ + partitions { + status = "disabled"; + }; + + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + compatible = "jedec,spi-nor"; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:sbl1"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@50000 { + label = "0:mibib"; + reg = <0x50000 0x10000>; + read-only; + }; + + partition@60000 { + label = "0:bootconfig"; + reg = <0x60000 0x20000>; + read-only; + }; + + partition@80000 { + label = "0:bootconfig1"; + reg = <0x80000 0x20000>; + read-only; + }; + + partition@a0000 { + label = "0:qsee"; + reg = <0xa0000 0x180000>; + read-only; + }; + + partition@220000 { + label = "0:qsee_1"; + reg = <0x220000 0x180000>; + read-only; + }; + + partition@3a0000 { + label = "0:devcfg"; + reg = <0x3a0000 0x10000>; + read-only; + }; + + partition@3b0000 { + label = "0:devcfg_1"; + reg = <0x3b0000 0x10000>; + read-only; + }; + + partition@3c0000 { + label = "0:apdp"; + reg = <0x3c0000 0x10000>; + read-only; + }; + + partition@3d0000 { + label = "0:apdp_1"; + reg = <0x3d0000 0x10000>; + read-only; + }; + + partition@3e0000 { + label = "0:rpm"; + reg = <0x3e0000 0x40000>; + read-only; + }; + + partition@420000 { + label = "0:rpm_1"; + reg = <0x420000 0x40000>; + read-only; + }; + + partition@460000 { + label = "0:cdt"; + reg = <0x460000 0x10000>; + read-only; + }; + + partition@470000 { + label = "0:cdt_1"; + reg = <0x470000 0x10000>; + read-only; + }; + + partition@480000 { + label = "0:appsbl"; + reg = <0x480000 0xc0000>; + read-only; + }; + + partition@540000 { + label = "0:appsbl_1"; + reg = <0x540000 0xc0000>; + read-only; + }; + + partition@600000 { + compatible = "u-boot,env"; + label = "0:appsblenv"; + reg = <0x600000 0x10000>; + + macaddr_lan: ethaddr { + #nvmem-cell-cells = <1>; + }; + }; + + partition@610000 { + label = "0:art"; + reg = <0x610000 0x40000>; + read-only; + }; + + partition@650000 { + label = "0:ethphyfw"; + reg = <0x650000 0x80000>; + read-only; + }; + + partition@6d0000 { + label = "0:crt"; + reg = <0x6d0000 0x10000>; + read-only; + }; + + partition@6e0000 { + label = "dual_flag"; + reg = <0x6e0000 0x10000>; + }; + + partition@6f0000 { + label = "reserved"; + reg = <0x6f0000 0x110000>; + read-only; + }; + }; + }; +}; + +&mdio { + status = "okay"; + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>; + + qca8075_1: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + + qca8075_2: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; + + qca8075_3: ethernet-phy@2 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <2>; + }; + + qca8075_4: ethernet-phy@3 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <3>; + }; + + qca8081: ethernet-phy@4{ + compatible = "ethernet-phy-id004d.d101"; + reg = <28>; + reset-gpios = <&tlmm 31 GPIO_ACTIVE_LOW>; + }; + + aqr113c: ethernet-phy@5 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <8>; + reset-gpios = <&tlmm 63 GPIO_ACTIVE_LOW>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; + switch_lan_bmp = <0x3e>; + switch_wan_bmp = <0x40>; + switch_mac_mode = <0x0>; + switch_mac_mode1 = <0xf>; + switch_mac_mode2 = <0xd>; + bm_tick_mode = <0>; + tm_tick_mode = <0>; + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0>; + }; + + port@1 { + port_id = <2>; + phy_address = <1>; + }; + + port@2 { + port_id = <3>; + phy_address = <2>; + }; + + port@3 { + port_id = <4>; + phy_address = <3>; + }; + + port@4 { + port_id = <5>; + phy_address = <28>; + port_mac_sel = "QGMAC_PORT"; + }; + + port@5 { + port_id = <6>; + ethernet-phy-ieee802.3-c45; + phy_address = <8>; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp1 { + status = "okay"; + phy-handle = <&qca8075_1>; + label = "lan1"; + nvmem-cells = <&macaddr_lan 0>; + nvmem-cell-names = "mac-address"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan2"; + nvmem-cells = <&macaddr_lan 0>; + nvmem-cell-names = "mac-address"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan3"; + nvmem-cells = <&macaddr_lan 0>; + nvmem-cell-names = "mac-address"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_4>; + label = "lan4"; + nvmem-cells = <&macaddr_lan 0>; + nvmem-cell-names = "mac-address"; +}; + +&dp5 { + status = "okay"; + phy-handle = <&qca8081>; + label = "wan"; + nvmem-cells = <&macaddr_lan 1>; + nvmem-cell-names = "mac-address"; +}; + +&dp6_syn { + status = "okay"; + phy-handle = <&aqr113c>; + label = "10g"; + nvmem-cells = <&macaddr_lan 0>; + nvmem-cell-names = "mac-address"; +}; + +&blsp1_i2c2 { + pinctrl-0 = <&i2c_0_pins>; + pinctrl-names = "default"; + status = "okay"; + + tmp103@70 { + compatible = "ti,tmp103"; + reg = <0x70>; + }; +}; + +&sdhc_1 { + status = "okay"; + /* unstable, problem with the hs400 > h200 speed switch */ + /delete-property/ mmc-hs400-1_8v; + mmc-hs200-1_8v; + mmc-ddr-1_8v; + vqmmc-supply = <&l11>; +}; + +&ssphy_0 { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&ssphy_1 { + status = "okay"; +}; + +&qusb_phy_1 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Zyxel-NBG7815"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts new file mode 100644 index 0000000000..32386dc93e --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; + +#include "ipq8074.dtsi" +#include "ipq8074-hk-cpu.dtsi" +#include "ipq8074-ess.dtsi" +#include +#include +#include + +/ { + model = "Buffalo WXR-5950AX12"; + compatible = "buffalo,wxr-5950ax12", "qcom,ipq8074"; + + aliases { + serial0 = &blsp1_uart5; + led-boot = &led_power_white; + led-failsafe = &led_power_red; + led-running = &led_power_white; + led-upgrade = &led_power_white; + label-mac-device = &dp5_syn; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-append = " ubi.mtd=user_property root=/dev/ubiblock1_0"; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + label = "white:router"; + gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-1 { + label = "red:router"; + gpios = <&tlmm 22 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led_power_red: led-2 { + label = "red:power"; + gpios = <&tlmm 31 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_POWER; + }; + + led_power_white: led-3 { + label = "white:power"; + gpios = <&tlmm 34 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_POWER; + }; + + led-4 { + label = "white:internet"; + gpios = <&tlmm 43 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-5 { + label = "red:internet"; + gpios = <&tlmm 44 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led-6 { + label = "red:wireless"; + gpios = <&tlmm 55 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_WLAN; + }; + + led-7 { + label = "white:wireless"; + gpios = <&tlmm 56 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_WLAN; + }; + }; + + keys { + compatible = "gpio-keys"; + + /* + * mode: 3x position switch + * + * - ROUTER + * - AP + * - WB (Wireless Bridge) + */ + ap { + label = "mode-ap"; + gpios = <&tlmm 29 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + bridge { + label = "mode-wb"; + gpios = <&tlmm 30 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + /* + * op: 2x position switch + * + * - AUTO + * - MANUAL (select Router/AP/WB manually) + */ + manual { + label = "op-manual"; + gpios = <&tlmm 52 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&tlmm 51 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&tlmm 54 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + reg_usb_vbus: regulator-5v-vbus { + compatible = "regulator-fixed"; + regulator-name = "vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&tlmm 64 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; +}; + +&tlmm { + mdio_pins: mdio-pins { + mdc { + pins = "gpio68"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio { + pins = "gpio69"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; +}; + +&blsp1_uart5 { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&crypto { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + status = "okay"; + + nand@0 { + reg = <0>; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "qcom,smem-part"; + + partition-0-appsblenv { + compatible = "fixed-partitions"; + label = "0:appsblenv"; + read-only; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + compatible = "u-boot,env"; + label = "env-data"; + reg = <0x0 0x40000>; + + macaddr_appsblenv_ethaddr: ethaddr { + }; + }; + }; + }; + }; +}; + +&mdio { + status = "okay"; + pinctrl-0 = <&mdio_pins>; + pinctrl-names = "default"; + /* + * RESET pins of phy chips + * + * WXR-5950AX12 has 2x RESET pins for QCA8075 and AQR113C. + * The pin of QCA8075 is for the chip and not phys in the chip, the + * pin of AQR113C is for 2x chips. So both pins are not appropriate + * to declare them as reset-gpios in phy nodes. + * Multiple entries in reset-gpios of mdio may not be supported, but + * leave the following as-is to show that the those reset pin exists. + */ + reset-gpios = <&tlmm 37 GPIO_ACTIVE_LOW>, /* QCA8075 RESET */ + <&tlmm 63 GPIO_ACTIVE_LOW>; /* AQR113C RESET (2x) */ + + aqr113c_1: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x0>; + }; + + aqr113c_2: ethernet-phy@8 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0x8>; + }; + + qca8075_1: ethernet-phy@18 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x18>; + }; + + qca8075_2: ethernet-phy@19 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x19>; + }; + + qca8075_3: ethernet-phy@1a { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1a>; + }; + + qca8075_4: ethernet-phy@1b { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1b>; + }; + + qca8075_5: ethernet-phy@1c { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x1c>; + }; +}; + +&switch { + status = "okay"; + + switch_cpu_bmp = <0x1>; + switch_lan_bmp = <0x3e>; + switch_wan_bmp = <0x40>; + switch_mac_mode = <0xb>; + switch_mac_mode1 = <0xd>; + switch_mac_mode2 = <0xd>; + bm_tick_mode = <0>; + tm_tick_mode = <0>; + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <0x18>; + }; + + port@1 { + port_id = <2>; + phy_address = <0x19>; + }; + + port@2 { + port_id = <3>; + phy_address = <0x1a>; + }; + + port@3 { + port_id = <4>; + phy_address = <0x1b>; + }; + + port@4 { + port_id = <5>; + ethernet-phy-ieee802.3-c45; + phy_address = <0x0>; + }; + + port@5 { + port_id = <6>; + ethernet-phy-ieee802.3-c45; + phy_address = <0x8>; + }; + }; +}; + +&edma { + status = "okay"; +}; + +&dp2 { + status = "okay"; + phy-handle = <&qca8075_2>; + label = "lan4"; + nvmem-cells = <&macaddr_appsblenv_ethaddr>; + nvmem-cell-names = "mac-address"; +}; + +&dp3 { + status = "okay"; + phy-handle = <&qca8075_3>; + label = "lan3"; + nvmem-cells = <&macaddr_appsblenv_ethaddr>; + nvmem-cell-names = "mac-address"; +}; + +&dp4 { + status = "okay"; + phy-handle = <&qca8075_4>; + label = "lan2"; + nvmem-cells = <&macaddr_appsblenv_ethaddr>; + nvmem-cell-names = "mac-address"; +}; + +&dp5_syn { + status = "okay"; + phy-handle = <&aqr113c_1>; + label = "wan"; + nvmem-cells = <&macaddr_appsblenv_ethaddr>; + nvmem-cell-names = "mac-address"; +}; + +&dp6_syn { + status = "okay"; + phy-handle = <&aqr113c_2>; + label = "lan1"; + nvmem-cells = <&macaddr_appsblenv_ethaddr>; + nvmem-cell-names = "mac-address"; +}; + +&ssphy_0 { + status = "okay"; +}; + +&qusb_phy_0 { + status = "okay"; +}; + +&usb_0 { + status = "okay"; + + vbus-supply = <®_usb_vbus>; +}; + +&wifi { + status = "okay"; + + qcom,ath11k-calibration-variant = "Buffalo-WXR-5950AX12"; +}; diff --git a/target/linux/qualcommax/generic/target.mk b/target/linux/qualcommax/generic/target.mk new file mode 100644 index 0000000000..f5cb1fb19b --- /dev/null +++ b/target/linux/qualcommax/generic/target.mk @@ -0,0 +1 @@ +BOARDNAME:=Generic diff --git a/target/linux/qualcommax/image/Makefile b/target/linux/qualcommax/image/Makefile new file mode 100644 index 0000000000..f59ad0c15f --- /dev/null +++ b/target/linux/qualcommax/image/Makefile @@ -0,0 +1,17 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +define Device/Default + PROFILES := Default + KERNEL_LOADADDR := 0x41000000 + DEVICE_DTS = $$(SOC)-$(lastword $(subst _, ,$(1))) + DEVICE_DTS_CONFIG := config@1 + DEVICE_DTS_DIR := $(DTS_DIR)/qcom + IMAGES := sysupgrade.bin + IMAGE/sysupgrade.bin = sysupgrade-tar | append-metadata + IMAGE/sysupgrade.bin/squashfs := +endef + +include $(SUBTARGET).mk + +$(eval $(call BuildImage)) diff --git a/target/linux/qualcommax/image/generic.mk b/target/linux/qualcommax/image/generic.mk new file mode 100644 index 0000000000..6003f2a5c0 --- /dev/null +++ b/target/linux/qualcommax/image/generic.mk @@ -0,0 +1,188 @@ +define Device/FitImage + KERNEL_SUFFIX := -uImage.itb + KERNEL = kernel-bin | libdeflate-gzip | fit gzip $$(KDIR)/image-$$(DEVICE_DTS).dtb + KERNEL_NAME := Image +endef + +define Device/FitImageLzma + KERNEL_SUFFIX := -uImage.itb + KERNEL = kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb + KERNEL_NAME := Image +endef + +define Device/EmmcImage + IMAGES += factory.bin sysupgrade.bin + IMAGE/factory.bin := append-rootfs | pad-rootfs | pad-to 64k + IMAGE/sysupgrade.bin/squashfs := append-rootfs | pad-to 64k | sysupgrade-tar rootfs=$$$$@ | append-metadata +endef + +define Device/UbiFit + KERNEL_IN_UBI := 1 + IMAGES := factory.ubi sysupgrade.bin + IMAGE/factory.ubi := append-ubi + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef + +define Device/buffalo_wxr-5950ax12 + $(call Device/FitImage) + DEVICE_VENDOR := Buffalo + DEVICE_MODEL := WXR-5950AX12 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@hk01 + SOC := ipq8074 + IMAGES := sysupgrade.bin + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata + DEVICE_PACKAGES := ipq-wifi-buffalo_wxr-5950ax12 +endef +TARGET_DEVICES += buffalo_wxr-5950ax12 + +define Device/compex_wpq873 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Compex + DEVICE_MODEL := WPQ873 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@hk09.wpq873 + SOC := ipq8072 + IMAGE/factory.ubi := append-ubi | qsdk-ipq-factory-nand +endef +TARGET_DEVICES += compex_wpq873 + +define Device/dynalink_dl-wrx36 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Dynalink + DEVICE_MODEL := DL-WRX36 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@rt5010w-d350-rev0 + SOC := ipq8072 + DEVICE_PACKAGES := ipq-wifi-dynalink_dl-wrx36 +endef +TARGET_DEVICES += dynalink_dl-wrx36 + +define Device/edgecore_eap102 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Edgecore + DEVICE_MODEL := EAP102 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@ac02 + SOC := ipq8071 + DEVICE_PACKAGES := ipq-wifi-edgecore_eap102 + IMAGE/factory.ubi := append-ubi | qsdk-ipq-factory-nand +endef +TARGET_DEVICES += edgecore_eap102 + +define Device/edimax_cax1800 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Edimax + DEVICE_MODEL := CAX1800 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@ac03 + SOC := ipq8070 + DEVICE_PACKAGES := ipq-wifi-edimax_cax1800 +endef +TARGET_DEVICES += edimax_cax1800 + +define Device/netgear_wax218 + $(call Device/FitImage) + $(call Device/UbiFit) + ARTIFACTS := web-ui-factory.fit + DEVICE_VENDOR := Netgear + DEVICE_MODEL := WAX218 + DEVICE_DTS_CONFIG := config@hk07 + BLOCKSIZE := 128k + PAGESIZE := 2048 + SOC := ipq8072 + ARTIFACT/web-ui-factory.fit := append-image initramfs-uImage.itb | \ + ubinize-kernel | qsdk-ipq-factory-nand + DEVICE_PACKAGES := kmod-spi-gpio kmod-spi-bitbang kmod-gpio-nxp-74hc164 \ + ipq-wifi-netgear_wax218 +endef +TARGET_DEVICES += netgear_wax218 + +define Device/prpl_haze + $(call Device/FitImage) + $(call Device/EmmcImage) + DEVICE_VENDOR := prpl Foundation + DEVICE_MODEL := Haze + DEVICE_DTS_CONFIG := config@hk09 + SOC := ipq8072 + DEVICE_PACKAGES += ath11k-firmware-qcn9074 ipq-wifi-prpl_haze kmod-ath11k-pci +endef +TARGET_DEVICES += prpl_haze + +define Device/qnap_301w + $(call Device/FitImage) + $(call Device/EmmcImage) + DEVICE_VENDOR := QNAP + DEVICE_MODEL := 301w + DEVICE_DTS_CONFIG := config@hk01 + KERNEL_SIZE := 16384k + SOC := ipq8072 + DEVICE_PACKAGES += ipq-wifi-qnap_301w +endef +TARGET_DEVICES += qnap_301w + +define Device/redmi_ax6 + $(call Device/xiaomi_ax3600) + DEVICE_VENDOR := Redmi + DEVICE_MODEL := AX6 + DEVICE_PACKAGES := ipq-wifi-redmi_ax6 +endef +TARGET_DEVICES += redmi_ax6 + +define Device/xiaomi_ax3600 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Xiaomi + DEVICE_MODEL := AX3600 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@ac04 + SOC := ipq8071 + KERNEL_SIZE := 36608k + DEVICE_PACKAGES := ipq-wifi-xiaomi_ax3600 kmod-ath10k-ct-smallbuffers ath10k-firmware-qca9887-ct +ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) + ARTIFACTS := initramfs-factory.ubi + ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-uImage.itb | ubinize-kernel +endif +endef +TARGET_DEVICES += xiaomi_ax3600 + +define Device/xiaomi_ax9000 + $(call Device/FitImage) + $(call Device/UbiFit) + DEVICE_VENDOR := Xiaomi + DEVICE_MODEL := AX9000 + BLOCKSIZE := 128k + PAGESIZE := 2048 + DEVICE_DTS_CONFIG := config@hk14 + SOC := ipq8072 + KERNEL_SIZE := 57344k + DEVICE_PACKAGES := ipq-wifi-xiaomi_ax9000 kmod-ath11k-pci ath11k-firmware-qcn9074 \ + kmod-ath10k-ct ath10k-firmware-qca9887-ct +ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) + ARTIFACTS := initramfs-factory.ubi + ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-uImage.itb | ubinize-kernel +endif +endef +TARGET_DEVICES += xiaomi_ax9000 + +define Device/zyxel_nbg7815 + $(call Device/FitImage) + $(call Device/EmmcImage) + DEVICE_VENDOR := ZYXEL + DEVICE_MODEL := NBG7815 + DEVICE_DTS_CONFIG := config@nbg7815 + SOC := ipq8074 + DEVICE_PACKAGES += ipq-wifi-zyxel_nbg7815 kmod-ath11k-pci kmod-hwmon-tmp103 \ + kmod-bluetooth +endef +TARGET_DEVICES += zyxel_nbg7815 diff --git a/target/linux/qualcommax/patches-6.1/0001-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch b/target/linux/qualcommax/patches-6.1/0001-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch new file mode 100644 index 0000000000..dd57eae360 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0001-v6.2-arm64-dts-qcom-ipq8074-add-A53-PLL-node.patch @@ -0,0 +1,32 @@ +From 6463c10bfdbd684ec7ecfd408ea541283215a088 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:06:28 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add A53 PLL node + +Add the required node for A53 PLL which will be used to provide the CPU +clock via APCS for APSS scaling. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220628.339366-9-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -675,6 +675,14 @@ + #mbox-cells = <1>; + }; + ++ a53pll: clock@b116000 { ++ compatible = "qcom,ipq8074-a53pll"; ++ reg = <0x0b116000 0x40>; ++ #clock-cells = <0>; ++ clocks = <&xo>; ++ clock-names = "xo"; ++ }; ++ + timer@b120000 { + #address-cells = <1>; + #size-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.1/0002-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch b/target/linux/qualcommax/patches-6.1/0002-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch new file mode 100644 index 0000000000..0320725999 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0002-v6.2-thermal-drivers-tsens-Add-support-for-combined-inter.patch @@ -0,0 +1,134 @@ +From e593e834fe8ba9bf314d8215ac05d8787f81efda Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:42 +0200 +Subject: [PATCH] thermal/drivers/tsens: Add support for combined interrupt + +Despite using tsens v2.3 IP, IPQ8074 and IPQ6018 only have one IRQ for +signaling both up/low and critical trips. + +Signed-off-by: Robert Marko +Reviewed-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-2-robimarko@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/qcom/tsens-8960.c | 1 + + drivers/thermal/qcom/tsens-v0_1.c | 1 + + drivers/thermal/qcom/tsens-v1.c | 1 + + drivers/thermal/qcom/tsens-v2.c | 1 + + drivers/thermal/qcom/tsens.c | 38 ++++++++++++++++++++++++++----- + drivers/thermal/qcom/tsens.h | 2 ++ + 6 files changed, 38 insertions(+), 6 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -269,6 +269,7 @@ static const struct tsens_ops ops_8960 = + static struct tsens_features tsens_8960_feat = { + .ver_major = VER_0, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 0, + .max_sensors = 11, +--- a/drivers/thermal/qcom/tsens-v0_1.c ++++ b/drivers/thermal/qcom/tsens-v0_1.c +@@ -549,6 +549,7 @@ static int __init init_8939(struct tsens + static struct tsens_features tsens_v0_1_feat = { + .ver_major = VER_0_1, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -273,6 +273,7 @@ static int calibrate_8976(struct tsens_p + static struct tsens_features tsens_v1_feat = { + .ver_major = VER_1_X, + .crit_int = 0, ++ .combo_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -31,6 +31,7 @@ + static struct tsens_features tsens_v2_feat = { + .ver_major = VER_2_X, + .crit_int = 1, ++ .combo_int = 0, + .adc = 0, + .srot_split = 1, + .max_sensors = 16, +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -532,6 +532,27 @@ static irqreturn_t tsens_irq_thread(int + return IRQ_HANDLED; + } + ++/** ++ * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts ++ * @irq: irq number ++ * @data: tsens controller private data ++ * ++ * Handle the combined interrupt as if it were 2 separate interrupts, so call the ++ * critical handler first and then the up/low one. ++ * ++ * Return: IRQ_HANDLED ++ */ ++static irqreturn_t tsens_combined_irq_thread(int irq, void *data) ++{ ++ irqreturn_t ret; ++ ++ ret = tsens_critical_irq_thread(irq, data); ++ if (ret != IRQ_HANDLED) ++ return ret; ++ ++ return tsens_irq_thread(irq, data); ++} ++ + static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high) + { + struct tsens_sensor *s = tz->devdata; +@@ -1074,13 +1095,18 @@ static int tsens_register(struct tsens_p + tsens_mC_to_hw(priv->sensor, 0)); + } + +- ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); +- if (ret < 0) +- return ret; ++ if (priv->feat->combo_int) { ++ ret = tsens_register_irq(priv, "combined", ++ tsens_combined_irq_thread); ++ } else { ++ ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); ++ if (ret < 0) ++ return ret; + +- if (priv->feat->crit_int) +- ret = tsens_register_irq(priv, "critical", +- tsens_critical_irq_thread); ++ if (priv->feat->crit_int) ++ ret = tsens_register_irq(priv, "critical", ++ tsens_critical_irq_thread); ++ } + + return ret; + } +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -493,6 +493,7 @@ enum regfield_ids { + * struct tsens_features - Features supported by the IP + * @ver_major: Major number of IP version + * @crit_int: does the IP support critical interrupts? ++ * @combo_int: does the IP use one IRQ for up, low and critical thresholds? + * @adc: do the sensors only output adc code (instead of temperature)? + * @srot_split: does the IP neatly splits the register space into SROT and TM, + * with SROT only being available to secure boot firmware? +@@ -502,6 +503,7 @@ enum regfield_ids { + struct tsens_features { + unsigned int ver_major; + unsigned int crit_int:1; ++ unsigned int combo_int:1; + unsigned int adc:1; + unsigned int srot_split:1; + unsigned int has_watchdog:1; diff --git a/target/linux/qualcommax/patches-6.1/0003-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch b/target/linux/qualcommax/patches-6.1/0003-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch new file mode 100644 index 0000000000..363061813a --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0003-v6.2-thermal-drivers-tsens-Allow-configuring-min-and-max-.patch @@ -0,0 +1,101 @@ +From 7805365fee582056b32c69cf35aafbb94b14a8ca Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:43 +0200 +Subject: [PATCH] thermal/drivers/tsens: Allow configuring min and max trips + +IPQ8074 and IPQ6018 dont support negative trip temperatures and support +up to 204 degrees C as the max trip temperature. + +So, instead of always setting the -40 as min and 120 degrees C as max +allow it to be configured as part of the features. + +Signed-off-by: Robert Marko +Reviewed-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-3-robimarko@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/qcom/tsens-8960.c | 2 ++ + drivers/thermal/qcom/tsens-v0_1.c | 2 ++ + drivers/thermal/qcom/tsens-v1.c | 2 ++ + drivers/thermal/qcom/tsens-v2.c | 2 ++ + drivers/thermal/qcom/tsens.c | 4 ++-- + drivers/thermal/qcom/tsens.h | 4 ++++ + 6 files changed, 14 insertions(+), 2 deletions(-) + +--- a/drivers/thermal/qcom/tsens-8960.c ++++ b/drivers/thermal/qcom/tsens-8960.c +@@ -273,6 +273,8 @@ static struct tsens_features tsens_8960_ + .adc = 1, + .srot_split = 0, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + struct tsens_plat_data data_8960 = { +--- a/drivers/thermal/qcom/tsens-v0_1.c ++++ b/drivers/thermal/qcom/tsens-v0_1.c +@@ -553,6 +553,8 @@ static struct tsens_features tsens_v0_1_ + .adc = 1, + .srot_split = 1, + .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = { +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -277,6 +277,8 @@ static struct tsens_features tsens_v1_fe + .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] = { +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -35,6 +35,8 @@ static struct tsens_features tsens_v2_fe + .adc = 0, + .srot_split = 1, + .max_sensors = 16, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, + }; + + static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -573,8 +573,8 @@ static int tsens_set_trips(struct therma + dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", + hw_id, __func__, low, high); + +- cl_high = clamp_val(high, -40000, 120000); +- cl_low = clamp_val(low, -40000, 120000); ++ cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp); ++ cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp); + + high_val = tsens_mC_to_hw(s, cl_high); + low_val = tsens_mC_to_hw(s, cl_low); +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -499,6 +499,8 @@ enum regfield_ids { + * with SROT only being available to secure boot firmware? + * @has_watchdog: does this IP support watchdog functionality? + * @max_sensors: maximum sensors supported by this version of the IP ++ * @trip_min_temp: minimum trip temperature supported by this version of the IP ++ * @trip_max_temp: maximum trip temperature supported by this version of the IP + */ + struct tsens_features { + unsigned int ver_major; +@@ -508,6 +510,8 @@ struct tsens_features { + unsigned int srot_split:1; + unsigned int has_watchdog:1; + unsigned int max_sensors; ++ int trip_min_temp; ++ int trip_max_temp; + }; + + /** diff --git a/target/linux/qualcommax/patches-6.1/0004-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch b/target/linux/qualcommax/patches-6.1/0004-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch new file mode 100644 index 0000000000..eaea693959 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0004-v6.2-thermal-drivers-tsens-Add-IPQ8074-support.patch @@ -0,0 +1,74 @@ +From 0164d794cbc58488a7321272e95958d10cf103a4 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:44 +0200 +Subject: [PATCH] thermal/drivers/tsens: Add IPQ8074 support + +Qualcomm IPQ8074 uses tsens v2.3 IP, however unlike other tsens v2 IP +it only has one IRQ, that is used for up/low as well as critical. +It also does not support negative trip temperatures. + +Signed-off-by: Robert Marko +Reviewed-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-4-robimarko@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/qcom/tsens-v2.c | 17 +++++++++++++++++ + drivers/thermal/qcom/tsens.c | 3 +++ + drivers/thermal/qcom/tsens.h | 2 +- + 3 files changed, 21 insertions(+), 1 deletion(-) + +--- a/drivers/thermal/qcom/tsens-v2.c ++++ b/drivers/thermal/qcom/tsens-v2.c +@@ -39,6 +39,17 @@ static struct tsens_features tsens_v2_fe + .trip_max_temp = 120000, + }; + ++static struct tsens_features ipq8074_feat = { ++ .ver_major = VER_2_X, ++ .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 */ +@@ -104,6 +115,12 @@ struct tsens_plat_data data_tsens_v2 = { + .fields = tsens_v2_regfields, + }; + ++struct tsens_plat_data data_ipq8074 = { ++ .ops = &ops_generic_v2, ++ .feat = &ipq8074_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 +@@ -981,6 +981,9 @@ static const struct of_device_id tsens_t + .compatible = "qcom,ipq8064-tsens", + .data = &data_8960, + }, { ++ .compatible = "qcom,ipq8074-tsens", ++ .data = &data_ipq8074, ++ }, { + .compatible = "qcom,mdm9607-tsens", + .data = &data_9607, + }, { +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -597,6 +597,6 @@ extern struct tsens_plat_data data_8916, + extern struct tsens_plat_data data_tsens_v1, data_8976, data_8956; + + /* TSENS v2 targets */ +-extern struct tsens_plat_data data_8996, data_tsens_v2; ++extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; + + #endif /* __QCOM_TSENS_H__ */ diff --git a/target/linux/qualcommax/patches-6.1/0005-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch b/target/linux/qualcommax/patches-6.1/0005-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch new file mode 100644 index 0000000000..bad75e4597 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0005-v6.2-arm64-dts-qcom-ipq8074-add-thermal-nodes.patch @@ -0,0 +1,130 @@ +From c3cc0c2a17f552be2426200e47a9e2c62cf449ce Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:02:45 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add thermal nodes + +IPQ8074 has a tsens v2.3.0 peripheral which monitors +temperatures around the various subsystems on the +die. + +So lets add the tsens and thermal zone nodes, passive +CPU cooling will come in later patches after CPU frequency +scaling is supported. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220245.338396-5-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 96 +++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -274,6 +274,16 @@ + status = "disabled"; + }; + ++ tsens: thermal-sensor@4a9000 { ++ compatible = "qcom,ipq8074-tsens"; ++ reg = <0x4a9000 0x1000>, /* TM */ ++ <0x4a8000 0x1000>; /* SROT */ ++ interrupts = ; ++ interrupt-names = "combined"; ++ #qcom,sensors = <16>; ++ #thermal-sensor-cells = <1>; ++ }; ++ + cryptobam: dma-controller@704000 { + compatible = "qcom,bam-v1.7.0"; + reg = <0x00704000 0x20000>; +@@ -874,4 +884,90 @@ + , + ; + }; ++ ++ thermal-zones { ++ nss-top-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 4>; ++ }; ++ ++ nss0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 5>; ++ }; ++ ++ nss1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 6>; ++ }; ++ ++ wcss-phya0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 7>; ++ }; ++ ++ wcss-phya1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 8>; ++ }; ++ ++ cpu0_thermal: cpu0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 9>; ++ }; ++ ++ cpu1_thermal: cpu1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 10>; ++ }; ++ ++ cpu2_thermal: cpu2-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 11>; ++ }; ++ ++ cpu3_thermal: cpu3-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 12>; ++ }; ++ ++ cluster_thermal: cluster-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 13>; ++ }; ++ ++ wcss-phyb0-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 14>; ++ }; ++ ++ wcss-phyb1-thermal { ++ polling-delay-passive = <250>; ++ polling-delay = <1000>; ++ ++ thermal-sensors = <&tsens 15>; ++ }; ++ }; + }; diff --git a/target/linux/qualcommax/patches-6.1/0006-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch b/target/linux/qualcommax/patches-6.1/0006-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch new file mode 100644 index 0000000000..e229851649 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0006-v6.2-arm64-dts-qcom-ipq8074-add-clocks-to-APCS.patch @@ -0,0 +1,29 @@ +From 0df592a0a1a3fff9133977192677aa915afc174f Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:08:49 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add clocks to APCS + +APCS now has support for providing the APSS clocks as the child device +for IPQ8074. + +So, add the A53 PLL and XO clocks in order to use APCS as the CPU +clocksource for APSS scaling. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818220849.339732-4-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -680,6 +680,8 @@ + apcs_glb: mailbox@b111000 { + compatible = "qcom,ipq8074-apcs-apps-global"; + reg = <0x0b111000 0x1000>; ++ clocks = <&a53pll>, <&xo>; ++ clock-names = "pll", "xo"; + + #clock-cells = <1>; + #mbox-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.1/0007-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch b/target/linux/qualcommax/patches-6.1/0007-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch new file mode 100644 index 0000000000..9162ea538d --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0007-v6.2-clk-qcom-ipq8074-convert-to-parent-data.patch @@ -0,0 +1,3601 @@ +From e6c5115d6845f25eda7e162dcd783a2044215867 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 30 Oct 2022 18:57:01 +0100 +Subject: [PATCH] clk: qcom: ipq8074: convert to parent data + +Convert the IPQ8074 GCC driver to use parent data instead of global +name matching. + +Utilize ARRAY_SIZE for num_parents instead of hardcoding the value. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221030175703.1103224-1-robimarko@gmail.com +--- + drivers/clk/qcom/gcc-ipq8074.c | 1781 +++++++++++++++----------------- + 1 file changed, 813 insertions(+), 968 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -49,349 +49,6 @@ enum { + P_UNIPHY2_TX, + }; + +-static const char * const gcc_xo_gpll0_gpll0_out_main_div2[] = { +- "xo", +- "gpll0", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const struct parent_map gcc_xo_gpll0_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll2_gpll0_out_main_div2[] = { +- "xo", +- "gpll0", +- "gpll2", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL2, 2 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const char * const gcc_xo_gpll0_sleep_clk[] = { +- "xo", +- "gpll0", +- "sleep_clk", +-}; +- +-static const struct parent_map gcc_xo_gpll0_sleep_clk_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 2 }, +- { P_SLEEP_CLK, 6 }, +-}; +- +-static const char * const gcc_xo_gpll6_gpll0_gpll0_out_main_div2[] = { +- "xo", +- "gpll6", +- "gpll0", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL6, 1 }, +- { P_GPLL0, 3 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const char * const gcc_xo_gpll0_out_main_div2_gpll0[] = { +- "xo", +- "gpll0_out_main_div2", +- "gpll0", +-}; +- +-static const struct parent_map gcc_xo_gpll0_out_main_div2_gpll0_map[] = { +- { P_XO, 0 }, +- { P_GPLL0_DIV2, 2 }, +- { P_GPLL0, 1 }, +-}; +- +-static const char * const gcc_usb3phy_0_cc_pipe_clk_xo[] = { +- "usb3phy_0_cc_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_usb3phy_0_cc_pipe_clk_xo_map[] = { +- { P_USB3PHY_0_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_usb3phy_1_cc_pipe_clk_xo[] = { +- "usb3phy_1_cc_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_usb3phy_1_cc_pipe_clk_xo_map[] = { +- { P_USB3PHY_1_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_pcie20_phy0_pipe_clk_xo[] = { +- "pcie20_phy0_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_pcie20_phy0_pipe_clk_xo_map[] = { +- { P_PCIE20_PHY0_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_pcie20_phy1_pipe_clk_xo[] = { +- "pcie20_phy1_pipe_clk", +- "xo", +-}; +- +-static const struct parent_map gcc_pcie20_phy1_pipe_clk_xo_map[] = { +- { P_PCIE20_PHY1_PIPE, 0 }, +- { P_XO, 2 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll6_gpll0_div2[] = { +- "xo", +- "gpll0", +- "gpll6", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL6, 2 }, +- { P_GPLL0_DIV2, 4 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll6_gpll0_out_main_div2[] = { +- "xo", +- "gpll0", +- "gpll6", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL6, 2 }, +- { P_GPLL0_DIV2, 3 }, +-}; +- +-static const char * const gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { +- "xo", +- "bias_pll_nss_noc_clk", +- "gpll0", +- "gpll2", +-}; +- +-static const struct parent_map gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map[] = { +- { P_XO, 0 }, +- { P_BIAS_PLL_NSS_NOC, 1 }, +- { P_GPLL0, 2 }, +- { P_GPLL2, 3 }, +-}; +- +-static const char * const gcc_xo_nss_crypto_pll_gpll0[] = { +- "xo", +- "nss_crypto_pll", +- "gpll0", +-}; +- +-static const struct parent_map gcc_xo_nss_crypto_pll_gpll0_map[] = { +- { P_XO, 0 }, +- { P_NSS_CRYPTO_PLL, 1 }, +- { P_GPLL0, 2 }, +-}; +- +-static const char * const gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6[] = { +- "xo", +- "ubi32_pll", +- "gpll0", +- "gpll2", +- "gpll4", +- "gpll6", +-}; +- +-static const struct parent_map gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map[] = { +- { P_XO, 0 }, +- { P_UBI32_PLL, 1 }, +- { P_GPLL0, 2 }, +- { P_GPLL2, 3 }, +- { P_GPLL4, 4 }, +- { P_GPLL6, 5 }, +-}; +- +-static const char * const gcc_xo_gpll0_out_main_div2[] = { +- "xo", +- "gpll0_out_main_div2", +-}; +- +-static const struct parent_map gcc_xo_gpll0_out_main_div2_map[] = { +- { P_XO, 0 }, +- { P_GPLL0_DIV2, 1 }, +-}; +- +-static const char * const gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { +- "xo", +- "bias_pll_cc_clk", +- "gpll0", +- "gpll4", +- "nss_crypto_pll", +- "ubi32_pll", +-}; +- +-static const struct parent_map gcc_xo_bias_gpll0_gpll4_nss_ubi32_map[] = { +- { P_XO, 0 }, +- { P_BIAS_PLL, 1 }, +- { P_GPLL0, 2 }, +- { P_GPLL4, 3 }, +- { P_NSS_CRYPTO_PLL, 4 }, +- { P_UBI32_PLL, 5 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll4[] = { +- "xo", +- "gpll0", +- "gpll4", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL4, 2 }, +-}; +- +-static const char * const gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_rx_clk", +- "uniphy0_gcc_tx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_RX, 1 }, +- { P_UNIPHY0_TX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_tx_clk", +- "uniphy0_gcc_rx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_TX, 1 }, +- { P_UNIPHY0_RX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_rx_clk", +- "uniphy0_gcc_tx_clk", +- "uniphy1_gcc_rx_clk", +- "uniphy1_gcc_tx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map +-gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_RX, 1 }, +- { P_UNIPHY0_TX, 2 }, +- { P_UNIPHY1_RX, 3 }, +- { P_UNIPHY1_TX, 4 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { +- "xo", +- "uniphy0_gcc_tx_clk", +- "uniphy0_gcc_rx_clk", +- "uniphy1_gcc_tx_clk", +- "uniphy1_gcc_rx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map +-gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY0_TX, 1 }, +- { P_UNIPHY0_RX, 2 }, +- { P_UNIPHY1_TX, 3 }, +- { P_UNIPHY1_RX, 4 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { +- "xo", +- "uniphy2_gcc_rx_clk", +- "uniphy2_gcc_tx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY2_RX, 1 }, +- { P_UNIPHY2_TX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { +- "xo", +- "uniphy2_gcc_tx_clk", +- "uniphy2_gcc_rx_clk", +- "ubi32_pll", +- "bias_pll_cc_clk", +-}; +- +-static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { +- { P_XO, 0 }, +- { P_UNIPHY2_TX, 1 }, +- { P_UNIPHY2_RX, 2 }, +- { P_UBI32_PLL, 5 }, +- { P_BIAS_PLL, 6 }, +-}; +- +-static const char * const gcc_xo_gpll0_gpll6_gpll0_sleep_clk[] = { +- "xo", +- "gpll0", +- "gpll6", +- "gpll0_out_main_div2", +- "sleep_clk", +-}; +- +-static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map[] = { +- { P_XO, 0 }, +- { P_GPLL0, 1 }, +- { P_GPLL6, 2 }, +- { P_GPLL0_DIV2, 4 }, +- { P_SLEEP_CLK, 6 }, +-}; +- + static struct clk_alpha_pll gpll0_main = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], +@@ -400,8 +57,9 @@ static struct clk_alpha_pll gpll0_main = + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpll0_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -414,9 +72,8 @@ static struct clk_fixed_factor gpll0_out + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "gpll0_out_main_div2", +- .parent_names = (const char *[]){ +- "gpll0_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll0_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -429,9 +86,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll0", +- .parent_names = (const char *[]){ +- "gpll0_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll0_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +@@ -445,8 +101,9 @@ static struct clk_alpha_pll gpll2_main = + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gpll2_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -461,9 +118,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll2", +- .parent_names = (const char *[]){ +- "gpll2_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll2_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -478,8 +134,9 @@ static struct clk_alpha_pll gpll4_main = + .enable_mask = BIT(5), + .hw.init = &(struct clk_init_data){ + .name = "gpll4_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -494,9 +151,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll4", +- .parent_names = (const char *[]){ +- "gpll4_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll4_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -512,8 +168,9 @@ static struct clk_alpha_pll gpll6_main = + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "gpll6_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -528,9 +185,8 @@ static struct clk_alpha_pll_postdiv gpll + .width = 2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll6", +- .parent_names = (const char *[]){ +- "gpll6_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll6_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -542,9 +198,8 @@ static struct clk_fixed_factor gpll6_out + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "gpll6_out_main_div2", +- .parent_names = (const char *[]){ +- "gpll6_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gpll6_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -560,8 +215,9 @@ static struct clk_alpha_pll ubi32_pll_ma + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "ubi32_pll_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, +@@ -575,9 +231,8 @@ static struct clk_alpha_pll_postdiv ubi3 + .width = 2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ubi32_pll", +- .parent_names = (const char *[]){ +- "ubi32_pll_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &ubi32_pll_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -592,8 +247,9 @@ static struct clk_alpha_pll nss_crypto_p + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "nss_crypto_pll_main", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_ops, +@@ -607,9 +263,8 @@ static struct clk_alpha_pll_postdiv nss_ + .width = 4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_crypto_pll", +- .parent_names = (const char *[]){ +- "nss_crypto_pll_main" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_crypto_pll_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -623,6 +278,18 @@ static const struct freq_tbl ftbl_pcnoc_ + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw}, ++ { .hw = &gpll0_out_main_div2.hw}, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 pcnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x27000, + .freq_tbl = ftbl_pcnoc_bfdcd_clk_src, +@@ -630,8 +297,8 @@ static struct clk_rcg2 pcnoc_bfdcd_clk_s + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcnoc_bfdcd_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + .flags = CLK_IS_CRITICAL, + }, +@@ -642,9 +309,8 @@ static struct clk_fixed_factor pcnoc_clk + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "pcnoc_clk_src", +- .parent_names = (const char *[]){ +- "pcnoc_bfdcd_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_bfdcd_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -658,8 +324,9 @@ static struct clk_branch gcc_sleep_clk_s + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sleep_clk_src", +- .parent_names = (const char *[]){ +- "sleep_clk" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "sleep_clk", ++ .name = "sleep_clk", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, +@@ -682,8 +349,8 @@ static struct clk_rcg2 blsp1_qup1_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -708,8 +375,8 @@ static struct clk_rcg2 blsp1_qup1_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -721,8 +388,8 @@ static struct clk_rcg2 blsp1_qup2_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -735,8 +402,8 @@ static struct clk_rcg2 blsp1_qup2_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -748,8 +415,8 @@ static struct clk_rcg2 blsp1_qup3_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -762,8 +429,8 @@ static struct clk_rcg2 blsp1_qup3_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -775,8 +442,8 @@ static struct clk_rcg2 blsp1_qup4_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -789,8 +456,8 @@ static struct clk_rcg2 blsp1_qup4_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -802,8 +469,8 @@ static struct clk_rcg2 blsp1_qup5_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup5_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -816,8 +483,8 @@ static struct clk_rcg2 blsp1_qup5_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup5_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -829,8 +496,8 @@ static struct clk_rcg2 blsp1_qup6_i2c_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup6_i2c_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -843,8 +510,8 @@ static struct clk_rcg2 blsp1_qup6_spi_ap + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup6_spi_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -877,8 +544,8 @@ static struct clk_rcg2 blsp1_uart1_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart1_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -891,8 +558,8 @@ static struct clk_rcg2 blsp1_uart2_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart2_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -905,8 +572,8 @@ static struct clk_rcg2 blsp1_uart3_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart3_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -919,8 +586,8 @@ static struct clk_rcg2 blsp1_uart4_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart4_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -933,8 +600,8 @@ static struct clk_rcg2 blsp1_uart5_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart5_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -947,8 +614,8 @@ static struct clk_rcg2 blsp1_uart6_apps_ + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart6_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -958,6 +625,11 @@ static const struct clk_parent_data gcc_ + { .hw = &gpll0.clkr.hw }, + }; + ++static const struct parent_map gcc_xo_gpll0_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++}; ++ + static const struct freq_tbl ftbl_pcie_axi_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(200000000, P_GPLL0, 4, 0, 0), +@@ -972,7 +644,7 @@ static struct clk_rcg2 pcie0_axi_clk_src + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_axi_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -981,6 +653,18 @@ static const struct freq_tbl ftbl_pcie_a + F(19200000, P_XO, 1, 0, 0), + }; + ++static const struct clk_parent_data gcc_xo_gpll0_sleep_clk[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .fw_name = "sleep_clk", .name = "sleep_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_sleep_clk_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 2 }, ++ { P_SLEEP_CLK, 6 }, ++}; ++ + static struct clk_rcg2 pcie0_aux_clk_src = { + .cmd_rcgr = 0x75024, + .freq_tbl = ftbl_pcie_aux_clk_src, +@@ -989,12 +673,22 @@ static struct clk_rcg2 pcie0_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_pcie20_phy0_pipe_clk_xo[] = { ++ { .name = "pcie20_phy0_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_pcie20_phy0_pipe_clk_xo_map[] = { ++ { P_PCIE20_PHY0_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux pcie0_pipe_clk_src = { + .reg = 0x7501c, + .shift = 8, +@@ -1003,8 +697,8 @@ static struct clk_regmap_mux pcie0_pipe_ + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "pcie0_pipe_clk_src", +- .parent_names = gcc_pcie20_phy0_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_pcie20_phy0_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_pcie20_phy0_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1019,7 +713,7 @@ static struct clk_rcg2 pcie1_axi_clk_src + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie1_axi_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1032,12 +726,22 @@ static struct clk_rcg2 pcie1_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie1_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_pcie20_phy1_pipe_clk_xo[] = { ++ { .name = "pcie20_phy1_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_pcie20_phy1_pipe_clk_xo_map[] = { ++ { P_PCIE20_PHY1_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux pcie1_pipe_clk_src = { + .reg = 0x7601c, + .shift = 8, +@@ -1046,8 +750,8 @@ static struct clk_regmap_mux pcie1_pipe_ + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "pcie1_pipe_clk_src", +- .parent_names = gcc_pcie20_phy1_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_pcie20_phy1_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_pcie20_phy1_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1066,6 +770,20 @@ static const struct freq_tbl ftbl_sdcc_a + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll2_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll2.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL2, 2 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x42004, + .freq_tbl = ftbl_sdcc_apps_clk_src, +@@ -1074,8 +792,8 @@ static struct clk_rcg2 sdcc1_apps_clk_sr + .parent_map = gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll2_gpll0_out_main_div2), + .ops = &clk_rcg2_floor_ops, + }, + }; +@@ -1086,6 +804,20 @@ static const struct freq_tbl ftbl_sdcc_i + F(308570000, P_GPLL6, 3.5, 0, 0), + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL6, 2 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x5d000, + .freq_tbl = ftbl_sdcc_ice_core_clk_src, +@@ -1094,8 +826,8 @@ static struct clk_rcg2 sdcc1_ice_core_cl + .parent_map = gcc_xo_gpll0_gpll6_gpll0_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_ice_core_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1108,8 +840,8 @@ static struct clk_rcg2 sdcc2_apps_clk_sr + .parent_map = gcc_xo_gpll0_gpll2_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc2_apps_clk_src", +- .parent_names = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll2_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll2_gpll0_out_main_div2), + .ops = &clk_rcg2_floor_ops, + }, + }; +@@ -1121,6 +853,18 @@ static const struct freq_tbl ftbl_usb_ma + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_out_main_div2_gpll0[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0_out_main_div2.hw }, ++ { .hw = &gpll0.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_out_main_div2_gpll0_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0_DIV2, 2 }, ++ { P_GPLL0, 1 }, ++}; ++ + static struct clk_rcg2 usb0_master_clk_src = { + .cmd_rcgr = 0x3e00c, + .freq_tbl = ftbl_usb_master_clk_src, +@@ -1129,8 +873,8 @@ static struct clk_rcg2 usb0_master_clk_s + .parent_map = gcc_xo_gpll0_out_main_div2_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb0_master_clk_src", +- .parent_names = gcc_xo_gpll0_out_main_div2_gpll0, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_out_main_div2_gpll0, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1148,8 +892,8 @@ static struct clk_rcg2 usb0_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb0_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1161,6 +905,20 @@ static const struct freq_tbl ftbl_usb_mo + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll6_gpll0_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL6, 1 }, ++ { P_GPLL0, 3 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_rcg2 usb0_mock_utmi_clk_src = { + .cmd_rcgr = 0x3e020, + .freq_tbl = ftbl_usb_mock_utmi_clk_src, +@@ -1169,12 +927,22 @@ static struct clk_rcg2 usb0_mock_utmi_cl + .parent_map = gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb0_mock_utmi_clk_src", +- .parent_names = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll6_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_usb3phy_0_cc_pipe_clk_xo[] = { ++ { .name = "usb3phy_0_cc_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_usb3phy_0_cc_pipe_clk_xo_map[] = { ++ { P_USB3PHY_0_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux usb0_pipe_clk_src = { + .reg = 0x3e048, + .shift = 8, +@@ -1183,8 +951,8 @@ static struct clk_regmap_mux usb0_pipe_c + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "usb0_pipe_clk_src", +- .parent_names = gcc_usb3phy_0_cc_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_usb3phy_0_cc_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_usb3phy_0_cc_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1199,8 +967,8 @@ static struct clk_rcg2 usb1_master_clk_s + .parent_map = gcc_xo_gpll0_out_main_div2_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb1_master_clk_src", +- .parent_names = gcc_xo_gpll0_out_main_div2_gpll0, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_out_main_div2_gpll0, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1213,8 +981,8 @@ static struct clk_rcg2 usb1_aux_clk_src + .parent_map = gcc_xo_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb1_aux_clk_src", +- .parent_names = gcc_xo_gpll0_sleep_clk, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1227,12 +995,22 @@ static struct clk_rcg2 usb1_mock_utmi_cl + .parent_map = gcc_xo_gpll6_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb1_mock_utmi_clk_src", +- .parent_names = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll6_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll6_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; + ++static const struct clk_parent_data gcc_usb3phy_1_cc_pipe_clk_xo[] = { ++ { .name = "usb3phy_1_cc_pipe_clk" }, ++ { .fw_name = "xo", .name = "xo" }, ++}; ++ ++static const struct parent_map gcc_usb3phy_1_cc_pipe_clk_xo_map[] = { ++ { P_USB3PHY_1_PIPE, 0 }, ++ { P_XO, 2 }, ++}; ++ + static struct clk_regmap_mux usb1_pipe_clk_src = { + .reg = 0x3f048, + .shift = 8, +@@ -1241,8 +1019,8 @@ static struct clk_regmap_mux usb1_pipe_c + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "usb1_pipe_clk_src", +- .parent_names = gcc_usb3phy_1_cc_pipe_clk_xo, +- .num_parents = 2, ++ .parent_data = gcc_usb3phy_1_cc_pipe_clk_xo, ++ .num_parents = ARRAY_SIZE(gcc_usb3phy_1_cc_pipe_clk_xo), + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1256,8 +1034,9 @@ static struct clk_branch gcc_xo_clk_src + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_xo_clk_src", +- .parent_names = (const char *[]){ +- "xo" ++ .parent_data = &(const struct clk_parent_data){ ++ .fw_name = "xo", ++ .name = "xo", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, +@@ -1271,9 +1050,8 @@ static struct clk_fixed_factor gcc_xo_di + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "gcc_xo_div4_clk_src", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1291,6 +1069,20 @@ static const struct freq_tbl ftbl_system + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL6, 2 }, ++ { P_GPLL0_DIV2, 3 }, ++}; ++ + static struct clk_rcg2 system_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x26004, + .freq_tbl = ftbl_system_noc_bfdcd_clk_src, +@@ -1298,8 +1090,8 @@ static struct clk_rcg2 system_noc_bfdcd_ + .parent_map = gcc_xo_gpll0_gpll6_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "system_noc_bfdcd_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_out_main_div2, +- .num_parents = 4, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + .flags = CLK_IS_CRITICAL, + }, +@@ -1310,9 +1102,8 @@ static struct clk_fixed_factor system_no + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "system_noc_clk_src", +- .parent_names = (const char *[]){ +- "system_noc_bfdcd_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &system_noc_bfdcd_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1333,7 +1124,7 @@ static struct clk_rcg2 nss_ce_clk_src = + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ce_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1344,6 +1135,20 @@ static const struct freq_tbl ftbl_nss_no + { } + }; + ++static const struct clk_parent_data gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "bias_pll_nss_noc_clk" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll2.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map[] = { ++ { P_XO, 0 }, ++ { P_BIAS_PLL_NSS_NOC, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL2, 3 }, ++}; ++ + static struct clk_rcg2 nss_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x68088, + .freq_tbl = ftbl_nss_noc_bfdcd_clk_src, +@@ -1351,8 +1156,8 @@ static struct clk_rcg2 nss_noc_bfdcd_clk + .parent_map = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_noc_bfdcd_clk_src", +- .parent_names = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2, +- .num_parents = 4, ++ .parent_data = gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2, ++ .num_parents = ARRAY_SIZE(gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1362,9 +1167,8 @@ static struct clk_fixed_factor nss_noc_c + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "nss_noc_clk_src", +- .parent_names = (const char *[]){ +- "nss_noc_bfdcd_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_bfdcd_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1377,6 +1181,18 @@ static const struct freq_tbl ftbl_nss_cr + { } + }; + ++static const struct clk_parent_data gcc_xo_nss_crypto_pll_gpll0[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &nss_crypto_pll.clkr.hw }, ++ { .hw = &gpll0.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_nss_crypto_pll_gpll0_map[] = { ++ { P_XO, 0 }, ++ { P_NSS_CRYPTO_PLL, 1 }, ++ { P_GPLL0, 2 }, ++}; ++ + static struct clk_rcg2 nss_crypto_clk_src = { + .cmd_rcgr = 0x68144, + .freq_tbl = ftbl_nss_crypto_clk_src, +@@ -1385,8 +1201,8 @@ static struct clk_rcg2 nss_crypto_clk_sr + .parent_map = gcc_xo_nss_crypto_pll_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_crypto_clk_src", +- .parent_names = gcc_xo_nss_crypto_pll_gpll0, +- .num_parents = 3, ++ .parent_data = gcc_xo_nss_crypto_pll_gpll0, ++ .num_parents = ARRAY_SIZE(gcc_xo_nss_crypto_pll_gpll0), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1400,6 +1216,24 @@ static const struct freq_tbl ftbl_nss_ub + { } + }; + ++static const struct clk_parent_data gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll2.clkr.hw }, ++ { .hw = &gpll4.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map[] = { ++ { P_XO, 0 }, ++ { P_UBI32_PLL, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL2, 3 }, ++ { P_GPLL4, 4 }, ++ { P_GPLL6, 5 }, ++}; ++ + static struct clk_rcg2 nss_ubi0_clk_src = { + .cmd_rcgr = 0x68104, + .freq_tbl = ftbl_nss_ubi_clk_src, +@@ -1407,8 +1241,8 @@ static struct clk_rcg2 nss_ubi0_clk_src + .parent_map = gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ubi0_clk_src", +- .parent_names = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, +- .num_parents = 6, ++ .parent_data = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, ++ .num_parents = ARRAY_SIZE(gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6), + .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1421,9 +1255,8 @@ static struct clk_regmap_div nss_ubi0_di + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_ubi0_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_ubi0_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi0_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1438,8 +1271,8 @@ static struct clk_rcg2 nss_ubi1_clk_src + .parent_map = gcc_xo_ubi32_gpll0_gpll2_gpll4_gpll6_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ubi1_clk_src", +- .parent_names = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, +- .num_parents = 6, ++ .parent_data = gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6, ++ .num_parents = ARRAY_SIZE(gcc_xo_ubi32_pll_gpll0_gpll2_gpll4_gpll6), + .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + }, +@@ -1452,9 +1285,8 @@ static struct clk_regmap_div nss_ubi1_di + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_ubi1_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_ubi1_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi1_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1468,6 +1300,16 @@ static const struct freq_tbl ftbl_ubi_mp + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_out_main_div2[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0_out_main_div2.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_out_main_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0_DIV2, 1 }, ++}; ++ + static struct clk_rcg2 ubi_mpt_clk_src = { + .cmd_rcgr = 0x68090, + .freq_tbl = ftbl_ubi_mpt_clk_src, +@@ -1475,8 +1317,8 @@ static struct clk_rcg2 ubi_mpt_clk_src = + .parent_map = gcc_xo_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ubi_mpt_clk_src", +- .parent_names = gcc_xo_gpll0_out_main_div2, +- .num_parents = 2, ++ .parent_data = gcc_xo_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1487,6 +1329,18 @@ static const struct freq_tbl ftbl_nss_im + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll4[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll4.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll4_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL4, 2 }, ++}; ++ + static struct clk_rcg2 nss_imem_clk_src = { + .cmd_rcgr = 0x68158, + .freq_tbl = ftbl_nss_imem_clk_src, +@@ -1494,8 +1348,8 @@ static struct clk_rcg2 nss_imem_clk_src + .parent_map = gcc_xo_gpll0_gpll4_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_imem_clk_src", +- .parent_names = gcc_xo_gpll0_gpll4, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll4, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll4), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1506,6 +1360,24 @@ static const struct freq_tbl ftbl_nss_pp + { } + }; + ++static const struct clk_parent_data gcc_xo_bias_gpll0_gpll4_nss_ubi32[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "bias_pll_cc_clk" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll4.clkr.hw }, ++ { .hw = &nss_crypto_pll.clkr.hw }, ++ { .hw = &ubi32_pll.clkr.hw }, ++}; ++ ++static const struct parent_map gcc_xo_bias_gpll0_gpll4_nss_ubi32_map[] = { ++ { P_XO, 0 }, ++ { P_BIAS_PLL, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL4, 3 }, ++ { P_NSS_CRYPTO_PLL, 4 }, ++ { P_UBI32_PLL, 5 }, ++}; ++ + static struct clk_rcg2 nss_ppe_clk_src = { + .cmd_rcgr = 0x68080, + .freq_tbl = ftbl_nss_ppe_clk_src, +@@ -1513,8 +1385,8 @@ static struct clk_rcg2 nss_ppe_clk_src = + .parent_map = gcc_xo_bias_gpll0_gpll4_nss_ubi32_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_ppe_clk_src", +- .parent_names = gcc_xo_bias_gpll0_gpll4_nss_ubi32, +- .num_parents = 6, ++ .parent_data = gcc_xo_bias_gpll0_gpll4_nss_ubi32, ++ .num_parents = ARRAY_SIZE(gcc_xo_bias_gpll0_gpll4_nss_ubi32), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1524,9 +1396,8 @@ static struct clk_fixed_factor nss_ppe_c + .div = 4, + .hw.init = &(struct clk_init_data){ + .name = "nss_ppe_cdiv_clk_src", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1540,6 +1411,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy0_rx_tx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_RX, 1 }, ++ { P_UNIPHY0_TX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port1_rx_clk_src = { + .cmd_rcgr = 0x68020, + .freq_tbl = ftbl_nss_port1_rx_clk_src, +@@ -1547,8 +1434,8 @@ static struct clk_rcg2 nss_port1_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port1_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1560,9 +1447,8 @@ static struct clk_regmap_div nss_port1_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port1_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port1_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1577,6 +1463,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy0_tx_rx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_TX, 1 }, ++ { P_UNIPHY0_RX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port1_tx_clk_src = { + .cmd_rcgr = 0x68028, + .freq_tbl = ftbl_nss_port1_tx_clk_src, +@@ -1584,8 +1486,8 @@ static struct clk_rcg2 nss_port1_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port1_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1597,9 +1499,8 @@ static struct clk_regmap_div nss_port1_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port1_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port1_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1614,8 +1515,8 @@ static struct clk_rcg2 nss_port2_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port2_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1627,9 +1528,8 @@ static struct clk_regmap_div nss_port2_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port2_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port2_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1644,8 +1544,8 @@ static struct clk_rcg2 nss_port2_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port2_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1657,9 +1557,8 @@ static struct clk_regmap_div nss_port2_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port2_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port2_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1674,8 +1573,8 @@ static struct clk_rcg2 nss_port3_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port3_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1687,9 +1586,8 @@ static struct clk_regmap_div nss_port3_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port3_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port3_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1704,8 +1602,8 @@ static struct clk_rcg2 nss_port3_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port3_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1717,9 +1615,8 @@ static struct clk_regmap_div nss_port3_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port3_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port3_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1734,8 +1631,8 @@ static struct clk_rcg2 nss_port4_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port4_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1747,9 +1644,8 @@ static struct clk_regmap_div nss_port4_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port4_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port4_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1764,8 +1660,8 @@ static struct clk_rcg2 nss_port4_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port4_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy0_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1777,9 +1673,8 @@ static struct clk_regmap_div nss_port4_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port4_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port4_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1799,6 +1694,27 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .name = "uniphy1_gcc_rx_clk" }, ++ { .name = "uniphy1_gcc_tx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map ++gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_RX, 1 }, ++ { P_UNIPHY0_TX, 2 }, ++ { P_UNIPHY1_RX, 3 }, ++ { P_UNIPHY1_TX, 4 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port5_rx_clk_src = { + .cmd_rcgr = 0x68060, + .freq_tbl = ftbl_nss_port5_rx_clk_src, +@@ -1806,8 +1722,8 @@ static struct clk_rcg2 nss_port5_rx_clk_ + .parent_map = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port5_rx_clk_src", +- .parent_names = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, +- .num_parents = 7, ++ .parent_data = gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1819,9 +1735,8 @@ static struct clk_regmap_div nss_port5_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port5_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port5_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1841,6 +1756,27 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy0_gcc_tx_clk" }, ++ { .name = "uniphy0_gcc_rx_clk" }, ++ { .name = "uniphy1_gcc_tx_clk" }, ++ { .name = "uniphy1_gcc_rx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map ++gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY0_TX, 1 }, ++ { P_UNIPHY0_RX, 2 }, ++ { P_UNIPHY1_TX, 3 }, ++ { P_UNIPHY1_RX, 4 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port5_tx_clk_src = { + .cmd_rcgr = 0x68068, + .freq_tbl = ftbl_nss_port5_tx_clk_src, +@@ -1848,8 +1784,8 @@ static struct clk_rcg2 nss_port5_tx_clk_ + .parent_map = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port5_tx_clk_src", +- .parent_names = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, +- .num_parents = 7, ++ .parent_data = gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1861,9 +1797,8 @@ static struct clk_regmap_div nss_port5_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port5_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port5_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1883,6 +1818,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy2_gcc_rx_clk" }, ++ { .name = "uniphy2_gcc_tx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY2_RX, 1 }, ++ { P_UNIPHY2_TX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port6_rx_clk_src = { + .cmd_rcgr = 0x68070, + .freq_tbl = ftbl_nss_port6_rx_clk_src, +@@ -1890,8 +1841,8 @@ static struct clk_rcg2 nss_port6_rx_clk_ + .parent_map = gcc_xo_uniphy2_rx_tx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port6_rx_clk_src", +- .parent_names = gcc_xo_uniphy2_rx_tx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy2_rx_tx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy2_rx_tx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1903,9 +1854,8 @@ static struct clk_regmap_div nss_port6_r + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port6_rx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port6_rx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_rx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1925,6 +1875,22 @@ static const struct freq_tbl ftbl_nss_po + { } + }; + ++static const struct clk_parent_data gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .name = "uniphy2_gcc_tx_clk" }, ++ { .name = "uniphy2_gcc_rx_clk" }, ++ { .hw = &ubi32_pll.clkr.hw }, ++ { .name = "bias_pll_cc_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { ++ { P_XO, 0 }, ++ { P_UNIPHY2_TX, 1 }, ++ { P_UNIPHY2_RX, 2 }, ++ { P_UBI32_PLL, 5 }, ++ { P_BIAS_PLL, 6 }, ++}; ++ + static struct clk_rcg2 nss_port6_tx_clk_src = { + .cmd_rcgr = 0x68078, + .freq_tbl = ftbl_nss_port6_tx_clk_src, +@@ -1932,8 +1898,8 @@ static struct clk_rcg2 nss_port6_tx_clk_ + .parent_map = gcc_xo_uniphy2_tx_rx_ubi32_bias_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "nss_port6_tx_clk_src", +- .parent_names = gcc_xo_uniphy2_tx_rx_ubi32_bias, +- .num_parents = 5, ++ .parent_data = gcc_xo_uniphy2_tx_rx_ubi32_bias, ++ .num_parents = ARRAY_SIZE(gcc_xo_uniphy2_tx_rx_ubi32_bias), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1945,9 +1911,8 @@ static struct clk_regmap_div nss_port6_t + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "nss_port6_tx_div_clk_src", +- .parent_names = (const char *[]){ +- "nss_port6_tx_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_tx_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + .flags = CLK_SET_RATE_PARENT, +@@ -1970,8 +1935,8 @@ static struct clk_rcg2 crypto_clk_src = + .parent_map = gcc_xo_gpll0_gpll0_out_main_div2_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "crypto_clk_src", +- .parent_names = gcc_xo_gpll0_gpll0_out_main_div2, +- .num_parents = 3, ++ .parent_data = gcc_xo_gpll0_gpll0_out_main_div2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll0_out_main_div2), + .ops = &clk_rcg2_ops, + }, + }; +@@ -1981,6 +1946,22 @@ static struct freq_tbl ftbl_gp_clk_src[] + { } + }; + ++static const struct clk_parent_data gcc_xo_gpll0_gpll6_gpll0_sleep_clk[] = { ++ { .fw_name = "xo", .name = "xo" }, ++ { .hw = &gpll0.clkr.hw }, ++ { .hw = &gpll6.clkr.hw }, ++ { .hw = &gpll0_out_main_div2.hw }, ++ { .fw_name = "sleep_clk", .name = "sleep_clk" }, ++}; ++ ++static const struct parent_map gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL0, 1 }, ++ { P_GPLL6, 2 }, ++ { P_GPLL0_DIV2, 4 }, ++ { P_SLEEP_CLK, 6 }, ++}; ++ + static struct clk_rcg2 gp1_clk_src = { + .cmd_rcgr = 0x08004, + .freq_tbl = ftbl_gp_clk_src, +@@ -1989,8 +1970,8 @@ static struct clk_rcg2 gp1_clk_src = { + .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp1_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, +- .num_parents = 5, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -2003,8 +1984,8 @@ static struct clk_rcg2 gp2_clk_src = { + .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp2_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, +- .num_parents = 5, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -2017,8 +1998,8 @@ static struct clk_rcg2 gp3_clk_src = { + .parent_map = gcc_xo_gpll0_gpll6_gpll0_sleep_clk_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp3_clk_src", +- .parent_names = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, +- .num_parents = 5, ++ .parent_data = gcc_xo_gpll0_gpll6_gpll0_sleep_clk, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0_gpll6_gpll0_sleep_clk), + .ops = &clk_rcg2_ops, + }, + }; +@@ -2030,9 +2011,8 @@ static struct clk_branch gcc_blsp1_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2047,9 +2027,8 @@ static struct clk_branch gcc_blsp1_qup1_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup1_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup1_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2064,9 +2043,8 @@ static struct clk_branch gcc_blsp1_qup1_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup1_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup1_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2081,9 +2059,8 @@ static struct clk_branch gcc_blsp1_qup2_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup2_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup2_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2098,9 +2075,8 @@ static struct clk_branch gcc_blsp1_qup2_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup2_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup2_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2115,9 +2091,8 @@ static struct clk_branch gcc_blsp1_qup3_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup3_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup3_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2132,9 +2107,8 @@ static struct clk_branch gcc_blsp1_qup3_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup3_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup3_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2149,9 +2123,8 @@ static struct clk_branch gcc_blsp1_qup4_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup4_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup4_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2166,9 +2139,8 @@ static struct clk_branch gcc_blsp1_qup4_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup4_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup4_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2183,9 +2155,8 @@ static struct clk_branch gcc_blsp1_qup5_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup5_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup5_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup5_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2200,9 +2171,8 @@ static struct clk_branch gcc_blsp1_qup5_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup5_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup5_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup5_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2217,9 +2187,8 @@ static struct clk_branch gcc_blsp1_qup6_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup6_i2c_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup6_i2c_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup6_i2c_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2234,9 +2203,8 @@ static struct clk_branch gcc_blsp1_qup6_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup6_spi_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_qup6_spi_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_qup6_spi_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2251,9 +2219,8 @@ static struct clk_branch gcc_blsp1_uart1 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart1_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart1_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart1_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2268,9 +2235,8 @@ static struct clk_branch gcc_blsp1_uart2 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart2_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart2_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart2_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2285,9 +2251,8 @@ static struct clk_branch gcc_blsp1_uart3 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart3_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart3_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart3_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2302,9 +2267,8 @@ static struct clk_branch gcc_blsp1_uart4 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart4_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart4_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart4_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2319,9 +2283,8 @@ static struct clk_branch gcc_blsp1_uart5 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart5_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart5_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart5_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2336,9 +2299,8 @@ static struct clk_branch gcc_blsp1_uart6 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart6_apps_clk", +- .parent_names = (const char *[]){ +- "blsp1_uart6_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &blsp1_uart6_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2354,9 +2316,8 @@ static struct clk_branch gcc_prng_ahb_cl + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "gcc_prng_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2371,9 +2332,8 @@ static struct clk_branch gcc_qpic_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qpic_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2388,9 +2348,8 @@ static struct clk_branch gcc_qpic_clk = + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qpic_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2405,9 +2364,8 @@ static struct clk_branch gcc_pcie0_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2422,9 +2380,8 @@ static struct clk_branch gcc_pcie0_aux_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_aux_clk", +- .parent_names = (const char *[]){ +- "pcie0_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2439,9 +2396,8 @@ static struct clk_branch gcc_pcie0_axi_m + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_axi_m_clk", +- .parent_names = (const char *[]){ +- "pcie0_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2456,9 +2412,8 @@ static struct clk_branch gcc_pcie0_axi_s + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_axi_s_clk", +- .parent_names = (const char *[]){ +- "pcie0_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2474,9 +2429,8 @@ static struct clk_branch gcc_pcie0_pipe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_pipe_clk", +- .parent_names = (const char *[]){ +- "pcie0_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2491,9 +2445,8 @@ static struct clk_branch gcc_sys_noc_pci + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_pcie0_axi_clk", +- .parent_names = (const char *[]){ +- "pcie0_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie0_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2508,9 +2461,8 @@ static struct clk_branch gcc_pcie1_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2525,9 +2477,8 @@ static struct clk_branch gcc_pcie1_aux_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_aux_clk", +- .parent_names = (const char *[]){ +- "pcie1_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2542,9 +2493,8 @@ static struct clk_branch gcc_pcie1_axi_m + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_axi_m_clk", +- .parent_names = (const char *[]){ +- "pcie1_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2559,9 +2509,8 @@ static struct clk_branch gcc_pcie1_axi_s + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_axi_s_clk", +- .parent_names = (const char *[]){ +- "pcie1_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2577,9 +2526,8 @@ static struct clk_branch gcc_pcie1_pipe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie1_pipe_clk", +- .parent_names = (const char *[]){ +- "pcie1_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2594,9 +2542,8 @@ static struct clk_branch gcc_sys_noc_pci + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_pcie1_axi_clk", +- .parent_names = (const char *[]){ +- "pcie1_axi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcie1_axi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2611,9 +2558,8 @@ static struct clk_branch gcc_usb0_aux_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_aux_clk", +- .parent_names = (const char *[]){ +- "usb0_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2628,9 +2574,8 @@ static struct clk_branch gcc_sys_noc_usb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb0_axi_clk", +- .parent_names = (const char *[]){ +- "usb0_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2645,9 +2590,8 @@ static struct clk_branch gcc_usb0_master + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_master_clk", +- .parent_names = (const char *[]){ +- "usb0_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2662,9 +2606,8 @@ static struct clk_branch gcc_usb0_mock_u + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_mock_utmi_clk", +- .parent_names = (const char *[]){ +- "usb0_mock_utmi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_mock_utmi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2679,9 +2622,8 @@ static struct clk_branch gcc_usb0_phy_cf + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_phy_cfg_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2697,9 +2639,8 @@ static struct clk_branch gcc_usb0_pipe_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_pipe_clk", +- .parent_names = (const char *[]){ +- "usb0_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb0_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2714,9 +2655,8 @@ static struct clk_branch gcc_usb0_sleep_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb0_sleep_clk", +- .parent_names = (const char *[]){ +- "gcc_sleep_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_sleep_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2731,9 +2671,8 @@ static struct clk_branch gcc_usb1_aux_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_aux_clk", +- .parent_names = (const char *[]){ +- "usb1_aux_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_aux_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2748,9 +2687,8 @@ static struct clk_branch gcc_sys_noc_usb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb1_axi_clk", +- .parent_names = (const char *[]){ +- "usb1_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2765,9 +2703,8 @@ static struct clk_branch gcc_usb1_master + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_master_clk", +- .parent_names = (const char *[]){ +- "usb1_master_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_master_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2782,9 +2719,8 @@ static struct clk_branch gcc_usb1_mock_u + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_mock_utmi_clk", +- .parent_names = (const char *[]){ +- "usb1_mock_utmi_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_mock_utmi_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2799,9 +2735,8 @@ static struct clk_branch gcc_usb1_phy_cf + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_phy_cfg_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2817,9 +2752,8 @@ static struct clk_branch gcc_usb1_pipe_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_pipe_clk", +- .parent_names = (const char *[]){ +- "usb1_pipe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &usb1_pipe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2834,9 +2768,8 @@ static struct clk_branch gcc_usb1_sleep_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb1_sleep_clk", +- .parent_names = (const char *[]){ +- "gcc_sleep_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_sleep_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2851,9 +2784,8 @@ static struct clk_branch gcc_sdcc1_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2868,9 +2800,8 @@ static struct clk_branch gcc_sdcc1_apps_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk", +- .parent_names = (const char *[]){ +- "sdcc1_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &sdcc1_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2885,9 +2816,8 @@ static struct clk_branch gcc_sdcc1_ice_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ice_core_clk", +- .parent_names = (const char *[]){ +- "sdcc1_ice_core_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &sdcc1_ice_core_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2902,9 +2832,8 @@ static struct clk_branch gcc_sdcc2_ahb_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2919,9 +2848,8 @@ static struct clk_branch gcc_sdcc2_apps_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_apps_clk", +- .parent_names = (const char *[]){ +- "sdcc2_apps_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &sdcc2_apps_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2936,9 +2864,8 @@ static struct clk_branch gcc_mem_noc_nss + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mem_noc_nss_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2953,9 +2880,8 @@ static struct clk_branch gcc_nss_ce_apb_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ce_apb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2970,9 +2896,8 @@ static struct clk_branch gcc_nss_ce_axi_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ce_axi_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -2987,9 +2912,8 @@ static struct clk_branch gcc_nss_cfg_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_cfg_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3004,9 +2928,8 @@ static struct clk_branch gcc_nss_crypto_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_crypto_clk", +- .parent_names = (const char *[]){ +- "nss_crypto_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_crypto_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3021,9 +2944,8 @@ static struct clk_branch gcc_nss_csr_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_csr_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3038,9 +2960,8 @@ static struct clk_branch gcc_nss_edma_cf + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_edma_cfg_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3055,9 +2976,8 @@ static struct clk_branch gcc_nss_edma_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_edma_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3072,9 +2992,8 @@ static struct clk_branch gcc_nss_imem_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_imem_clk", +- .parent_names = (const char *[]){ +- "nss_imem_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_imem_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3089,9 +3008,8 @@ static struct clk_branch gcc_nss_noc_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_noc_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3106,9 +3024,8 @@ static struct clk_branch gcc_nss_ppe_btq + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_btq_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3123,9 +3040,8 @@ static struct clk_branch gcc_nss_ppe_cfg + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_cfg_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3140,9 +3056,8 @@ static struct clk_branch gcc_nss_ppe_clk + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3157,9 +3072,8 @@ static struct clk_branch gcc_nss_ppe_ipe + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ppe_ipe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3174,9 +3088,8 @@ static struct clk_branch gcc_nss_ptp_ref + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_ptp_ref_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_cdiv_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_cdiv_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3192,9 +3105,8 @@ static struct clk_branch gcc_crypto_ppe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_ppe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3209,9 +3121,8 @@ static struct clk_branch gcc_nssnoc_ce_a + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ce_apb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3226,9 +3137,8 @@ static struct clk_branch gcc_nssnoc_ce_a + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ce_axi_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3243,9 +3153,8 @@ static struct clk_branch gcc_nssnoc_cryp + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_crypto_clk", +- .parent_names = (const char *[]){ +- "nss_crypto_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_crypto_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3260,9 +3169,8 @@ static struct clk_branch gcc_nssnoc_ppe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ppe_cfg_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3277,9 +3185,8 @@ static struct clk_branch gcc_nssnoc_ppe_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ppe_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3294,9 +3201,8 @@ static struct clk_branch gcc_nssnoc_qosg + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_qosgen_ref_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3311,9 +3217,8 @@ static struct clk_branch gcc_nssnoc_snoc + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_snoc_clk", +- .parent_names = (const char *[]){ +- "system_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &system_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3328,9 +3233,8 @@ static struct clk_branch gcc_nssnoc_time + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_timeout_ref_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_div4_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_div4_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3345,9 +3249,8 @@ static struct clk_branch gcc_nssnoc_ubi0 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ubi0_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3362,9 +3265,8 @@ static struct clk_branch gcc_nssnoc_ubi1 + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nssnoc_ubi1_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3380,9 +3282,8 @@ static struct clk_branch gcc_ubi0_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3398,9 +3299,8 @@ static struct clk_branch gcc_ubi0_axi_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3416,9 +3316,8 @@ static struct clk_branch gcc_ubi0_nc_axi + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_nc_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3434,9 +3333,8 @@ static struct clk_branch gcc_ubi0_core_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_core_clk", +- .parent_names = (const char *[]){ +- "nss_ubi0_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi0_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3452,9 +3350,8 @@ static struct clk_branch gcc_ubi0_mpt_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi0_mpt_clk", +- .parent_names = (const char *[]){ +- "ubi_mpt_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &ubi_mpt_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3470,9 +3367,8 @@ static struct clk_branch gcc_ubi1_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_ahb_clk", +- .parent_names = (const char *[]){ +- "nss_ce_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ce_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3488,9 +3384,8 @@ static struct clk_branch gcc_ubi1_axi_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3506,9 +3401,8 @@ static struct clk_branch gcc_ubi1_nc_axi + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_nc_axi_clk", +- .parent_names = (const char *[]){ +- "nss_noc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_noc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3524,9 +3418,8 @@ static struct clk_branch gcc_ubi1_core_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_core_clk", +- .parent_names = (const char *[]){ +- "nss_ubi1_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ubi1_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3542,9 +3435,8 @@ static struct clk_branch gcc_ubi1_mpt_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ubi1_mpt_clk", +- .parent_names = (const char *[]){ +- "ubi_mpt_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &ubi_mpt_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3559,9 +3451,8 @@ static struct clk_branch gcc_cmn_12gpll_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cmn_12gpll_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3576,9 +3467,8 @@ static struct clk_branch gcc_cmn_12gpll_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cmn_12gpll_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3593,9 +3483,8 @@ static struct clk_branch gcc_mdio_ahb_cl + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mdio_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3610,9 +3499,8 @@ static struct clk_branch gcc_uniphy0_ahb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3627,9 +3515,8 @@ static struct clk_branch gcc_uniphy0_sys + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3644,9 +3531,8 @@ static struct clk_branch gcc_uniphy1_ahb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3661,9 +3547,8 @@ static struct clk_branch gcc_uniphy1_sys + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3678,9 +3563,8 @@ static struct clk_branch gcc_uniphy2_ahb + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3695,9 +3579,8 @@ static struct clk_branch gcc_uniphy2_sys + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_sys_clk", +- .parent_names = (const char *[]){ +- "gcc_xo_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gcc_xo_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3712,9 +3595,8 @@ static struct clk_branch gcc_nss_port1_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port1_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3729,9 +3611,8 @@ static struct clk_branch gcc_nss_port1_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port1_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3746,9 +3627,8 @@ static struct clk_branch gcc_nss_port2_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port2_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3763,9 +3643,8 @@ static struct clk_branch gcc_nss_port2_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port2_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3780,9 +3659,8 @@ static struct clk_branch gcc_nss_port3_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port3_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3797,9 +3675,8 @@ static struct clk_branch gcc_nss_port3_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port3_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3814,9 +3691,8 @@ static struct clk_branch gcc_nss_port4_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port4_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3831,9 +3707,8 @@ static struct clk_branch gcc_nss_port4_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port4_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3848,9 +3723,8 @@ static struct clk_branch gcc_nss_port5_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port5_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3865,9 +3739,8 @@ static struct clk_branch gcc_nss_port5_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port5_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3882,9 +3755,8 @@ static struct clk_branch gcc_nss_port6_r + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port6_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3899,9 +3771,8 @@ static struct clk_branch gcc_nss_port6_t + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_nss_port6_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3916,9 +3787,8 @@ static struct clk_branch gcc_port1_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port1_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3933,9 +3803,8 @@ static struct clk_branch gcc_port2_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port2_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3950,9 +3819,8 @@ static struct clk_branch gcc_port3_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port3_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3967,9 +3835,8 @@ static struct clk_branch gcc_port4_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port4_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -3984,9 +3851,8 @@ static struct clk_branch gcc_port5_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port5_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4001,9 +3867,8 @@ static struct clk_branch gcc_port6_mac_c + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_port6_mac_clk", +- .parent_names = (const char *[]){ +- "nss_ppe_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_ppe_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4018,9 +3883,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port1_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4035,9 +3899,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port1_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port1_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port1_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4052,9 +3915,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port2_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4069,9 +3931,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port2_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port2_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port2_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4086,9 +3947,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port3_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4103,9 +3963,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port3_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port3_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port3_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4120,9 +3979,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port4_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4137,9 +3995,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port4_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port4_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port4_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4154,9 +4011,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port5_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4171,9 +4027,8 @@ static struct clk_branch gcc_uniphy0_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy0_port5_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4188,9 +4043,8 @@ static struct clk_branch gcc_uniphy1_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_port5_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4205,9 +4059,8 @@ static struct clk_branch gcc_uniphy1_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy1_port5_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port5_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port5_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4222,9 +4075,8 @@ static struct clk_branch gcc_uniphy2_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_port6_rx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_rx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_rx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4239,9 +4091,8 @@ static struct clk_branch gcc_uniphy2_por + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_uniphy2_port6_tx_clk", +- .parent_names = (const char *[]){ +- "nss_port6_tx_div_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &nss_port6_tx_div_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4257,9 +4108,8 @@ static struct clk_branch gcc_crypto_ahb_ + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_ahb_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4275,9 +4125,8 @@ static struct clk_branch gcc_crypto_axi_ + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_axi_clk", +- .parent_names = (const char *[]){ +- "pcnoc_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &pcnoc_clk_src.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4293,9 +4142,8 @@ static struct clk_branch gcc_crypto_clk + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_clk", +- .parent_names = (const char *[]){ +- "crypto_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &crypto_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4310,9 +4158,8 @@ static struct clk_branch gcc_gp1_clk = { + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp1_clk", +- .parent_names = (const char *[]){ +- "gp1_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gp1_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4327,9 +4174,8 @@ static struct clk_branch gcc_gp2_clk = { + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp2_clk", +- .parent_names = (const char *[]){ +- "gp2_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gp2_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4344,9 +4190,8 @@ static struct clk_branch gcc_gp3_clk = { + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp3_clk", +- .parent_names = (const char *[]){ +- "gp3_clk_src" +- }, ++ .parent_hws = (const struct clk_hw *[]){ ++ &gp3_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, +@@ -4368,7 +4213,7 @@ static struct clk_rcg2 pcie0_rchng_clk_s + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_rchng_clk_src", + .parent_data = gcc_xo_gpll0, +- .num_parents = 2, ++ .num_parents = ARRAY_SIZE(gcc_xo_gpll0), + .ops = &clk_rcg2_ops, + }, + }; diff --git a/target/linux/qualcommax/patches-6.1/0008-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch b/target/linux/qualcommax/patches-6.1/0008-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch new file mode 100644 index 0000000000..e0725a6612 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0008-v6.2-clk-qcom-reset-support-resetting-multiple-bits.patch @@ -0,0 +1,59 @@ +From 813ba3e427671ba3ff35c825087b03f0ad91cf02 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 14:28:59 +0100 +Subject: [PATCH] clk: qcom: reset: support resetting multiple bits + +This patch adds the support for giving the complete bitmask +in reset structure and reset operation will use this bitmask +for all reset operations. + +Currently, reset structure only takes a single bit for each reset +and then calculates the bitmask by using the BIT() macro. + +However, this is not sufficient anymore for newer SoC-s like IPQ8074, +IPQ6018 and more, since their networking resets require multiple bits +to be asserted in order to properly reset the HW block completely. + +So, in order to allow asserting multiple bits add "bitmask" field to +qcom_reset_map, and then use that bitmask value if its populated in the +driver, if its not populated, then we just default to existing behaviour +and calculate the bitmask on the fly. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107132901.489240-1-robimarko@gmail.com +--- + drivers/clk/qcom/reset.c | 4 ++-- + drivers/clk/qcom/reset.h | 1 + + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/reset.c ++++ b/drivers/clk/qcom/reset.c +@@ -30,7 +30,7 @@ qcom_reset_assert(struct reset_controlle + + rst = to_qcom_reset_controller(rcdev); + map = &rst->reset_map[id]; +- mask = BIT(map->bit); ++ mask = map->bitmask ? map->bitmask : BIT(map->bit); + + return regmap_update_bits(rst->regmap, map->reg, mask, mask); + } +@@ -44,7 +44,7 @@ qcom_reset_deassert(struct reset_control + + rst = to_qcom_reset_controller(rcdev); + map = &rst->reset_map[id]; +- mask = BIT(map->bit); ++ mask = map->bitmask ? map->bitmask : BIT(map->bit); + + return regmap_update_bits(rst->regmap, map->reg, mask, 0); + } +--- a/drivers/clk/qcom/reset.h ++++ b/drivers/clk/qcom/reset.h +@@ -12,6 +12,7 @@ struct qcom_reset_map { + unsigned int reg; + u8 bit; + u8 udelay; ++ u32 bitmask; + }; + + struct regmap; diff --git a/target/linux/qualcommax/patches-6.1/0009-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch b/target/linux/qualcommax/patches-6.1/0009-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch new file mode 100644 index 0000000000..75f16a1673 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0009-v6.2-dt-bindings-clock-qcom-ipq8074-add-missing-networkin.patch @@ -0,0 +1,39 @@ +From e78a40eb24187a8b4f9b89e2181f674df39c2013 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 14:29:00 +0100 +Subject: [PATCH] dt-bindings: clock: qcom: ipq8074: add missing networking + resets + +Add bindings for the missing networking resets found in IPQ8074 GCC. + +Signed-off-by: Robert Marko +Acked-by: Krzysztof Kozlowski +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107132901.489240-2-robimarko@gmail.com +--- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -367,6 +367,20 @@ + #define GCC_PCIE1_AHB_ARES 129 + #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 + #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 ++#define GCC_PPE_FULL_RESET 132 ++#define GCC_UNIPHY0_SOFT_RESET 133 ++#define GCC_UNIPHY0_XPCS_RESET 134 ++#define GCC_UNIPHY1_SOFT_RESET 135 ++#define GCC_UNIPHY1_XPCS_RESET 136 ++#define GCC_UNIPHY2_SOFT_RESET 137 ++#define GCC_UNIPHY2_XPCS_RESET 138 ++#define GCC_EDMA_HW_RESET 139 ++#define GCC_NSSPORT1_RESET 140 ++#define GCC_NSSPORT2_RESET 141 ++#define GCC_NSSPORT3_RESET 142 ++#define GCC_NSSPORT4_RESET 143 ++#define GCC_NSSPORT5_RESET 144 ++#define GCC_NSSPORT6_RESET 145 + + #define USB0_GDSC 0 + #define USB1_GDSC 1 diff --git a/target/linux/qualcommax/patches-6.1/0010-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch b/target/linux/qualcommax/patches-6.1/0010-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch new file mode 100644 index 0000000000..212fc84869 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0010-v6.2-clk-qcom-ipq8074-add-missing-networking-resets.patch @@ -0,0 +1,41 @@ +From da76cb63d04dc22ed32123b8c1d084c006d67bfb Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 14:29:01 +0100 +Subject: [PATCH] clk: qcom: ipq8074: add missing networking resets + +Downstream QCA 5.4 kernel defines networking resets which are not present +in the mainline kernel but are required for the networking drivers. + +So, port the downstream resets and avoid using magic values for mask, +construct mask for resets which require multiple bits to be set/cleared. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107132901.489240-3-robimarko@gmail.com +--- + drivers/clk/qcom/gcc-ipq8074.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4671,6 +4671,20 @@ static const struct qcom_reset_map gcc_i + [GCC_PCIE1_AXI_SLAVE_ARES] = { 0x76040, 4 }, + [GCC_PCIE1_AHB_ARES] = { 0x76040, 5 }, + [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, ++ [GCC_PPE_FULL_RESET] = { .reg = 0x68014, .bitmask = GENMASK(19, 16) }, ++ [GCC_UNIPHY0_SOFT_RESET] = { .reg = 0x56004, .bitmask = GENMASK(13, 4) | BIT(1) }, ++ [GCC_UNIPHY0_XPCS_RESET] = { 0x56004, 2 }, ++ [GCC_UNIPHY1_SOFT_RESET] = { .reg = 0x56104, .bitmask = GENMASK(5, 4) | BIT(1) }, ++ [GCC_UNIPHY1_XPCS_RESET] = { 0x56104, 2 }, ++ [GCC_UNIPHY2_SOFT_RESET] = { .reg = 0x56204, .bitmask = GENMASK(5, 4) | BIT(1) }, ++ [GCC_UNIPHY2_XPCS_RESET] = { 0x56204, 2 }, ++ [GCC_EDMA_HW_RESET] = { .reg = 0x68014, .bitmask = GENMASK(21, 20) }, ++ [GCC_NSSPORT1_RESET] = { .reg = 0x68014, .bitmask = BIT(24) | GENMASK(1, 0) }, ++ [GCC_NSSPORT2_RESET] = { .reg = 0x68014, .bitmask = BIT(25) | GENMASK(3, 2) }, ++ [GCC_NSSPORT3_RESET] = { .reg = 0x68014, .bitmask = BIT(26) | GENMASK(5, 4) }, ++ [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) }, + }; + + static struct gdsc *gcc_ipq8074_gdscs[] = { diff --git a/target/linux/qualcommax/patches-6.1/0011-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch b/target/linux/qualcommax/patches-6.1/0011-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch new file mode 100644 index 0000000000..7372b1da8e --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0011-v6.2-clk-qcom-ipq8074-populate-fw_name-for-all-parents.patch @@ -0,0 +1,152 @@ +From 78936d46470938caa9a7ea529deeb36777b4f98e Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:46:55 +0100 +Subject: [PATCH] clk: qcom: ipq8074: populate fw_name for all parents + +It appears that having only .name populated in parent_data for clocks +which are only globally searchable currently will not work as the clk core +won't copy that name if there is no .fw_name present as well. + +So, populate .fw_name for all parent clocks in parent_data. + +Fixes: ae55ad32e273 ("clk: qcom: ipq8074: convert to parent data") + +Co-developed-by: Christian Marangi +Signed-off-by: Christian Marangi +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221116214655.1116467-1-robimarko@gmail.com +--- + drivers/clk/qcom/gcc-ipq8074.c | 52 +++++++++++++++++----------------- + 1 file changed, 26 insertions(+), 26 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -680,7 +680,7 @@ static struct clk_rcg2 pcie0_aux_clk_src + }; + + static const struct clk_parent_data gcc_pcie20_phy0_pipe_clk_xo[] = { +- { .name = "pcie20_phy0_pipe_clk" }, ++ { .fw_name = "pcie0_pipe", .name = "pcie20_phy0_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + +@@ -733,7 +733,7 @@ static struct clk_rcg2 pcie1_aux_clk_src + }; + + static const struct clk_parent_data gcc_pcie20_phy1_pipe_clk_xo[] = { +- { .name = "pcie20_phy1_pipe_clk" }, ++ { .fw_name = "pcie1_pipe", .name = "pcie20_phy1_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + +@@ -1137,7 +1137,7 @@ static const struct freq_tbl ftbl_nss_no + + static const struct clk_parent_data gcc_xo_bias_pll_nss_noc_clk_gpll0_gpll2[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "bias_pll_nss_noc_clk" }, ++ { .fw_name = "bias_pll_nss_noc_clk", .name = "bias_pll_nss_noc_clk" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, + }; +@@ -1362,7 +1362,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", .name = "xo" }, +- { .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 }, +@@ -1413,10 +1413,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", .name = "xo" }, +- { .name = "uniphy0_gcc_rx_clk" }, +- { .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 }, +- { .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[] = { +@@ -1465,10 +1465,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", .name = "xo" }, +- { .name = "uniphy0_gcc_tx_clk" }, +- { .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 }, +- { .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[] = { +@@ -1696,12 +1696,12 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy0_rx_tx_uniphy1_rx_tx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy0_gcc_rx_clk" }, +- { .name = "uniphy0_gcc_tx_clk" }, +- { .name = "uniphy1_gcc_rx_clk" }, +- { .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 }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map +@@ -1758,12 +1758,12 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy0_tx_rx_uniphy1_tx_rx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy0_gcc_tx_clk" }, +- { .name = "uniphy0_gcc_rx_clk" }, +- { .name = "uniphy1_gcc_tx_clk" }, +- { .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 }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map +@@ -1820,10 +1820,10 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy2_rx_tx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy2_gcc_rx_clk" }, +- { .name = "uniphy2_gcc_tx_clk" }, ++ { .fw_name = "uniphy2_gcc_rx_clk", .name = "uniphy2_gcc_rx_clk" }, ++ { .fw_name = "uniphy2_gcc_tx_clk", .name = "uniphy2_gcc_tx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map gcc_xo_uniphy2_rx_tx_ubi32_bias_map[] = { +@@ -1877,10 +1877,10 @@ static const struct freq_tbl ftbl_nss_po + + static const struct clk_parent_data gcc_xo_uniphy2_tx_rx_ubi32_bias[] = { + { .fw_name = "xo", .name = "xo" }, +- { .name = "uniphy2_gcc_tx_clk" }, +- { .name = "uniphy2_gcc_rx_clk" }, ++ { .fw_name = "uniphy2_gcc_tx_clk", .name = "uniphy2_gcc_tx_clk" }, ++ { .fw_name = "uniphy2_gcc_rx_clk", .name = "uniphy2_gcc_rx_clk" }, + { .hw = &ubi32_pll.clkr.hw }, +- { .name = "bias_pll_cc_clk" }, ++ { .fw_name = "bias_pll_cc_clk", .name = "bias_pll_cc_clk" }, + }; + + static const struct parent_map gcc_xo_uniphy2_tx_rx_ubi32_bias_map[] = { diff --git a/target/linux/qualcommax/patches-6.1/0012-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch b/target/linux/qualcommax/patches-6.1/0012-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch new file mode 100644 index 0000000000..1f99de002b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0012-v6.2-arm64-dts-qcom-ipq8074-pass-XO-and-sleep-clocks-to-G.patch @@ -0,0 +1,36 @@ +From 9033c3c86ea0dd35bd2ab957317573b755967298 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 30 Oct 2022 18:57:03 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: pass XO and sleep clocks to GCC + +Pass XO and sleep clocks to the GCC controller so it does not have to +find them by matching globaly by name. + +If not passed directly, driver maintains backwards compatibility by then +falling back to global lookup. + +Since we are here, set cell numbers in decimal instead of hex. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221030175703.1103224-3-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -361,9 +361,11 @@ + gcc: gcc@1800000 { + compatible = "qcom,gcc-ipq8074"; + reg = <0x01800000 0x80000>; +- #clock-cells = <0x1>; ++ clocks = <&xo>, <&sleep_clk>; ++ clock-names = "xo", "sleep_clk"; ++ #clock-cells = <1>; + #power-domain-cells = <1>; +- #reset-cells = <0x1>; ++ #reset-cells = <1>; + }; + + tcsr_mutex: hwlock@1905000 { diff --git a/target/linux/qualcommax/patches-6.1/0013-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch b/target/linux/qualcommax/patches-6.1/0013-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch new file mode 100644 index 0000000000..cd146420cf --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0013-v6.2-arm64-dts-qcom-add-PMP8074-DTSI.patch @@ -0,0 +1,149 @@ +From fb76b808f8628215afebaf0f8af0bde635302590 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:18:14 +0200 +Subject: [PATCH] arm64: dts: qcom: add PMP8074 DTSI + +PMP8074 is a companion PMIC to the Qualcomm IPQ8074 series that is +controlled via SPMI. + +Add DTSI for it providing GPIO, regulator, RTC and VADC support. + +RTC is disabled by default as there is no built-in battery so it will +loose time unless board vendor added a battery, so make it optional. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818221815.346233-4-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/pmp8074.dtsi | 125 ++++++++++++++++++++++++++ + 1 file changed, 125 insertions(+) + create mode 100644 arch/arm64/boot/dts/qcom/pmp8074.dtsi + +--- /dev/null ++++ b/arch/arm64/boot/dts/qcom/pmp8074.dtsi +@@ -0,0 +1,125 @@ ++// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause ++ ++#include ++#include ++ ++&spmi_bus { ++ pmic@0 { ++ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; ++ reg = <0x0 SPMI_USID>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pmp8074_adc: adc@3100 { ++ compatible = "qcom,spmi-adc-rev2"; ++ reg = <0x3100>; ++ interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #io-channel-cells = <1>; ++ ++ ref-gnd@0 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vref-1p25@1 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vref-vadc@2 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pmic_die: die-temp@6 { ++ reg = ; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ xo_therm: xo-temp@76 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm1: thermistor1@77 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm2: thermistor2@78 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ pa_therm3: thermistor3@79 { ++ reg = ; ++ qcom,ratiometric; ++ qcom,hw-settle-time = <200>; ++ qcom,pre-scaling = <1 1>; ++ }; ++ ++ vph-pwr@131 { ++ reg = ; ++ qcom,pre-scaling = <1 3>; ++ }; ++ }; ++ ++ pmp8074_rtc: rtc@6000 { ++ compatible = "qcom,pm8941-rtc"; ++ reg = <0x6000>; ++ reg-names = "rtc", "alarm"; ++ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>; ++ allow-set-time; ++ status = "disabled"; ++ }; ++ ++ pmp8074_gpios: gpio@c000 { ++ compatible = "qcom,pmp8074-gpio", "qcom,spmi-gpio"; ++ reg = <0xc000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmp8074_gpios 0 0 12>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ }; ++ ++ pmic@1 { ++ compatible = "qcom,pmp8074", "qcom,spmi-pmic"; ++ reg = <0x1 SPMI_USID>; ++ ++ regulators { ++ compatible = "qcom,pmp8074-regulators"; ++ ++ s3: s3 { ++ regulator-name = "vdd_s3"; ++ regulator-min-microvolt = <592000>; ++ regulator-max-microvolt = <1064000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ s4: s4 { ++ regulator-name = "vdd_s4"; ++ regulator-min-microvolt = <712000>; ++ regulator-max-microvolt = <992000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ l11: l11 { ++ regulator-name = "l11"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; ++ }; ++}; diff --git a/target/linux/qualcommax/patches-6.1/0014-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch b/target/linux/qualcommax/patches-6.1/0014-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch new file mode 100644 index 0000000000..ebd3763a58 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0014-v6.2-arm64-dts-qcom-ipq8074-hk01-add-VQMMC-supply.patch @@ -0,0 +1,37 @@ +From 2c394cfc1779886048feca7dc7f4075da5f6328c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 19 Aug 2022 00:18:15 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074-hk01: add VQMMC supply + +Since now we have control over the PMP8074 PMIC providing various system +voltages including L11 which provides the SDIO/eMMC I/O voltage set it as +the SDHCI VQMMC supply. + +This allows SDHCI controller to switch to 1.8V I/O mode and support high +speed modes like HS200 and HS400. + +Signed-off-by: Robert Marko +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20220818221815.346233-5-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -3,6 +3,7 @@ + /* Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + #include "ipq8074.dtsi" ++#include "pmp8074.dtsi" + + / { + model = "Qualcomm Technologies, Inc. IPQ8074-HK01"; +@@ -84,6 +85,7 @@ + + &sdhc_1 { + status = "okay"; ++ vqmmc-supply = <&l11>; + }; + + &qusb_phy_0 { diff --git a/target/linux/qualcommax/patches-6.1/0015-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch b/target/linux/qualcommax/patches-6.1/0015-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch new file mode 100644 index 0000000000..e08f6d1f3c --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0015-v6.2-arm64-dts-qcom-hk01-use-GPIO-flags-for-tlmm.patch @@ -0,0 +1,42 @@ +From 82ceb86227b1fc15c76d5fc691b2bf425f1a63b3 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 7 Nov 2022 10:29:30 +0100 +Subject: [PATCH] arm64: dts: qcom: hk01: use GPIO flags for tlmm + +Use respective GPIO_ACTIVE_LOW/HIGH flags for tlmm GPIOs instead of +harcoding the cell value. + +Signed-off-by: Robert Marko +Reviewed-by: Krzysztof Kozlowski +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107092930.33325-3-robimarko@gmail.com +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -4,6 +4,7 @@ + */ + #include "ipq8074.dtsi" + #include "pmp8074.dtsi" ++#include + + / { + model = "Qualcomm Technologies, Inc. IPQ8074-HK01"; +@@ -52,12 +53,12 @@ + + &pcie0 { + status = "okay"; +- perst-gpios = <&tlmm 61 0x1>; ++ perst-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>; + }; + + &pcie1 { + status = "okay"; +- perst-gpios = <&tlmm 58 0x1>; ++ perst-gpios = <&tlmm 58 GPIO_ACTIVE_LOW>; + }; + + &pcie_qmp0 { diff --git a/target/linux/qualcommax/patches-6.1/0016-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch b/target/linux/qualcommax/patches-6.1/0016-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch new file mode 100644 index 0000000000..a8bf2492f4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0016-v6.2-arm64-dts-qcom-ipq8074-Fix-up-comments.patch @@ -0,0 +1,82 @@ +From 1b1c1423ca3e740984aa883512a72c4ea08fbe28 Mon Sep 17 00:00:00 2001 +From: Konrad Dybcio +Date: Mon, 7 Nov 2022 15:55:17 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074-*: Fix up comments + +Make sure all multiline C-style commends begin with just '/*' with +the comment text starting on a new line. + +Also, fix up some whitespace within comments. + +Signed-off-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221107145522.6706-8-konrad.dybcio@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074-hk01.dts | 3 ++- + arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts | 3 ++- + arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts | 3 ++- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 12 ++++++------ + 4 files changed, 12 insertions(+), 9 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk01.dts +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + /dts-v1/; +-/* Copyright (c) 2017, The Linux Foundation. All rights reserved. ++/* ++ * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + #include "ipq8074.dtsi" + #include "pmp8074.dtsi" +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk10-c1.dts +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0-only +-/* Copyright (c) 2020 The Linux Foundation. All rights reserved. ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + /dts-v1/; + +--- a/arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8074-hk10-c2.dts +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + /dts-v1/; +-/* Copyright (c) 2020 The Linux Foundation. All rights reserved. ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + #include "ipq8074-hk10.dtsi" + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -129,10 +129,10 @@ + status = "disabled"; + + usb1_ssphy: phy@58200 { +- reg = <0x00058200 0x130>, /* Tx */ ++ reg = <0x00058200 0x130>, /* Tx */ + <0x00058400 0x200>, /* Rx */ +- <0x00058800 0x1f8>, /* PCS */ +- <0x00058600 0x044>; /* PCS misc*/ ++ <0x00058800 0x1f8>, /* PCS */ ++ <0x00058600 0x044>; /* PCS misc */ + #phy-cells = <0>; + #clock-cells = <0>; + clocks = <&gcc GCC_USB1_PIPE_CLK>; +@@ -172,10 +172,10 @@ + status = "disabled"; + + usb0_ssphy: phy@78200 { +- reg = <0x00078200 0x130>, /* Tx */ ++ reg = <0x00078200 0x130>, /* Tx */ + <0x00078400 0x200>, /* Rx */ +- <0x00078800 0x1f8>, /* PCS */ +- <0x00078600 0x044>; /* PCS misc*/ ++ <0x00078800 0x1f8>, /* PCS */ ++ <0x00078600 0x044>; /* PCS misc */ + #phy-cells = <0>; + #clock-cells = <0>; + clocks = <&gcc GCC_USB0_PIPE_CLK>; diff --git a/target/linux/qualcommax/patches-6.1/0017-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch b/target/linux/qualcommax/patches-6.1/0017-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch new file mode 100644 index 0000000000..1ce1140682 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0017-v6.2-arm64-dts-qcom-ipq8074-align-TLMM-pin-configuration-.patch @@ -0,0 +1,60 @@ +From 5f20690f77878b1ba24ec88df01b92d5131a6780 Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +Date: Tue, 8 Nov 2022 15:23:57 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: align TLMM pin configuration with + DT schema + +DT schema expects TLMM pin configuration nodes to be named with +'-state' suffix and their optional children with '-pins' suffix. + +Signed-off-by: Krzysztof Kozlowski +Reviewed-by: Konrad Dybcio +Signed-off-by: Bjorn Andersson +Link: https://lore.kernel.org/r/20221108142357.67202-2-krzysztof.kozlowski@linaro.org +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -318,35 +318,35 @@ + interrupt-controller; + #interrupt-cells = <0x2>; + +- serial_4_pins: serial4-pinmux { ++ serial_4_pins: serial4-state { + pins = "gpio23", "gpio24"; + function = "blsp4_uart1"; + drive-strength = <8>; + bias-disable; + }; + +- i2c_0_pins: i2c-0-pinmux { ++ i2c_0_pins: i2c-0-state { + pins = "gpio42", "gpio43"; + function = "blsp1_i2c"; + drive-strength = <8>; + bias-disable; + }; + +- spi_0_pins: spi-0-pins { ++ spi_0_pins: spi-0-state { + pins = "gpio38", "gpio39", "gpio40", "gpio41"; + function = "blsp0_spi"; + drive-strength = <8>; + bias-disable; + }; + +- hsuart_pins: hsuart-pins { ++ hsuart_pins: hsuart-state { + pins = "gpio46", "gpio47", "gpio48", "gpio49"; + function = "blsp2_uart"; + drive-strength = <8>; + bias-disable; + }; + +- qpic_pins: qpic-pins { ++ qpic_pins: qpic-state { + pins = "gpio1", "gpio3", "gpio4", + "gpio5", "gpio6", "gpio7", + "gpio8", "gpio10", "gpio11", diff --git a/target/linux/qualcommax/patches-6.1/0019-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch b/target/linux/qualcommax/patches-6.1/0019-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch new file mode 100644 index 0000000000..8719bf74c4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0019-v6.3-arm64-dts-qcom-ipq8074-set-Gen2-PCIe-pcie-max-link-s.patch @@ -0,0 +1,24 @@ +From a4748d2850783d36f77ccf2b5fcc86ccf1800ef1 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:48:36 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: set Gen2 PCIe pcie max-link-speed + +Add the generic 'max-link-speed' property to describe the Gen2 PCIe link +generation limit. +This allows the generic DWC code to configure the link speed correctly. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -766,6 +766,7 @@ + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <1>; ++ max-link-speed = <2>; + #address-cells = <3>; + #size-cells = <2>; + diff --git a/target/linux/qualcommax/patches-6.1/0020-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch b/target/linux/qualcommax/patches-6.1/0020-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch new file mode 100644 index 0000000000..60caee46ca --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0020-v6.3-PCI-qcom-Add-support-for-IPQ8074-Gen3-port.patch @@ -0,0 +1,26 @@ +From f356132229b18ceef5d5ef9103bbaa9bdeb84c8d Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Fri, 13 Jan 2023 17:44:47 +0100 +Subject: [PATCH] PCI: qcom: Add IPQ8074 Gen3 port support + +IPQ8074 has one Gen2 and one Gen3 port, with Gen2 port already supported. +Add compatible for Gen3 port which uses the same controller as IPQ6018. + +Link: https://lore.kernel.org/r/20230113164449.906002-7-robimarko@gmail.com +Signed-off-by: Robert Marko +Signed-off-by: Lorenzo Pieralisi +Signed-off-by: Bjorn Helgaas +--- + 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 +@@ -1745,6 +1745,7 @@ static const struct of_device_id qcom_pc + { .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 }, + { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 }, + { .compatible = "qcom,pcie-ipq8074", .data = &cfg_2_3_3 }, ++ { .compatible = "qcom,pcie-ipq8074-gen3", .data = &cfg_2_9_0 }, + { .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 }, + { .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 }, + { .compatible = "qcom,pcie-sa8540p", .data = &cfg_1_9_0 }, diff --git a/target/linux/qualcommax/patches-6.1/0021-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch b/target/linux/qualcommax/patches-6.1/0021-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch new file mode 100644 index 0000000000..eb772be4ce --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0021-v6.3-clk-qcom-ipq8074-populate-fw_name-for-usb3phy-s.patch @@ -0,0 +1,38 @@ +From 614d31c231c7707322b643f409eeb7e28adc7f8c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sun, 8 Jan 2023 13:36:28 +0100 +Subject: [PATCH] clk: qcom: ipq8074: populate fw_name for usb3phy-s + +Having only .name populated in parent_data for clocks which are only +globally searchable currently will not work as the clk core won't copy +that name if there is no .fw_name present as well. + +So, populate .fw_name for usb3phy clocks in parent_data as they were +missed by me in ("clk: qcom: ipq8074: populate fw_name for all parents"). + +Fixes: ae55ad32e273 ("clk: qcom: ipq8074: convert to parent data") +Signed-off-by: Robert Marko +--- + drivers/clk/qcom/gcc-ipq8074.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -934,7 +934,7 @@ static struct clk_rcg2 usb0_mock_utmi_cl + }; + + static const struct clk_parent_data gcc_usb3phy_0_cc_pipe_clk_xo[] = { +- { .name = "usb3phy_0_cc_pipe_clk" }, ++ { .fw_name = "usb3phy_0_cc_pipe_clk", .name = "usb3phy_0_cc_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + +@@ -1002,7 +1002,7 @@ static struct clk_rcg2 usb1_mock_utmi_cl + }; + + static const struct clk_parent_data gcc_usb3phy_1_cc_pipe_clk_xo[] = { +- { .name = "usb3phy_1_cc_pipe_clk" }, ++ { .fw_name = "usb3phy_1_cc_pipe_clk", .name = "usb3phy_1_cc_pipe_clk" }, + { .fw_name = "xo", .name = "xo" }, + }; + diff --git a/target/linux/qualcommax/patches-6.1/0022-v6.5-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch b/target/linux/qualcommax/patches-6.1/0022-v6.5-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch new file mode 100644 index 0000000000..72b38270c4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0022-v6.5-soc-qcom-mdt_loader-Fix-unconditional-call-to-scm_pa.patch @@ -0,0 +1,77 @@ +From b8295c6eb276b60f7b75c53a9703ca8fee01eba2 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 26 May 2023 13:09:17 +0200 +Subject: [PATCH] soc: qcom: mdt_loader: Fix unconditional call to + scm_pas_mem_setup + +Commit ebeb20a9cd3f ("soc: qcom: mdt_loader: Always invoke PAS +mem_setup") dropped the relocate check and made pas_mem_setup run +unconditionally. The code was later moved with commit f4e526ff7e38 +("soc: qcom: mdt_loader: Extract PAS operations") to +qcom_mdt_pas_init() effectively losing track of what was actually +done. + +The assumption that PAS mem_setup can be done anytime was effectively +wrong, with no good reason and this caused regression on some SoC +that use remoteproc to bringup ath11k. One example is IPQ8074 SoC that +effectively broke resulting in remoteproc silently die and ath11k not +working. + +On this SoC FW relocate is not enabled and PAS mem_setup was correctly +skipped in previous kernel version resulting in correct bringup and +function of remoteproc and ath11k. + +To fix the regression, reintroduce the relocate check in +qcom_mdt_pas_init() and correctly skip PAS mem_setup where relocate is +not enabled. + +Fixes: ebeb20a9cd3f ("soc: qcom: mdt_loader: Always invoke PAS mem_setup") +Reported-by: Robert Marko +Tested-by: Robert Marko +Co-developed-by: Robert Marko +Signed-off-by: Robert Marko +Signed-off-by: Christian Marangi +Cc: stable@vger.kernel.org +--- + drivers/soc/qcom/mdt_loader.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +--- a/drivers/soc/qcom/mdt_loader.c ++++ b/drivers/soc/qcom/mdt_loader.c +@@ -210,6 +210,7 @@ int qcom_mdt_pas_init(struct device *dev + const struct elf32_hdr *ehdr; + phys_addr_t min_addr = PHYS_ADDR_MAX; + phys_addr_t max_addr = 0; ++ bool relocate = false; + size_t metadata_len; + void *metadata; + int ret; +@@ -224,6 +225,9 @@ int qcom_mdt_pas_init(struct device *dev + if (!mdt_phdr_valid(phdr)) + continue; + ++ if (phdr->p_flags & QCOM_MDT_RELOCATABLE) ++ relocate = true; ++ + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + +@@ -246,11 +250,13 @@ int qcom_mdt_pas_init(struct device *dev + goto out; + } + +- ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); +- if (ret) { +- /* Unable to set up relocation */ +- dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); +- goto out; ++ if (relocate) { ++ ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); ++ if (ret) { ++ /* Unable to set up relocation */ ++ dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); ++ goto out; ++ } + } + + out: diff --git a/target/linux/qualcommax/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch b/target/linux/qualcommax/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch new file mode 100644 index 0000000000..5713775948 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0100-clk-qcom-clk-rcg2-introduce-support-for-multiple-con.patch @@ -0,0 +1,203 @@ +From 032be4f49dda786fea9e1501212f6cd09a7ded96 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Nov 2022 14:49:43 +0100 +Subject: [PATCH] clk: qcom: clk-rcg2: introduce support for multiple conf for + same freq + +Some RCG frequency can be reached by multiple configuration. + +We currently declare multiple configuration for the same frequency but +that is not supported and always the first configuration will be taken. + +These multiple configuration are needed as based on the current parent +configuration, it may be needed to use a different configuration to +reach the same frequency. + +To handle this introduce 2 new macro, FM and C. + +- FM is used to declare an empty freq_tbl with just the frequency and an + array of confs to insert all the config for the provided frequency. + +- C is used to declare a fre_conf where src, pre_div, m and n are + provided. + +The driver is changed to handle this special freq_tbl and select the +correct config by calculating the final rate and deciding based on the +one that is less different than the requested one. + +Tested-by: Robert Marko +Signed-off-by: Christian Marangi +--- + drivers/clk/qcom/clk-rcg.h | 14 ++++++- + drivers/clk/qcom/clk-rcg2.c | 84 +++++++++++++++++++++++++++++++++---- + 2 files changed, 88 insertions(+), 10 deletions(-) + +--- a/drivers/clk/qcom/clk-rcg.h ++++ b/drivers/clk/qcom/clk-rcg.h +@@ -7,7 +7,17 @@ + #include + #include "clk-regmap.h" + +-#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } ++#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n), 0, NULL } ++ ++#define FM(_f, _confs) { .freq = (_f), .confs_num = ARRAY_SIZE(_confs), .confs = (_confs) } ++#define C(s, h, m, n) { (s), (2 * (h) - 1), (m), (n) } ++ ++struct freq_conf { ++ u8 src; ++ u8 pre_div; ++ u16 m; ++ u16 n; ++}; + + struct freq_tbl { + unsigned long freq; +@@ -15,6 +25,8 @@ struct freq_tbl { + u8 pre_div; + u16 m; + u16 n; ++ int confs_num; ++ const struct freq_conf *confs; + }; + + /** +--- a/drivers/clk/qcom/clk-rcg2.c ++++ b/drivers/clk/qcom/clk-rcg2.c +@@ -209,11 +209,60 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, + return __clk_rcg2_recalc_rate(hw, parent_rate, cfg); + } + ++static void ++clk_rcg2_select_conf(struct clk_hw *hw, struct freq_tbl *f_tbl, ++ const struct freq_tbl *f, unsigned long req_rate) ++{ ++ unsigned long best_rate = 0, parent_rate, rate; ++ const struct freq_conf *conf, *best_conf; ++ struct clk_rcg2 *rcg = to_clk_rcg2(hw); ++ struct clk_hw *p; ++ int index, i; ++ ++ /* Search in each provided config the one that is near the wanted rate */ ++ for (i = 0, conf = f->confs; i < f->confs_num; i++, conf++) { ++ index = qcom_find_src_index(hw, rcg->parent_map, conf->src); ++ if (index < 0) ++ continue; ++ ++ p = clk_hw_get_parent_by_index(hw, index); ++ if (!p) ++ continue; ++ ++ parent_rate = clk_hw_get_rate(p); ++ rate = calc_rate(parent_rate, conf->n, conf->m, conf->n, conf->pre_div); ++ ++ if (rate == req_rate) { ++ best_conf = conf; ++ break; ++ } ++ ++ if (abs(req_rate - rate) < abs(best_rate - rate)) { ++ best_rate = rate; ++ best_conf = conf; ++ } ++ } ++ ++ /* ++ * Very unlikely. ++ * Force the first conf if we can't find a correct config. ++ */ ++ if (unlikely(i == f->confs_num)) ++ best_conf = f->confs; ++ ++ /* Apply the config */ ++ f_tbl->src = best_conf->src; ++ f_tbl->pre_div = best_conf->pre_div; ++ f_tbl->m = best_conf->m; ++ f_tbl->n = best_conf->n; ++} ++ + static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, + struct clk_rate_request *req, + enum freq_policy policy) + { + unsigned long clk_flags, rate = req->rate; ++ struct freq_tbl f_tbl; + struct clk_hw *p; + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int index; +@@ -232,7 +281,15 @@ static int _freq_tbl_determine_rate(stru + if (!f) + return -EINVAL; + +- index = qcom_find_src_index(hw, rcg->parent_map, f->src); ++ f_tbl = *f; ++ /* ++ * A single freq may be reached by multiple configuration. ++ * Try to find the bast one if we have this kind of freq_table. ++ */ ++ if (f->confs) ++ clk_rcg2_select_conf(hw, &f_tbl, f, rate); ++ ++ index = qcom_find_src_index(hw, rcg->parent_map, f_tbl.src); + if (index < 0) + return index; + +@@ -242,18 +299,18 @@ static int _freq_tbl_determine_rate(stru + return -EINVAL; + + if (clk_flags & CLK_SET_RATE_PARENT) { +- rate = f->freq; +- if (f->pre_div) { ++ rate = f_tbl.freq; ++ if (f_tbl.pre_div) { + if (!rate) + rate = req->rate; + rate /= 2; +- rate *= f->pre_div + 1; ++ rate *= f_tbl.pre_div + 1; + } + +- if (f->n) { ++ if (f_tbl.n) { + u64 tmp = rate; +- tmp = tmp * f->n; +- do_div(tmp, f->m); ++ tmp = tmp * f_tbl.n; ++ do_div(tmp, f_tbl.m); + rate = tmp; + } + } else { +@@ -261,7 +318,7 @@ static int _freq_tbl_determine_rate(stru + } + req->best_parent_hw = p; + req->best_parent_rate = rate; +- req->rate = f->freq; ++ req->rate = f_tbl.freq; + + return 0; + } +@@ -357,6 +414,7 @@ static int __clk_rcg2_set_rate(struct cl + { + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const struct freq_tbl *f; ++ struct freq_tbl f_tbl; + + switch (policy) { + case FLOOR: +@@ -372,7 +430,15 @@ static int __clk_rcg2_set_rate(struct cl + if (!f) + return -EINVAL; + +- return clk_rcg2_configure(rcg, f); ++ f_tbl = *f; ++ /* ++ * A single freq may be reached by multiple configuration. ++ * Try to find the best one if we have this kind of freq_table. ++ */ ++ if (f->confs) ++ clk_rcg2_select_conf(hw, &f_tbl, f, rate); ++ ++ return clk_rcg2_configure(rcg, &f_tbl); + } + + static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/target/linux/qualcommax/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch b/target/linux/qualcommax/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch new file mode 100644 index 0000000000..32fb2d9d87 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0101-clk-qcom-gcc-ipq8074-rework-nss_port5-6-clock-to-mul.patch @@ -0,0 +1,129 @@ +From f778553f296792f4d1e8b3552603ad6116ea3eb3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 3 Nov 2022 14:49:44 +0100 +Subject: [PATCH] clk: qcom: gcc-ipq8074: rework nss_port5/6 clock to multiple + conf + +Rework nss_port5/6 to use the new multiple configuration implementation +and correctly fix the clocks for these port under some corner case. + +This is particularly relevant for device that have 2.5G or 10G port +connected to port5 or port 6 on ipq8074. As the parent are shared +across multiple port it may be required to select the correct +configuration to accomplish the desired clock. Without this patch such +port doesn't work in some specific ethernet speed as the clock will be +set to the wrong frequency as we just select the first configuration for +the related frequency instead of selecting the best one. + +Tested-by: Robert Marko # ipq8074 Qnap QHora-301W +Signed-off-by: Christian Marangi +--- + drivers/clk/qcom/gcc-ipq8074.c | 64 +++++++++++++++++++++++++--------- + 1 file changed, 48 insertions(+), 16 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -1682,13 +1682,21 @@ static struct clk_regmap_div nss_port4_t + }, + }; + ++static const struct freq_conf ftbl_nss_port5_rx_clk_src_25[] = { ++ C(P_UNIPHY1_RX, 12.5, 0, 0), ++ C(P_UNIPHY0_RX, 5, 0, 0), ++}; ++ ++static const struct freq_conf ftbl_nss_port5_rx_clk_src_125[] = { ++ C(P_UNIPHY1_RX, 2.5, 0, 0), ++ C(P_UNIPHY0_RX, 1, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), +- F(25000000, P_UNIPHY0_RX, 5, 0, 0), ++ FM(25000000, ftbl_nss_port5_rx_clk_src_25), + F(78125000, P_UNIPHY1_RX, 4, 0, 0), +- F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), +- F(125000000, P_UNIPHY0_RX, 1, 0, 0), ++ FM(125000000, ftbl_nss_port5_rx_clk_src_125), + F(156250000, P_UNIPHY1_RX, 2, 0, 0), + F(312500000, P_UNIPHY1_RX, 1, 0, 0), + { } +@@ -1744,13 +1752,21 @@ static struct clk_regmap_div nss_port5_r + }, + }; + ++static struct freq_conf ftbl_nss_port5_tx_clk_src_25[] = { ++ C(P_UNIPHY1_TX, 12.5, 0, 0), ++ C(P_UNIPHY0_TX, 5, 0, 0), ++}; ++ ++static struct freq_conf ftbl_nss_port5_tx_clk_src_125[] = { ++ C(P_UNIPHY1_TX, 2.5, 0, 0), ++ C(P_UNIPHY0_TX, 1, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), +- F(25000000, P_UNIPHY0_TX, 5, 0, 0), ++ FM(25000000, ftbl_nss_port5_tx_clk_src_25), + F(78125000, P_UNIPHY1_TX, 4, 0, 0), +- F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), +- F(125000000, P_UNIPHY0_TX, 1, 0, 0), ++ FM(125000000, ftbl_nss_port5_tx_clk_src_125), + F(156250000, P_UNIPHY1_TX, 2, 0, 0), + F(312500000, P_UNIPHY1_TX, 1, 0, 0), + { } +@@ -1806,13 +1822,21 @@ static struct clk_regmap_div nss_port5_t + }, + }; + ++static struct freq_conf ftbl_nss_port6_rx_clk_src_25[] = { ++ C(P_UNIPHY2_RX, 5, 0, 0), ++ C(P_UNIPHY2_RX, 12.5, 0, 0), ++}; ++ ++static struct freq_conf ftbl_nss_port6_rx_clk_src_125[] = { ++ C(P_UNIPHY2_RX, 1, 0, 0), ++ C(P_UNIPHY2_RX, 2.5, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY2_RX, 5, 0, 0), +- F(25000000, P_UNIPHY2_RX, 12.5, 0, 0), ++ FM(25000000, ftbl_nss_port6_rx_clk_src_25), + F(78125000, P_UNIPHY2_RX, 4, 0, 0), +- F(125000000, P_UNIPHY2_RX, 1, 0, 0), +- F(125000000, P_UNIPHY2_RX, 2.5, 0, 0), ++ FM(125000000, ftbl_nss_port6_rx_clk_src_125), + F(156250000, P_UNIPHY2_RX, 2, 0, 0), + F(312500000, P_UNIPHY2_RX, 1, 0, 0), + { } +@@ -1863,13 +1887,21 @@ static struct clk_regmap_div nss_port6_r + }, + }; + ++static struct freq_conf ftbl_nss_port6_tx_clk_src_25[] = { ++ C(P_UNIPHY2_TX, 5, 0, 0), ++ C(P_UNIPHY2_TX, 12.5, 0, 0), ++}; ++ ++static struct freq_conf ftbl_nss_port6_tx_clk_src_125[] = { ++ C(P_UNIPHY2_TX, 1, 0, 0), ++ C(P_UNIPHY2_TX, 2.5, 0, 0), ++}; ++ + static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), +- F(25000000, P_UNIPHY2_TX, 5, 0, 0), +- F(25000000, P_UNIPHY2_TX, 12.5, 0, 0), ++ FM(25000000, ftbl_nss_port6_tx_clk_src_25), + F(78125000, P_UNIPHY2_TX, 4, 0, 0), +- F(125000000, P_UNIPHY2_TX, 1, 0, 0), +- F(125000000, P_UNIPHY2_TX, 2.5, 0, 0), ++ FM(125000000, ftbl_nss_port6_tx_clk_src_125), + F(156250000, P_UNIPHY2_TX, 2, 0, 0), + F(312500000, P_UNIPHY2_TX, 1, 0, 0), + { } diff --git a/target/linux/qualcommax/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch b/target/linux/qualcommax/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch new file mode 100644 index 0000000000..af53c077ff --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch @@ -0,0 +1,70 @@ +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 +@@ -85,6 +85,26 @@ + #size-cells = <2>; + ranges; + ++ nss@40000000 { ++ no-map; ++ reg = <0x0 0x40000000 0x0 0x01000000>; ++ }; ++ ++ tzapp_region: tzapp@4a400000 { ++ no-map; ++ reg = <0x0 0x4a400000 0x0 0x00200000>; ++ }; ++ ++ uboot@4a600000 { ++ no-map; ++ reg = <0x0 0x4a600000 0x0 0x00400000>; ++ }; ++ ++ sbl@4aa00000 { ++ no-map; ++ reg = <0x0 0x4aa00000 0x0 0x00100000>; ++ }; ++ + smem@4ab00000 { + compatible = "qcom,smem"; + reg = <0x0 0x4ab00000 0x0 0x00100000>; +@@ -97,6 +117,21 @@ + no-map; + reg = <0x0 0x4ac00000 0x0 0x00400000>; + }; ++ ++ 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.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch b/target/linux/qualcommax/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch new file mode 100644 index 0000000000..69d4126f76 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0110-arm64-dts-qcom-ipq8074-pass-QMP-PCI-PHY-PIPE-clocks-.patch @@ -0,0 +1,30 @@ +From 8a576b5bc9f0555d1d970cacabcaa24a3b74fa57 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Nov 2022 22:15:01 +0100 +Subject: [PATCH] arm64: dts: qcom: ipq8074: pass QMP PCI PHY PIPE clocks to + GCC + +Pass QMP PCI PHY PIPE clocks to the GCC controller so it does not have to +find them by matching globaly by name. + +If not passed directly, driver maintains backwards compatibility by then +falling back to global lookup. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -396,8 +396,8 @@ + gcc: gcc@1800000 { + compatible = "qcom,gcc-ipq8074"; + reg = <0x01800000 0x80000>; +- clocks = <&xo>, <&sleep_clk>; +- clock-names = "xo", "sleep_clk"; ++ clocks = <&xo>, <&sleep_clk>, <&pcie_phy0>, <&pcie_phy1>; ++ clock-names = "xo", "sleep_clk", "pcie0_pipe", "pcie1_pipe"; + #clock-cells = <1>; + #power-domain-cells = <1>; + #reset-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch b/target/linux/qualcommax/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch new file mode 100644 index 0000000000..9fefd8852a --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0111-arm64-dts-qcom-ipq8074-use-msi-parent-for-PCIe.patch @@ -0,0 +1,43 @@ +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 +@@ -699,7 +699,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>; +@@ -811,8 +811,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 142 +@@ -873,8 +872,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 75 diff --git a/target/linux/qualcommax/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch b/target/linux/qualcommax/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch new file mode 100644 index 0000000000..0a984948b5 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0112-remoteproc-qcom-Add-PRNG-proxy-clock.patch @@ -0,0 +1,155 @@ +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; + +@@ -151,6 +139,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; +@@ -240,6 +243,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 */ +@@ -733,6 +742,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) + goto free_rproc; + +- if (wcss->version == WCSS_QCS404) { +- ret = q6v5_wcss_init_clock(wcss); ++ if (desc->init_clock) { ++ ret = desc->init_clock(wcss); + if (ret) + goto free_rproc; ++ } + +- ret = q6v5_wcss_init_regulator(wcss); ++ if (desc->init_regulator) { ++ ret = desc->init_regulator(wcss); + if (ret) + goto free_rproc; + } +@@ -1087,6 +1113,7 @@ static int q6v5_wcss_remove(struct platf + } + + 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, +@@ -1096,6 +1123,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.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch b/target/linux/qualcommax/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch new file mode 100644 index 0000000000..0328efc041 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0113-remoteproc-qcom-Add-secure-PIL-support.patch @@ -0,0 +1,143 @@ +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 3 ++ ++#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_ssr ssr_subdev; +@@ -152,6 +157,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) +@@ -251,6 +257,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) { +@@ -285,6 +300,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"); +@@ -718,6 +734,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); +@@ -742,6 +767,7 @@ static int q6v5_wcss_stop(struct rproc * + return ret; + } + ++pas_done: + clk_disable_unprepare(wcss->prng_clk); + qcom_q6v5_unprepare(&wcss->q6v5); + +@@ -765,9 +791,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 = 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) +@@ -1120,6 +1156,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.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch b/target/linux/qualcommax/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch new file mode 100644 index 0000000000..e5c9506c89 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0114-remoteproc-qcom-Add-support-for-split-q6-m3-wlan-fir.patch @@ -0,0 +1,103 @@ +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_ssr ssr_subdev; +@@ -147,7 +148,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; +@@ -789,8 +791,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 = 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) +@@ -1150,7 +1174,8 @@ static int q6v5_wcss_remove(struct platf + + 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, +@@ -1163,7 +1188,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.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch b/target/linux/qualcommax/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch new file mode 100644 index 0000000000..be63d46e8e --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0115-remoteproc-qcom-Add-ssr-subdevice-identifier.patch @@ -0,0 +1,24 @@ +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 +@@ -1179,6 +1179,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.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch b/target/linux/qualcommax/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch new file mode 100644 index 0000000000..f0b717210f --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0116-remoteproc-qcom-Update-regmap-offsets-for-halt-regis.patch @@ -0,0 +1,79 @@ +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 +@@ -86,7 +86,7 @@ + #define TCSR_WCSS_CLK_MASK 0x1F + #define TCSR_WCSS_CLK_ENABLE 0x14 + +-#define MAX_HALT_REG 3 ++#define MAX_HALT_REG 4 + + #define WCNSS_PAS_ID 6 + +@@ -154,6 +154,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; +@@ -875,10 +876,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; +@@ -929,9 +933,9 @@ static int q6v5_wcss_init_mmio(struct q6 + return -EINVAL; + } + +- wcss->halt_q6 = halt_reg[0]; +- wcss->halt_wcss = halt_reg[1]; +- wcss->halt_nc = halt_reg[2]; ++ wcss->halt_q6 = halt_reg[1]; ++ wcss->halt_wcss = halt_reg[2]; ++ wcss->halt_nc = halt_reg[3]; + + return 0; + } +@@ -1179,6 +1183,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, +@@ -1193,6 +1198,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.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch b/target/linux/qualcommax/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch new file mode 100644 index 0000000000..fe0e0f9e0b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0117-dt-bindings-clock-qcom-Add-reset-for-WCSSAON.patch @@ -0,0 +1,26 @@ +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.1/0118-clk-qcom-Add-WCSSAON-reset.patch b/target/linux/qualcommax/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch new file mode 100644 index 0000000000..791531775e --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0118-clk-qcom-Add-WCSSAON-reset.patch @@ -0,0 +1,25 @@ +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 +@@ -4717,6 +4717,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.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch b/target/linux/qualcommax/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch new file mode 100644 index 0000000000..8674522a11 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0119-remoteproc-wcss-disable-auto-boot-for-IPQ8074.patch @@ -0,0 +1,48 @@ +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 +@@ -161,6 +161,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) +@@ -1150,6 +1151,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) + goto free_rproc; +@@ -1188,6 +1190,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 = { +@@ -1204,6 +1207,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.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch b/target/linux/qualcommax/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch new file mode 100644 index 0000000000..e37ba37e96 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0120-arm64-dts-qcom-Enable-Q6v5-WCSS-for-ipq8074-SoC.patch @@ -0,0 +1,120 @@ +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 +@@ -140,6 +140,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 { + #address-cells = <0x1>; + #size-cells = <0x1>; +@@ -409,6 +435,11 @@ + #hwlock-cells = <1>; + }; + ++ tcsr_q6: syscon@1945000 { ++ compatible = "syscon"; ++ reg = <0x01945000 0xe000>; ++ }; ++ + spmi_bus: spmi@200f000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x0200f000 0x001000>, +@@ -913,6 +944,56 @@ + "axi_s_sticky"; + status = "disabled"; + }; ++ ++ 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.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch b/target/linux/qualcommax/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch new file mode 100644 index 0000000000..bd5410c934 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0121-arm64-dts-ipq8074-Add-WLAN-node.patch @@ -0,0 +1,135 @@ +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 +@@ -994,6 +994,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.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch b/target/linux/qualcommax/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch new file mode 100644 index 0000000000..a3c5f344ab --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0122-arm64-dts-ipq8074-add-CPU-clock.patch @@ -0,0 +1,59 @@ +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.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch b/target/linux/qualcommax/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch new file mode 100644 index 0000000000..3520b38134 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0123-arm64-dts-ipq8074-add-cooling-cells-to-CPU-nodes.patch @@ -0,0 +1,48 @@ +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.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch b/target/linux/qualcommax/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch new file mode 100644 index 0000000000..09abbfa2e5 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0128-cpufreq-qcom-nvmem-add-support-for-IPQ8074.patch @@ -0,0 +1,108 @@ +From 11592aa862e67f4477dee7e94d5c8244d893de1b Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Sat, 31 Dec 2022 13:03:41 +0100 +Subject: [PATCH] cpufreq: qcom-nvmem: add support for IPQ8074 + +IPQ8074 comes in 2 families: +* IPQ8070A/IPQ8071A (Acorn) up to 1.4GHz +* IPQ8072A/IPQ8074A/IPQ8076A/IPQ8078A (Hawkeye) up to 2.2GHz + +So, in order to be able to share one OPP table lets add support for IPQ8074 +family based of SMEM SoC ID-s as speedbin fuse is always 0 on IPQ8074. + +IPQ8074 compatible is blacklisted from DT platdev as the cpufreq device +will get created by NVMEM CPUFreq driver. + +Signed-off-by: Robert Marko +--- +Changes in v2: +* Print an error if SMEM ID is not part of the IPQ8074 family +and restrict the speed to Acorn variant (1.4GHz) +--- + drivers/cpufreq/cpufreq-dt-platdev.c | 1 + + drivers/cpufreq/qcom-cpufreq-nvmem.c | 43 ++++++++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +--- a/drivers/cpufreq/cpufreq-dt-platdev.c ++++ b/drivers/cpufreq/cpufreq-dt-platdev.c +@@ -164,6 +164,7 @@ static const struct of_device_id blockli + { .compatible = "ti,omap3", }, + + { .compatible = "qcom,ipq8064", }, ++ { .compatible = "qcom,ipq8074", }, + { .compatible = "qcom,apq8064", }, + { .compatible = "qcom,msm8974", }, + { .compatible = "qcom,msm8960", }, +--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c ++++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c +@@ -31,6 +31,9 @@ + + #include + ++#define IPQ8074_HAWKEYE_VERSION BIT(0) ++#define IPQ8074_ACORN_VERSION BIT(1) ++ + struct qcom_cpufreq_drv; + + struct qcom_cpufreq_match_data { +@@ -204,6 +207,41 @@ len_error: + return ret; + } + ++static int qcom_cpufreq_ipq8074_name_version(struct device *cpu_dev, ++ struct nvmem_cell *speedbin_nvmem, ++ char **pvs_name, ++ struct qcom_cpufreq_drv *drv) ++{ ++ u32 msm_id; ++ int ret; ++ *pvs_name = NULL; ++ ++ ret = qcom_smem_get_soc_id(&msm_id); ++ if (ret) ++ return ret; ++ ++ switch (msm_id) { ++ case QCOM_ID_IPQ8070A: ++ case QCOM_ID_IPQ8071A: ++ drv->versions = IPQ8074_ACORN_VERSION; ++ break; ++ case QCOM_ID_IPQ8072A: ++ case QCOM_ID_IPQ8074A: ++ case QCOM_ID_IPQ8076A: ++ case QCOM_ID_IPQ8078A: ++ drv->versions = IPQ8074_HAWKEYE_VERSION; ++ break; ++ default: ++ dev_err(cpu_dev, ++ "SoC ID %u is not part of IPQ8074 family, limiting to 1.4GHz!\n", ++ msm_id); ++ drv->versions = IPQ8074_ACORN_VERSION; ++ break; ++ } ++ ++ return 0; ++} ++ + static const struct qcom_cpufreq_match_data match_data_kryo = { + .get_version = qcom_cpufreq_kryo_name_version, + }; +@@ -218,6 +256,10 @@ static const struct qcom_cpufreq_match_d + .genpd_names = qcs404_genpd_names, + }; + ++static const struct qcom_cpufreq_match_data match_data_ipq8074 = { ++ .get_version = qcom_cpufreq_ipq8074_name_version, ++}; ++ + static int qcom_cpufreq_probe(struct platform_device *pdev) + { + struct qcom_cpufreq_drv *drv; +@@ -363,6 +405,7 @@ static const struct of_device_id qcom_cp + { .compatible = "qcom,msm8996", .data = &match_data_kryo }, + { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, + { .compatible = "qcom,ipq8064", .data = &match_data_krait }, ++ { .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 }, + { .compatible = "qcom,apq8064", .data = &match_data_krait }, + { .compatible = "qcom,msm8974", .data = &match_data_krait }, + { .compatible = "qcom,msm8960", .data = &match_data_krait }, diff --git a/target/linux/qualcommax/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch b/target/linux/qualcommax/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch new file mode 100644 index 0000000000..3a6f4e9c87 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0129-arm64-dts-qcom-ipq8074-add-QFPROM-fuses.patch @@ -0,0 +1,128 @@ +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 +@@ -340,6 +340,113 @@ + status = "disabled"; + }; + ++ qfprom: efuse@a4000 { ++ compatible = "qcom,ipq8074-qfprom", "qcom,qfprom"; ++ reg = <0x000a4000 0x1000>; ++ #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 { + compatible = "qcom,prng-ee"; + reg = <0x000e3000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch b/target/linux/qualcommax/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch new file mode 100644 index 0000000000..9c1e7b9d29 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0130-arm64-dts-qcom-ipq8074-add-CPU-OPP-table.patch @@ -0,0 +1,102 @@ +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 { +@@ -83,6 +87,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.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch b/target/linux/qualcommax/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch new file mode 100644 index 0000000000..8c2e59eeb4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0132-mmc-core-disable-TRIM-on-Micron-MTFC4GACAJCN-1M.patch @@ -0,0 +1,36 @@ +From f5aaf6669bd4f1f0218dd7fd5dc90941267ad860 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 30 May 2023 23:26:30 +0200 +Subject: [PATCH] mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M + +It seems that Micron MTFC4GACAJCN-1M despite advertising TRIM support does +not work when the core is trying to use REQ_OP_WRITE_ZEROES. + +We are seeing the following errors in OpenWrt under 6.1 on Qnap Qhora 301W +that we did not previously have and tracked it down to REQ_OP_WRITE_ZEROES: +[ 18.085950] I/O error, dev loop0, sector 596 op 0x9:(WRITE_ZEROES) flags 0x800 phys_seg 0 prio class 2 + +Disabling TRIM makes the error go away, so lets add a quirk for this eMMC +to disable TRIM. + +Signed-off-by: Robert Marko +--- + drivers/mmc/core/quirks.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -101,6 +101,13 @@ static const struct mmc_fixup __maybe_un + MMC_QUIRK_TRIM_BROKEN), + + /* ++ * Micron MTFC4GACAJCN-1M advertises TRIM but it does not seems to ++ * support being used to offload WRITE_ZEROES. ++ */ ++ MMC_FIXUP("Q2J54A", CID_MANFID_MICRON, 0x014e, add_quirk_mmc, ++ MMC_QUIRK_TRIM_BROKEN), ++ ++ /* + * Some SD cards reports discard support while they don't + */ + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, diff --git a/target/linux/qualcommax/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch b/target/linux/qualcommax/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch new file mode 100644 index 0000000000..ac7af52a74 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0133-mmc-core-disable-TRIM-on-Kingston-EMMC04G-M627.patch @@ -0,0 +1,38 @@ +From 26c97b6fb7d291f55e0e4a410d266d1355118ed9 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 31 May 2023 20:21:26 +0200 +Subject: [PATCH] mmc: core: disable TRIM on Kingston EMMC04G-M627 + +It seems that Kingston EMMC04G-M627 despite advertising TRIM support does +not work when the core is trying to use REQ_OP_WRITE_ZEROES. + +We are seeing I/O errors in OpenWrt under 6.1 on Zyxel NBG7815 that we did +not previously have and tracked it down to REQ_OP_WRITE_ZEROES. + +Trying to use fstrim seems to also throw errors like: +[93010.835112] I/O error, dev loop0, sector 16902 op 0x3:(DISCARD) flags 0x800 phys_seg 1 prio class 2 + +Disabling TRIM makes the error go away, so lets add a quirk for this eMMC +to disable TRIM. + +Signed-off-by: Robert Marko +--- + drivers/mmc/core/quirks.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -108,6 +108,13 @@ static const struct mmc_fixup __maybe_un + MMC_QUIRK_TRIM_BROKEN), + + /* ++ * Kingston EMMC04G-M627 advertises TRIM but it does not seems to ++ * support being used to offload WRITE_ZEROES. ++ */ ++ MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, ++ MMC_QUIRK_TRIM_BROKEN), ++ ++ /* + * Some SD cards reports discard support while they don't + */ + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd, diff --git a/target/linux/qualcommax/patches-6.1/0134-arm64-dts-qcom-ipq8074-add-critical-thermal-trips.patch b/target/linux/qualcommax/patches-6.1/0134-arm64-dts-qcom-ipq8074-add-critical-thermal-trips.patch new file mode 100644 index 0000000000..6231d8afb7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0134-arm64-dts-qcom-ipq8074-add-critical-thermal-trips.patch @@ -0,0 +1,197 @@ +From 145bbf2b88990ef3ff00ee541bb7662008683c16 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 7 Jun 2023 20:26:26 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq8074: add critical thermal trips + +According to bindings, thermal zones must have associated trips as well. +Since we currently dont have CPUFreq support and thus no passive cooling +lets start by defining critical trips to protect the devices against +severe overheating. + +Signed-off-by: Robert Marko +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 96 +++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -1293,6 +1293,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 4>; ++ ++ trips { ++ nss-top-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + nss0-thermal { +@@ -1300,6 +1308,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 5>; ++ ++ trips { ++ nss-0-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + nss1-thermal { +@@ -1307,6 +1323,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 6>; ++ ++ trips { ++ nss-1-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + wcss-phya0-thermal { +@@ -1314,6 +1338,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 7>; ++ ++ trips { ++ wcss-phya0-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + wcss-phya1-thermal { +@@ -1321,6 +1353,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 8>; ++ ++ trips { ++ wcss-phya1-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + cpu0_thermal: cpu0-thermal { +@@ -1328,6 +1368,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 9>; ++ ++ trips { ++ cpu0-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + cpu1_thermal: cpu1-thermal { +@@ -1335,6 +1383,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 10>; ++ ++ trips { ++ cpu1-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + cpu2_thermal: cpu2-thermal { +@@ -1342,6 +1398,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 11>; ++ ++ trips { ++ cpu2-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + cpu3_thermal: cpu3-thermal { +@@ -1349,6 +1413,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 12>; ++ ++ trips { ++ cpu3-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + cluster_thermal: cluster-thermal { +@@ -1356,6 +1428,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 13>; ++ ++ trips { ++ cluster-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + wcss-phyb0-thermal { +@@ -1363,6 +1443,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 14>; ++ ++ trips { ++ wcss-phyb0-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + + wcss-phyb1-thermal { +@@ -1370,6 +1458,14 @@ + polling-delay = <1000>; + + thermal-sensors = <&tsens 15>; ++ ++ trips { ++ wcss-phyb1-crit { ++ temperature = <110000>; ++ hysteresis = <1000>; ++ type = "critical"; ++ }; ++ }; + }; + }; + }; diff --git a/target/linux/qualcommax/patches-6.1/0900-power-Add-Qualcomm-APM.patch b/target/linux/qualcommax/patches-6.1/0900-power-Add-Qualcomm-APM.patch new file mode 100644 index 0000000000..2e5c72b7d1 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0900-power-Add-Qualcomm-APM.patch @@ -0,0 +1,1047 @@ +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 +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + source "drivers/power/reset/Kconfig" + source "drivers/power/supply/Kconfig" ++source "drivers/power/qcom/Kconfig" +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_POWER_RESET) += reset/ + 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,944 @@ ++/* ++ * 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_find_property(dev->of_node, "qcom,apm-post-halt-delay", NULL)) { ++ 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_find_property(dev->of_node, "qcom,apm-halt-clk-delay", NULL)) { ++ 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_find_property(dev->of_node, "qcom,apm-resume-clk-delay", NULL)) { ++ 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_find_property(dev->of_node, "qcom,apm-sel-switch-delay", NULL)) { ++ 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 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; ++ const struct of_device_id *match; ++ 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; ++ } ++ ++ match = of_match_device(msm_apm_match_table, dev); ++ if (!match) ++ 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)match->data; ++ 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 int 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); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver msm_apm_driver = { ++ .driver = { ++ .name = MSM_APM_DRIVER_NAME, ++ .of_match_table = msm_apm_match_table, ++ .owner = THIS_MODULE, ++ }, ++ .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.1/0901-regulator-add-Qualcomm-CPR-regulators.patch b/target/linux/qualcommax/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch new file mode 100644 index 0000000000..9b9f7159f3 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0901-regulator-add-Qualcomm-CPR-regulators.patch @@ -0,0 +1,12144 @@ +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 +@@ -1524,4 +1524,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 +@@ -110,6 +110,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,695 @@ ++/* ++ * 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 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 of_device_id *match; ++ 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; ++ ++ match = of_match_device(cpr3_regulator_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ ++ cpr_data = (struct cpr3_reg_data *)match->data; ++ 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 int cpr3_npu_regulator_remove(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return 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, ++ .owner = THIS_MODULE, ++ }, ++ .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; ++ struct cpr4_reg_data *cpr4_regulator_data; ++ 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,2750 @@ ++/* ++ * 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 ++ ++#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 ++ ++/** ++ * 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_find_property(vreg->of_node, "qcom,system-voltage", NULL)) { ++ 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_find_property(vreg->of_node, ++ "qcom,allow-aging-voltage-adjustment", NULL)) { ++ 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_find_property(vreg->of_node, ++ "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) { ++ 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_find_property(vreg->of_node, "qcom,cpr-aging-derate", ++ NULL)) { ++ 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_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) { ++ 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_find_property(ctrl->dev->of_node, "clock-names", NULL)) { ++ 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_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { ++ 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_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) { ++ 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_find_property(vreg->of_node, prop, NULL)) ++ 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) ++ strlcpy(prop_name, prop_name_v2, sizeof(prop_name_v2)); ++ else ++ strlcpy(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 { ++ strlcpy(prop_str, "qcom,cpr-cold-temp-voltage-adjustment", ++ sizeof(prop_str)); ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* 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) ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", ++ sizeof(prop_str)); ++ else ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", ++ sizeof(prop_str)); ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* 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 { ++ strlcpy(req_prop_str, "qcom,cpr-cold-temp-voltage-adjustment", ++ sizeof(req_prop_str)); ++ } ++ ++ if (soc_version_major > 1) ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold-v2", ++ sizeof(prop_str)); ++ else ++ strlcpy(prop_str, "qcom,cpr-cold-temp-threshold", ++ sizeof(prop_str)); ++ ++ if (!of_find_property(vreg->of_node, req_prop_str, NULL)) { ++ /* No adjustment required. */ ++ cpr3_info(vreg, "Cold temperature adjustment not required.\n"); ++ return 0; ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* 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 { ++ strlcpy(prop_str, "qcom,cpr-open-loop-voltage-fuse-adjustment", ++ sizeof(prop_str)); ++ } ++ ++ if (!of_find_property(vreg->of_node, prop_str, NULL)) { ++ /* 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_find_property(vreg->of_node, ++ "qcom,cpr-open-loop-voltage-adjustment", NULL)) { ++ /* 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_find_property(vreg->of_node, ++ "qcom,cpr-open-loop-voltage-min-diff", NULL)) { ++ 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_find_property(vreg->of_node, volt_adj, NULL) ++ && !of_find_property(vreg->of_node, volt_fuse_adj, NULL) ++ && !vreg->aging_allowed) { ++ /* No adjustment required. */ ++ return 0; ++ } else if (!of_find_property(vreg->of_node, ro_scaling, NULL)) { ++ 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_find_property(vreg->of_node, volt_fuse_adj, NULL)) { ++ 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_find_property(vreg->of_node, volt_adj, NULL)) { ++ 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_find_property(node, "qcom,apm-ctrl", NULL)) { ++ /* 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_find_property(node, use_corner_band ? ++ "qcom,corner-band-allow-temp-adjustment" ++ : "qcom,corner-allow-temp-adjustment", NULL)) { ++ if (!ctrl->allow_temp_adj) { ++ cpr3_err(ctrl, "Temperature adjustment configurations missing\n"); ++ return -EINVAL; ++ } ++ ++ vreg->allow_temp_adj = true; ++ } ++ ++ if (of_find_property(node, use_corner_band ? ++ "qcom,corner-band-allow-core-count-adjustment" ++ : "qcom,corner-allow-core-count-adjustment", ++ NULL)) { ++ 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_find_property(vreg->of_node, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) { ++ /* 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_find_property(vreg->of_node, ++ "qcom,cpr-closed-loop-voltage-adjustment", NULL); ++ fused_adjustment = of_find_property(vreg->of_node, ++ "qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL); ++ ++ if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) { ++ /* No adjustment required. */ ++ return 0; ++ } else if (!of_find_property(vreg->of_node, ++ "qcom,cpr-ro-scaling-factor", NULL)) { ++ 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,1819 @@ ++/* ++ * 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_find_property(node, "qcom,cpr-ro-sel", NULL)) { ++ 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_find_property(node, ++ "qcom,cpr-misc-fuse-voltage-adjustment", ++ NULL)) { ++ 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_find_property(vreg->of_node, ++ "qcom,cpr-misc-fuse-voltage-adjustment", NULL)) { ++ 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_find_property(vreg->of_node, "qcom,allow-boost", NULL)) { ++ 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_find_property(vreg->of_node, ++ "qcom,cpr-boost-voltage-fuse-adjustment", NULL)) { ++ 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_find_property(vreg->of_node, ++ "qcom,cpr-boost-temp-adjustment", NULL)) { ++ 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_find_property(ctrl->dev->of_node, "vdd-limit-supply", NULL)) { ++ 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 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 of_device_id *match; ++ 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; ++ ++ match = of_match_device(cpr4_regulator_match_table, &pdev->dev); ++ if (!match) ++ return -ENODEV; ++ ++ cpr_data = (struct cpr4_reg_data *)match->data; ++ 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 int cpr4_apss_regulator_remove(struct platform_device *pdev) ++{ ++ struct cpr3_controller *ctrl = platform_get_drvdata(pdev); ++ ++ return 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, ++ .owner = THIS_MODULE, ++ }, ++ .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); +--- /dev/null ++++ b/include/soc/qcom/socinfo.h +@@ -0,0 +1,463 @@ ++/* Copyright (c) 2009-2014, 2016, 2020, 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 _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++ ++#include ++ ++#define CPU_IPQ8074 323 ++#define CPU_IPQ8072 342 ++#define CPU_IPQ8076 343 ++#define CPU_IPQ8078 344 ++#define CPU_IPQ8070 375 ++#define CPU_IPQ8071 376 ++ ++#define CPU_IPQ8072A 389 ++#define CPU_IPQ8074A 390 ++#define CPU_IPQ8076A 391 ++#define CPU_IPQ8078A 392 ++#define CPU_IPQ8070A 395 ++#define CPU_IPQ8071A 396 ++ ++#define CPU_IPQ8172 397 ++#define CPU_IPQ8173 398 ++#define CPU_IPQ8174 399 ++ ++#define CPU_IPQ6018 402 ++#define CPU_IPQ6028 403 ++#define CPU_IPQ6000 421 ++#define CPU_IPQ6010 422 ++#define CPU_IPQ6005 453 ++ ++#define CPU_IPQ5010 446 ++#define CPU_IPQ5018 447 ++#define CPU_IPQ5028 448 ++#define CPU_IPQ5000 503 ++#define CPU_IPQ0509 504 ++#define CPU_IPQ0518 505 ++ ++#define CPU_IPQ9514 510 ++#define CPU_IPQ9554 512 ++#define CPU_IPQ9570 513 ++#define CPU_IPQ9574 514 ++#define CPU_IPQ9550 511 ++#define CPU_IPQ9510 521 ++ ++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); ++} ++ ++static inline int read_ipq_cpu_type(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "cpu_type", NULL); ++ /* ++ * Return Default CPU type if "cpu_type" property is not found in DTSI ++ */ ++ if (!prop) ++ return CPU_IPQ8074; ++ ++ return le32_to_cpu(*prop); ++} ++ ++static inline int cpu_is_ipq8070(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8070a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8172(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8172; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8173(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8173; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8174(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8174; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6005(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6005; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq0509(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ0509; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq0518(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ0518; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9514(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9514; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9554(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9554; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9570(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9570; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9574(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9574; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9550(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9550; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq9510(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ9510; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq807x(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq8072() || cpu_is_ipq8074() || ++ cpu_is_ipq8076() || cpu_is_ipq8078() || ++ cpu_is_ipq8070() || cpu_is_ipq8071() || ++ cpu_is_ipq8072a() || cpu_is_ipq8074a() || ++ cpu_is_ipq8076a() || cpu_is_ipq8078a() || ++ cpu_is_ipq8070a() || cpu_is_ipq8071a() || ++ cpu_is_ipq8172() || cpu_is_ipq8173() || ++ cpu_is_ipq8174(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq60xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq6018() || cpu_is_ipq6028() || ++ cpu_is_ipq6000() || cpu_is_ipq6010() || ++ cpu_is_ipq6005(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq50xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq5010() || cpu_is_ipq5018() || ++ cpu_is_ipq5028() || cpu_is_ipq5000() || ++ cpu_is_ipq0509() || cpu_is_ipq0518(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq95xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq9514() || cpu_is_ipq9554() || ++ cpu_is_ipq9570() || cpu_is_ipq9574() || ++ cpu_is_ipq9550() || cpu_is_ipq9510(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_nss_crypto_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq50xx() || cpu_is_ipq9570() || ++ cpu_is_ipq9550() || cpu_is_ipq9574() || ++ cpu_is_ipq9554(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_internal_wifi_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq50xx() || cpu_is_ipq9514() || ++ cpu_is_ipq9554() || cpu_is_ipq9574(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_uniphy1_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq60xx() || ++ cpu_is_ipq9554() || cpu_is_ipq9570() || ++ cpu_is_ipq9574() || cpu_is_ipq9550(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_uniphy2_enabled(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq807x() || cpu_is_ipq9570() || ++ cpu_is_ipq9574(); ++#else ++ return 0; ++#endif ++} ++ ++#endif /* _ARCH_ARM_MACH_MSM_SOCINFO_H_ */ diff --git a/target/linux/qualcommax/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch b/target/linux/qualcommax/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch new file mode 100644 index 0000000000..9b8b4df12b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0902-arm64-dts-ipq8074-add-label-to-clocks.patch @@ -0,0 +1,24 @@ +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>;