#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2015-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: LicenseRef-NvidiaProprietary
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#  * Neither the name of NVIDIA CORPORATION nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

JETSON_CLOCKS="$(basename ${0})"
FAN_SPEED_OVERRIDE=0
CONF_FILE="${HOME}/.jetsonclocks_conf.txt"
FAN_STATUS_FILE="${HOME}/.jetsonclocks_fan.txt"
GPU_RM_STATUS_FILE="${HOME}/.jetsonclocks_gpu_rm.txt"
gpu_type="unknown"
gpu_rm_probed=0
has_igpu_devfreq=0
RED='\e[0;31m'
GREEN='\e[0;32m'
BLUE='\e[0;34m'
BRED='\e[1;31m'
BGREEN='\e[1;32m'
BBLUE='\e[1;34m'
NC='\e[0m' # No Color

usage()
{
	if [ "$1" != "" ]; then
		echo -e ${RED}"$1"${NC}
	fi

		cat >& 2 <<EOF
Maximize jetson performance by setting static max frequency to CPU, GPU and EMC clocks.
Usage:
${JETSON_CLOCKS} [options]
  options,
  --help, -h         show this help message
  --show             display current settings
  --fan              set PWM fan speed to maximal
  --store [file]     store current settings to a file (default: \${HOME}/l4t_dfs.conf)
  --restore [file]   restore saved settings from a file (default: \${HOME}/l4t_dfs.conf)
  run ${JETSON_CLOCKS} without any option to set static max frequency to CPU, GPU and EMC clocks.
EOF

	exit 0
}

detect_gpu_type()
{
	iGPU_DEV_NODES=(/dev/nvhost-gpu /dev/nvhost-power-gpu)
	dGPU_DEV_NODES=(/dev/nvidiactl /dev/nvgpu-pci/*)

	if [ "${SOCFAMILY}" == "tegra264" ]; then
		gpu_type="iGPU"
		return
	fi

	for dev_node in ${iGPU_DEV_NODES[@]}; do
		if [ -e $dev_node ]; then
			gpu_type="iGPU"
			return
		fi
	done

	for dev_node in ${dGPU_DEV_NODES[@]}; do
		if [ -e $dev_node ]; then
			gpu_type="dGPU"
			return
		fi
	done
}

detect_igpu_devfreq()
{
	if [ "${SOCFAMILY}" == "tegra264" ]; then
		igpu_name="gpu"
	else
		igpu_name="$(tr -d '\0' < /sys/devices/platform/gpu.0/of_node/compatible)"
		if [ "${igpu_name}" != "nvidia,gv11b" ] && [ "${igpu_name}" != "nvidia,gp10b" ] && \
		   [ "${igpu_name}" != "nvidia,ga10b" ] && [ "${igpu_name}" != "nvidia,ga10f" ]; then
			echo "Error! Unknown GPU!"
			exit 1
		fi
	fi

	for devfreq in /sys/class/devfreq/*; do
		if [ "${SOCFAMILY}" != "tegra264" ]; then
			devfreq_igpu_name=$(tr -d '\0' <${devfreq}/device/of_node/compatible)
			if [[ "${igpu_name}" =~ "${devfreq_igpu_name}" ]]; then
				has_igpu_devfreq=1
			fi
		# T264
		elif [[ "${devfreq}" =~ "${igpu_name}" ]]; then
			has_igpu_devfreq=1
		fi

		if [ ${has_igpu_devfreq} -eq 0 ]; then
			continue
		fi

		GPU_MIN_FREQ="${devfreq}/min_freq"
		GPU_MAX_FREQ="${devfreq}/max_freq"
		GPU_CUR_FREQ="${devfreq}/cur_freq"
		GPU_SET_FREQ="${devfreq}/min_freq"
		break
	done
}

check_nvidia_smi()
{
	nvidia-smi &> /dev/null
	ret=$?
	if [ ${ret} -ne 0 ]; then
		case ${ret} in
			127)
				echo "Error: nvidia-smi not found."
				exit 1
				;;
			*)
				echo "Error: fail to do nvidia-smi operation." \
					"The exit code of nvidia-smi is ${ret}"
				exit 1
				;;
		esac
	fi
}

host1x_get_node()
{
	HOST1X_KSTABLE_NODE="/sys/devices/platform/bus@0/13e00000.host1x"
	case "${SOCFAMILY}" in
		tegra234 | tegra264)
			HOST1X_NODE="/sys/devices/platform/13e40000.host1x"
			;;
		*)
			;;

	esac
}

dla_get_state()
{
	DLA0_STATE=0
	DLA1_STATE=0
	DLA0_NODE="15880000.nvdla0"
	DLA1_NODE="158c0000.nvdla1"

	host1x_get_node
	if [ -d "${HOST1X_NODE}/${DLA0_NODE}" ] || \
		[ -d "${HOST1X_KSTABLE_NODE}/${DLA0_NODE}" ]; then
		DLA0_STATE=1
	fi

	if [ -d "${HOST1X_NODE}/${DLA1_NODE}" ] || \
		[ -d "${HOST1X_KSTABLE_NODE}/${DLA1_NODE}" ]; then
		DLA1_STATE=1
	fi
}

pva_get_state()
{
	PVA0_STATE=0
	PVA1_STATE=0
	PVA0_NODE="16000000.pva0"
	PVA1_NODE="16800000.pva1"

	host1x_get_node
	if [ -d "${HOST1X_NODE}/${PVA0_NODE}" ] || \
		[ -d "${HOST1X_KSTABLE_NODE}/${PVA0_NODE}" ]; then
		PVA0_STATE=1
	fi

	if [ -d "${HOST1X_NODE}/${PVA1_NODE}" ] || \
		[ -d "${HOST1X_KSTABLE_NODE}/${PVA1_NODE}" ]; then
		PVA1_STATE=1
	fi
}

dgpu_restore()
{
	if [[ "${gpu_type}" == "dGPU" ]]; then
		nvidia-smi -rac -i 0 > /dev/null
		ret=$?
		if [ ${ret} -ne 0 ]; then
			echo "Error: Failed to restore dGPU application clocks frequency!"
		fi
	fi
}

fan_restore()
{
	if [ -e "${FAN_STATUS_FILE}" ]; then
		NVFANCONTROL_STATUS="$(cat ${FAN_STATUS_FILE})"
			if [ "${NVFANCONTROL_STATUS}" == "active" ]; then
				systemctl start nvfancontrol
			else
				systemctl stop nvfancontrol
			fi
	fi
}

restore()
{
	for conf in $(cat "${CONF_FILE}"); do
		file="$(echo $conf | cut -f1 -d :)"
		data="$(echo $conf | cut -f2 -d :)"
		case "${file}" in
			/sys/devices/system/cpu/cpu*/online |\
			/sys/kernel/debug/clk/override*/state)
			if [ $(cat $file) -ne $data ]; then
					echo "${data}" > "${file}"
				fi
				;;
			/sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq)
				echo "${data}" > "${file}" 2>/dev/null
				;;
			*)
				echo "${data}" > "${file}"
				ret=$?
				if [ ${ret} -ne 0 ]; then
					echo "Error: Failed to restore $file"
				fi
				;;
		esac
	done

	# Restore GPU runtime power management state
	GPU_NODE=$(head -n1 ${GPU_RM_STATUS_FILE})
	grep -q "Runtime Power Management Control: auto" "${GPU_RM_STATUS_FILE}"
	ret=$?
	if [ ${ret} -eq 0 ]; then
		echo auto > "${GPU_NODE}/power/control"
	else
		echo on > "${GPU_NODE}/power/control"
	fi

	# Restore GPU persistent mode
	grep -Eq "Persistence Mode\s+: Disabled" "${GPU_RM_STATUS_FILE}"
	# return 0 if found "Disabled" successfully, 1 otherwise.
	nvidia-smi -pm $?

	fan_restore
	dgpu_restore
}

store()
{
	[ $# -gt 0 ] && grep -H '' $@ >> "${CONF_FILE}"
}

do_nvpmodel()
{
	case "${ACTION}" in
		show)
			NVPMODEL_BIN="/usr/sbin/nvpmodel"
			NVPMODEL_CONF="/etc/nvpmodel.conf"
			if [ -e "${NVPMODEL_BIN}" ]; then
				if [ -e "${NVPMODEL_CONF}" ]; then
					POWER_MODE="$(nvpmodel -q | grep "NV Power Mode")"
					echo "${POWER_MODE}"
				fi
			fi
			;;
		esac
}

do_fan()
{
	NVFANCONTROL_STATUS="$(systemctl is-active nvfancontrol)"
	NVSPISERVER_STATUS="$(systemctl is-active nv-spi-server)"
	TJ_ZONE="$(grep -il tj-thermal /sys/class/thermal/thermal_zone*/type | xargs dirname)"
	FAN_MAX_PWM=255
	FAN_NODES=(
		/sys/devices/platform/pwm-fan*/hwmon/hwmon*/pwm*
		/sys/bus/i2c/drivers/f75308/*/hwmon/hwmon*/pwm*
	)

	if [ ! -e "${TJ_ZONE}/policy" ]; then
		echo "Error: Failed to find policy of Tj thermal zone!"
		return
	fi

	case "${ACTION}" in
		show)
			read tj_curr_policy < "${TJ_ZONE}/policy"

			dyn_fan_ctrl_src="disabled"
			if [ "${NVSPISERVER_STATUS}" == "active" ]; then
				echo "FAN Dynamic Speed Control=SMCU"
				return
			elif [ "${NVFANCONTROL_STATUS}" == "active" ]; then
				dyn_fan_ctrl_src="nvfancontrol"
			elif [ "${tj_curr_policy}" != "user_space" ]; then
				dyn_fan_ctrl_src="kernel"
			fi

			for file in "${FAN_NODES[@]}"; do
				if [ -e "${file}" ]; then
					hwmon_id="$(dirname ${file} | xargs basename)"
					pwm_id="$(basename ${file})"
					pwm="$(cat ${file})"
					echo "FAN Dynamic Speed Control=${dyn_fan_ctrl_src} ${hwmon_id}_${pwm_id}=${pwm}"
				fi
			done
			;;
		store)
			if [ "${NVSPISERVER_STATUS}" == "active" ]; then
				return
			fi

			store "${TJ_ZONE}/policy"
			echo "${NVFANCONTROL_STATUS}" > "${FAN_STATUS_FILE}"
			store "${FAN_STATUS_FILE}"
			;;
		*)
			if [ "${NVSPISERVER_STATUS}" == "active" ]; then
				return
			fi

			if [ "${FAN_SPEED_OVERRIDE}" -eq "1" ]; then
				systemctl stop nvfancontrol

				# Switch to userspace policy for tj thermal sensor
				echo "user_space" > "${TJ_ZONE}/policy"
				ret="$?"
				if [ "${ret}" -ne "0" ]; then
					echo "Error: Failed to switch to user_space for Tj thermal zone!"
					return
				fi

				for file in "${FAN_NODES[@]}"; do
					if [ -e "${file}" ]; then
						echo "${FAN_MAX_PWM}" > "${file}";
					fi
				done
			fi
			;;
	esac
}

do_hotplug()
{
	case "${ACTION}" in
		show)
			echo "Online CPUs: $(cat /sys/devices/system/cpu/online), Offline CPUs: $(cat /sys/devices/system/cpu/offline)"
			;;
		store)
			for file in /sys/devices/system/cpu/cpu[0-9]*/online; do
				store "${file}"
			done
			;;
		*)
			;;
	esac
}

do_cpu()
{
	FREQ_GOVERNOR="cpufreq/scaling_governor"
	CPU_MIN_FREQ="cpufreq/scaling_min_freq"
	CPU_MAX_FREQ="cpufreq/scaling_max_freq"
	CPU_CUR_FREQ="cpufreq/scaling_cur_freq"
	CPU_SYSFS="/sys/devices/system/cpu"

	if [ ! -d "${CPU_SYSFS}/cpu0/cpuidle" ]; then
		echo "WARNING! CPUIDLE is not supported!"
	fi


	if [ ! -d "${CPU_SYSFS}/cpu0/cpufreq" ]; then
		echo "WARNING! CPUFREQ is not supported!"
	fi

	case "${ACTION}" in
		show)
			for folder in $(ls -d ${CPU_SYSFS}/cpu[0-9]* | sort -V); do
				if [ "$(cat ${folder}/online)" -eq 0 ]; then
					continue
				fi
				CPU=$(basename ${folder})
				if [ -d "${folder}/cpuidle" ]; then
					idle_states=""
					for idle in ${folder}/cpuidle/state[0-9]*; do
						idle_states+="$(cat ${idle}/name)";
						idle_disable="$(cat ${idle}/disable)"
						idle_states+="=$((idle_disable==0)) ";
					done
				fi
				if [ -e "${folder}/${FREQ_GOVERNOR}" ]; then
					printf "%-7s" ${CPU}:
					echo "Governor=$(cat ${folder}/${FREQ_GOVERNOR})" \
						"MinFreq=$(cat ${folder}/${CPU_MIN_FREQ})" \
						"MaxFreq=$(cat ${folder}/${CPU_MAX_FREQ})" \
						"CurrentFreq=$(cat ${folder}/${CPU_CUR_FREQ})"\
						"IdleStates: $idle_states";
				fi
			done
			;;
		store)
			store ${CPU_SYSFS}/cpu[0-9]*/cpufreq/scaling_min_freq
			store ${CPU_SYSFS}/cpu[0-9]*/cpuidle/state[0-9]*/disable
			;;
		*)
			if [ -d "${CPU_SYSFS}/cpu0/cpufreq" ]; then
				for folder in ${CPU_SYSFS}/cpu[0-9]*; do
					cat "${folder}/${CPU_MAX_FREQ}" > "${folder}/${CPU_MIN_FREQ}" 2>/dev/null
				done
			fi

			if [ -d "${CPU_SYSFS}/cpu0/cpuidle" ]; then
				tee ${CPU_SYSFS}/cpu[0-9]*/cpuidle/state[0-9]*/disable <<< 1 >/dev/null
			fi
			;;
	esac
}

count_set_bits()
{
	num="${1}"
	count=0

	while [ ${num} != 0 ]
	do
		count=$((${count} + (${num} & 1)))
		num=$((${num} >> 1))
	done

	return ${count}
}

get_platform_gpu_tpcs()
{
	case "${SOCFAMILY}" in
		tegra234)
			return 8
			;;
		tegra264)
			return 10
			;;
		*)
			echo "Active GPU TPCs: Error! unsupported SOC ${SOCFAMILY}"
			;;
	esac

	return 0
}

get_active_gpu_tpcs()
{
	get_platform_gpu_tpcs
	total_tpcs="$?"
	if [ ${total_tpcs} != 0 ]; then
		tpc_pg_mask="$(cat "${1}")"
		count_set_bits "${tpc_pg_mask}"
		set_bits="$?"
		return $((${total_tpcs} - ${set_bits}))
	fi

	return 0
}

do_igpu()
{

	if [ "${SOCFAMILY}" == "tegra264" ]; then
		GPU_NODE="/sys/bus/pci/devices/0000:01:00.0/"
		GPU_RAIL_GATE="/sys/bus/pci/devices/0000:01:00.0/railgate_enable"
		tpc_pg_mask_path="/sys/bus/pci/devices/0000:01:00.0/tpc_pg_mask"
	else
		GPU_NODE="/sys/devices/platform/gpu.0/of_node/"
		GPU_RAIL_GATE="/sys/devices/platform/gpu.0/railgate_enable"
		tpc_pg_mask_path="/sys/devices/platform/gpu.0/tpc_pg_mask"
	fi

	if [ ! -d "${GPU_NODE}" ]; then
		echo "Error! No GPU found!"
		exit 1
	fi

	# If GPU is probed with RM driver:
	if [ ${gpu_rm_probed} -eq 1 ]; then
		case "${ACTION}" in
			show)
				for devfreq in gpu-gpc-0 gpu-nvd-0 gpu-pwr-0; do
					if [ -d "/sys/class/devfreq/${devfreq}" ]; then
						echo "${devfreq} MinFreq=$(cat /sys/class/devfreq/${devfreq}/min_freq)" \
							"MaxFreq=$(cat /sys/class/devfreq/${devfreq}/max_freq)" \
							"CurrentFreq=$(cat /sys/class/devfreq/${devfreq}/cur_freq)"
					fi
				done
				;;
			store)
				echo ${GPU_NODE} > "${GPU_RM_STATUS_FILE}"

				# Check if GPU runtime power management is enabled
				if [ -f "${GPU_NODE}/power/control" ]; then
					runtime_control=$(cat "${GPU_NODE}/power/control")
					echo "Runtime Power Management Control: ${runtime_control}" >> "${GPU_RM_STATUS_FILE}"
				fi

				# Save GPU persistent mode
				nvidia-smi -i 0000:01:00.0 -q | grep "Persistence Mode" >> "${GPU_RM_STATUS_FILE}"

				for devfreq in gpu-gpc-0 gpu-nvd-0 gpu-pwr-0; do
					if [ -d "/sys/class/devfreq/${devfreq}" ]; then
						store "/sys/class/devfreq/${devfreq}/min_freq"
					fi
				done
				;;
			*)
				# Operations with GPU RM:
				# - Disable GPU RM Rail-Gating
				# - Enable GPU RM persistent mode
				# - Set each GPU devfreq device (GPC/NVD/PWR) to max frequency
				echo on > "${GPU_NODE}/power/control"
				nvidia-smi -pm 1

				for devfreq in gpu-gpc-0 gpu-nvd-0 gpu-pwr-0; do
					if [ -d "/sys/class/devfreq/${devfreq}" ]; then
						max_freq=$(cat /sys/class/devfreq/${devfreq}/max_freq)
						echo "${max_freq}" > /sys/class/devfreq/${devfreq}/min_freq
					fi
				done
				;;
		esac
		return
	fi

	detect_igpu_devfreq
	if [ ${has_igpu_devfreq} -eq 0 ]; then
		# The GPU needs to be powered up once to register with DEVFREQ
		GPU_RAIL_GATE_STATE="$(cat ${GPU_RAIL_GATE})"
		echo 0 > "${GPU_RAIL_GATE}"

		# Detect GPU DEVFREQ again (once only)
		detect_igpu_devfreq

		# Restore the GPU rail gate state
		echo "${GPU_RAIL_GATE_STATE}" > "${GPU_RAIL_GATE}"
	fi

	if [ ${has_igpu_devfreq} -eq 0 ]; then
		echo "Error! GPU frequency scaling not supported!"
		exit 1
	fi

	if [ -f "${tpc_pg_mask_path}" ]; then
		get_active_gpu_tpcs "${tpc_pg_mask_path}"
		active_gpu_tpcs="$?"
	else
		active_gpu_tpcs="Not Present!"
	fi

	case "${ACTION}" in
		show)
			echo "GPU MinFreq=$(cat ${GPU_MIN_FREQ})" \
				"MaxFreq=$(cat ${GPU_MAX_FREQ})" \
				"CurrentFreq=$(cat ${GPU_CUR_FREQ})"

			if [ "${active_gpu_tpcs}" != 0 ]; then
				echo "Active GPU TPCs: ${active_gpu_tpcs}"
			fi

			;;
		store)
			store "${GPU_MIN_FREQ}"
			if [ -f "${GPU_RAIL_GATE}" ]; then
				store "${GPU_RAIL_GATE}"
			fi
			;;
		*)
			if [ -f "${GPU_RAIL_GATE}" ]; then
				echo 0 > "${GPU_RAIL_GATE}"
			fi
			cat "${GPU_MAX_FREQ}" > "${GPU_SET_FREQ}"
			ret=$?
			if [ ${ret} -ne 0 ]; then
				echo "Error: Failed to max GPU frequency!"
			fi
			;;
	esac
}

do_dgpu()
{
	NV_SMI_QUERY_GPU="nvidia-smi --format=csv,noheader,nounits --query-gpu"
	dGPU_DEF_MEM="$(${NV_SMI_QUERY_GPU}=clocks.default_applications.memory)"
	dGPU_MAX_MEM="$(${NV_SMI_QUERY_GPU}=clocks.max.memory)"
	dGPU_CUR_MEM="$(${NV_SMI_QUERY_GPU}=clocks.applications.memory)"
	dGPU_DEF_GRA="$(${NV_SMI_QUERY_GPU}=clocks.default_applications.graphics)"
	dGPU_MAX_GRA="$(${NV_SMI_QUERY_GPU}=clocks.max.graphics)"
	dGPU_CUR_GRA="$(${NV_SMI_QUERY_GPU}=clocks.applications.graphics)"

	case "${ACTION}" in
		show)
			echo "dGPU DefaultMemFreq=${dGPU_DEF_MEM}MHz" \
				"MaxMemFreq=${dGPU_MAX_MEM}MHz" \
				"CurrentMemFreq=${dGPU_CUR_MEM}MHz"
			echo "dGPU DefaultGraFreq=${dGPU_DEF_GRA}MHz" \
				"MaxGraFreq=${dGPU_MAX_GRA}MHz" \
				"CurrentGraFreq=${dGPU_CUR_GRA}MHz"
			;;
		*)
			nvidia-smi -pm ENABLED -i 0 > /dev/null
			nvidia-smi -ac ${dGPU_MAX_MEM},${dGPU_MAX_GRA} -i 0 > /dev/null
			ret=$?
			if [ ${ret} -ne 0 ]; then
				echo "Error: Failed to max dGPU application clocks frequency!"
			fi
			;;
	esac
}

do_emc()
{
	case "${SOCFAMILY}" in
		tegra234|tegra239|tegra264)
			EMC_MIN_FREQ="/sys/class/devfreq/bwmgr/min_freq"
			EMC_MAX_FREQ="/sys/class/devfreq/bwmgr/max_freq"
			EMC_CUR_FREQ="/sys/class/devfreq/bwmgr/cur_freq"
			EMC_UPDATE_FREQ="/sys/class/devfreq/bwmgr/min_freq"
			;;
		*)
			echo "Error! unsupported SOC ${SOCFAMILY}"
			exit 1;
			;;
	esac

	case "${ACTION}" in
		show)
			echo "EMC MinFreq=$(cat ${EMC_MIN_FREQ})" \
				"MaxFreq=$(cat ${EMC_MAX_FREQ})" \
				"CurrentFreq=$(cat ${EMC_CUR_FREQ})"
			;;
		store)
			store "${EMC_MIN_FREQ}"
			store "${EMC_MAX_FREQ}"
			;;
		*)
			max_freq=$(cat ${EMC_MAX_FREQ})
			echo "${max_freq}" > "${EMC_MIN_FREQ}"
			;;
	esac
}

do_dla ()
{
	dla_get_state
	case "${SOCFAMILY}" in
		tegra234)
			DLA0_CORE_MIN_FREQ="/sys/kernel/debug/clk/dla0_core/clk_min_rate"
			DLA0_CORE_MAX_FREQ="/sys/kernel/debug/clk/dla0_core/clk_max_rate"
			DLA0_CORE_CUR_FREQ="/sys/kernel/debug/clk/dla0_core/clk_rate"
			DLA0_FALCON_MIN_FREQ="/sys/kernel/debug/clk/dla0_falcon/clk_min_rate"
			DLA0_FALCON_MAX_FREQ="/sys/kernel/debug/clk/dla0_falcon/clk_max_rate"
			DLA0_FALCON_CUR_FREQ="/sys/kernel/debug/clk/dla0_falcon/clk_rate"
			DLA1_CORE_MIN_FREQ="/sys/kernel/debug/clk/dla1_core/clk_min_rate"
			DLA1_CORE_MAX_FREQ="/sys/kernel/debug/clk/dla1_core/clk_max_rate"
			DLA1_CORE_CUR_FREQ="/sys/kernel/debug/clk/dla1_core/clk_rate"
			DLA1_FALCON_MIN_FREQ="/sys/kernel/debug/clk/dla1_falcon/clk_min_rate"
			DLA1_FALCON_MAX_FREQ="/sys/kernel/debug/clk/dla1_falcon/clk_max_rate"
			DLA1_FALCON_CUR_FREQ="/sys/kernel/debug/clk/dla1_falcon/clk_rate"
			;;
		*)
			;;

	esac

	case "${ACTION}" in
		show)
			if [ -e "${DLA0_CORE_MAX_FREQ}" ]; then
				echo "DLA0_CORE:   Online=${DLA0_STATE}" \
					"MinFreq=$(cat ${DLA0_CORE_MIN_FREQ})" \
					"MaxFreq=$(cat ${DLA0_CORE_MAX_FREQ})" \
					"CurrentFreq=$(cat ${DLA0_CORE_CUR_FREQ})"
			fi
			if [ -e "${DLA0_FALCON_MAX_FREQ}" ]; then
				echo "DLA0_FALCON: Online=${DLA0_STATE}" \
					"MinFreq=$(cat ${DLA0_FALCON_MIN_FREQ})" \
					"MaxFreq=$(cat ${DLA0_FALCON_MAX_FREQ})" \
					"CurrentFreq=$(cat ${DLA0_FALCON_CUR_FREQ})"
			fi
			if [ -e "${DLA1_CORE_MAX_FREQ}" ]; then
				echo "DLA1_CORE:   Online=${DLA1_STATE}" \
					"MinFreq=$(cat ${DLA1_CORE_MIN_FREQ})" \
					"MaxFreq=$(cat ${DLA1_CORE_MAX_FREQ})" \
					"CurrentFreq=$(cat ${DLA1_CORE_CUR_FREQ})"
			fi
			if [ -e "${DLA1_FALCON_MAX_FREQ}" ]; then
				echo "DLA1_FALCON: Online=${DLA1_STATE}" \
					"MinFreq=$(cat ${DLA1_FALCON_MIN_FREQ})" \
					"MaxFreq=$(cat ${DLA1_FALCON_MAX_FREQ})" \
					"CurrentFreq=$(cat ${DLA1_FALCON_CUR_FREQ})"
			fi
			;;
		*)
			;;
	esac
}

do_pva ()
{
	pva_get_state
	case "${SOCFAMILY}" in
		tegra234 | tegra264)
			PVA0_VPS0_MIN_FREQ="/sys/kernel/debug/clk/pva0_vps/clk_min_rate"
			PVA0_VPS0_MAX_FREQ="/sys/kernel/debug/clk/pva0_vps/clk_max_rate"
			PVA0_VPS0_CUR_FREQ="/sys/kernel/debug/clk/pva0_vps/clk_rate"
			PVA0_AXI_MIN_FREQ="/sys/kernel/debug/clk/pva0_cpu_axi/clk_min_rate"
			PVA0_AXI_MAX_FREQ="/sys/kernel/debug/clk/pva0_cpu_axi/clk_max_rate"
			PVA0_AXI_CUR_FREQ="/sys/kernel/debug/clk/pva0_cpu_axi/clk_rate"
			;;
		*)
			;;

	esac

	case "${ACTION}" in
		show)
			if [ -e "${PVA0_VPS0_MAX_FREQ}" ]; then
				echo "PVA0_VPS0: Online=${PVA0_STATE}" \
					"MinFreq=$(cat ${PVA0_VPS0_MIN_FREQ})" \
					"MaxFreq=$(cat ${PVA0_VPS0_MAX_FREQ})" \
					"CurrentFreq=$(cat ${PVA0_VPS0_CUR_FREQ})"
			fi
			if [ -e "${PVA0_VPS1_MAX_FREQ}" ]; then
				echo "PVA0_VPS1: Online=${PVA0_STATE}" \
					"MinFreq=$(cat ${PVA0_VPS1_MIN_FREQ})" \
					"MaxFreq=$(cat ${PVA0_VPS1_MAX_FREQ})" \
					"CurrentFreq=$(cat ${PVA0_VPS1_CUR_FREQ})"
			fi
			if [ -e "${PVA0_AXI_MAX_FREQ}" ]; then
				echo "PVA0_AXI:  Online=${PVA0_STATE}" \
					"MinFreq=$(cat ${PVA0_AXI_MIN_FREQ})" \
					"MaxFreq=$(cat ${PVA0_AXI_MAX_FREQ})" \
					"CurrentFreq=$(cat ${PVA0_AXI_CUR_FREQ})"
			fi
			if [ -e "${PVA1_VPS0_MAX_FREQ}" ]; then
				echo "PVA1_VPS0: Online=${PVA1_STATE}" \
					"MinFreq=$(cat ${PVA1_VPS0_MIN_FREQ})" \
					"MaxFreq=$(cat ${PVA1_VPS0_MAX_FREQ})" \
					"CurrentFreq=$(cat ${PVA1_VPS0_CUR_FREQ})"
			fi
			if [ -e "${PVA1_VPS1_MAX_FREQ}" ]; then
				echo "PVA1_VPS1: Online=${PVA1_STATE}" \
					"MinFreq=$(cat ${PVA1_VPS1_MIN_FREQ})" \
					"MaxFreq=$(cat ${PVA1_VPS1_MAX_FREQ})" \
					"CurrentFreq=$(cat ${PVA1_VPS1_CUR_FREQ})"
			fi
			if [ -e "${PVA1_AXI_MAX_FREQ}" ]; then
				echo "PVA1_AXI:  Online=${PVA1_STATE}" \
					"MinFreq=$(cat ${PVA1_AXI_MIN_FREQ})" \
					"MaxFreq=$(cat ${PVA1_AXI_MAX_FREQ})" \
					"CurrentFreq=$(cat ${PVA1_AXI_CUR_FREQ})"
			fi
			;;
		*)
			;;
	esac
}

do_cvnas ()
{
	case "${ACTION}" in
		show)
			if [ -e "${CVNAS_MAX_FREQ}" ]; then
				echo "CVNAS MinFreq=$(cat ${CVNAS_MIN_FREQ})" \
					"MaxFreq=$(cat ${CVNAS_MAX_FREQ})" \
					"CurrentFreq=$(cat ${CVNAS_CUR_FREQ})"
			fi
			;;
		*)
			;;
	esac
}

t264_is_rm_gpu_probed()
{
	# Check if platform is T264
	if [ "${SOCFAMILY}" != "tegra264" ]; then
		return 1
	fi

	# Check if GPU is probed by checking PCI device node
	if [ ! -d "/sys/bus/pci/devices/0000:01:00.0/" ]; then
		return 1
	fi

	# Check if GPU is using NVIDIA driver
	gpu_driver=$(basename $(realpath /sys/bus/pci/devices/0000:01:00.0/driver))
	if [ "${gpu_driver}" = "nvidia" ]; then
		gpu_rm_probed=1
		return 0
	fi

	return 1
}

main ()
{
	while [ -n "$1" ]; do
		case "$1" in
			--show)
				echo "SOC family:${SOCFAMILY}  Machine:${machine}"
				ACTION=show
				;;
			--store)
				[ -n "$2" ] && CONF_FILE=$2
				ACTION=store
				shift 1
				;;
			--restore)
				[ -n "$2" ] && CONF_FILE=$2
				ACTION=restore
				shift 1
				;;
			--fan)
				FAN_SPEED_OVERRIDE=1
				;;
			-h|--help)
				usage
				exit 0
				;;
			*)
				usage "Unknown option: $1"
				exit 1
				;;
		esac
		shift 1
	done

	[ "$(whoami)" != "root" ] && \
		echo Error: Run this script\($0\) as a root user && exit 1

	case $ACTION in
		store)
			if [ -e "${CONF_FILE}" ]; then
				echo "File $CONF_FILE already exists. Can I overwrite it? Y/N:"
				read answer
				case $answer in
					y|Y)
						rm -f $CONF_FILE
						;;
					*)
						echo "Error: file $CONF_FILE already exists!"
						exit 1
						;;
				esac
			fi
			echo "Storing system configuration in ${CONF_FILE}"
			;;
		restore)
			if [ ! -e "${CONF_FILE}" ]; then
				echo "Error: $CONF_FILE file not found !"
				exit 1
			fi
			echo "Restoring system configuration from ${CONF_FILE}"
			restore
			exit 0
			;;
	esac

	do_hotplug
	do_cpu
	t264_is_rm_gpu_probed
	detect_gpu_type
	if [[ "${gpu_type}" == "iGPU" ]]; then
		do_igpu
	elif [[ "${gpu_type}" == "dGPU" ]]; then
		check_nvidia_smi
		do_dgpu
	else
		echo "Error: Unknown GPU Type!"
		exit 1
	fi
	do_emc
	do_dla
	do_pva
	do_cvnas
	do_fan
	do_nvpmodel
}

if [ -e "/proc/device-tree/compatible" ]; then
	if [ -e "/proc/device-tree/model" ]; then
		machine="$(tr -d '\0' < /proc/device-tree/model)"
	fi
	CHIP="$(tr -d '\0' < /proc/device-tree/compatible)"
	if [[ "${CHIP}" =~ "tegra234" ]]; then
		SOCFAMILY="tegra234"
	elif [[ "${CHIP}" =~ "tegra264" ]]; then
		SOCFAMILY="tegra264"
	fi
fi

main $@
exit 0
