#!/bin/bash

#
# SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the copyright holder 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 AND CONTRIBUTORS "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 HOLDER 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.

SCRIPT_NAME=$(basename "${0}")
NVPM_SYMLINK="/etc/nvpmodel.conf"

function set_socfamily()
{
	if [ -e "/proc/device-tree/compatible" ]; then
		machine="$(tr -d '\0' < /proc/device-tree/compatible)"
		if [[ "${machine}" =~ "p3711-0000" ]]; then
			cvb="p3711-0000"
		elif [[ "${machine}" =~ "p3740-0002-b01" ]]; then
			cvb="p3740-0002-b01"
		elif [[ "${machine}" =~ "p3740-0002" ]]; then
			cvb="p3740-0002-c01"
		elif [[ "${machine}" =~ "p3971" ]]; then
			cvb="p3971"
		fi

		if [[ "${machine}" =~ "e3900" ]]; then
			machine="e3900"
		elif [[ "${machine}" =~ "jetson-xavier-industrial" ]]; then
			machine="jetson-xavier-industrial"
		elif [[ "${machine}" =~ "jetson-xavier" ]]; then
			machine="jetson-xavier"
		elif [[ "${machine}" =~ "p2972-0006" ]]; then
			machine="p2972-0006"
		elif [[ "${machine}" =~ "p3668" ]]; then
			if [[ "${machine}" =~ "nvidia,p3668-emul" ]]; then
				machine="p3668-emul"
			else
				machine="p3668"
			fi
		elif [[ "${machine}" =~ "p3701" ]]; then
			if [[ "${machine}" =~ "p3701-0000-as-p3767-0000" ]]; then
				machine="p3701-0000-as-p3767-0000"
			elif [[ "${machine}" =~ "p3701-0000-as-p3767-0001" ]]; then
				machine="p3701-0000-as-p3767-0001"
			elif [[ "${machine}" =~ "p3701-0000-as-p3767-0003" ]]; then
				machine="p3701-0000-as-p3767-0003"
			elif [[ "${machine}" =~ "p3701-0000-as-p3767-0004" ]]; then
				machine="p3701-0000-as-p3767-0004"
			elif [[ "${machine}" =~ "p3701-0000-as-pxxxx" ]]; then
				machine="p3701-0000-as-pxxxx"
			elif [[ "${machine}" =~ "p3701-0002" ]]; then
				machine="p3701-0002"
			elif [[ "${machine}" =~ "p3701-0000-as-p3701-0004" ]]; then
				machine="p3701-0000-as-p3701-0004"
			elif [[ "${machine}" =~ "p3701-0004" ]]; then
				machine="p3701-0004"
			elif [[ "${machine}" =~ "p3701-0008" ]]; then
				if [[ "${machine}" =~ "safety" ]]; then
					machine="p3701-0008-safety"
				else
					machine="p3701-0008"
				fi
			else
				machine="p3701-0000"
			fi
		elif [[ "${machine}" =~ "p3767" ]]; then
			if [[ "${machine}" =~ "p3767-0000-as-p3767-0001" ]]; then
				machine="p3767-0000-as-p3767-0001"
			elif [[ "${machine}" =~ "p3767-0000-as-p3767-0003" ]]; then
				machine="p3767-0000-as-p3767-0003"
			elif [[ "${machine}" =~ "p3767-0000-as-p3767-0004" ]]; then
				machine="p3767-0000-as-p3767-0004"
			elif [[ "${machine}" =~ "p3767-0000-super" ]]; then
				machine="p3767-0000-super"
			elif [[ "${machine}" =~ "p3767-0000-px1" ]]; then
				machine="p3767-0000-px1"
			elif [[ "${machine}" =~ "p3767-0001-super" ]]; then
				machine="p3767-0001-super"
			elif [[ "${machine}" =~ "p3767-0001" ]]; then
				machine="p3767-0001"
			elif [[ "${machine}" =~ "p3767-0002" ]]; then
				machine="p3767-0002"
			elif [[ "${machine}" =~ "p3767-0003-super" ]]; then
				machine="p3767-0003-super"
			elif [[ "${machine}" =~ "p3767-0003" ]]; then
				machine="p3767-0003"
			elif [[ "${machine}" =~ "p3767-0004-super" ]]; then
				machine="p3767-0004-super"
			elif [[ "${machine}" =~ "p3767-0004" ]]; then
				machine="p3767-0004"
			elif [[ "${machine}" =~ "p3767-0005-super" ]]; then
				machine="p3767-0005-super"
			elif [[ "${machine}" =~ "p3767-0005" ]]; then
				machine="p3767-0005"
			else
				machine="p3767-0000"
			fi
		elif [[ "${machine}" =~ "e2421-1099-as-pxxxx" ]]; then
				machine="e2421-1099-as-pxxxx"
		elif [[ "${machine}" =~ "e2423" ]]; then
				 machine="e2423"
		else
			machine="$(cat /proc/device-tree/model)"
		fi

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

function set_safety_flag()
{
	IS_SAFETY_PLATFORM=0
	if [[ "${machine}" =~ "safety" ]]; then
		IS_SAFETY_PLATFORM=1
	fi
}

function set_power_state_perm()
{
	# set power state permission
	if [ -e "/sys/power/state" ]; then
		chmod 0666 "/sys/power/state"
	fi
}

function is_nvpm_relinking_needed()
{
	declare -a plat_variants=("super" "safety")
	linked_conf="$(readlink "${NVPM_SYMLINK}")"
	ret="$?"

	if [ "${ret}" -ne "0" ]; then
		# Early return if the nvpmodel conf is not symlink or on error
		return 1
	fi

	# Check if there is a mismatch between compatible str and conf in use
	for plat in "${plat_variants[@]}"; do
		if [[ "${machine}" =~ ${plat} ]] && \
			[[ ! "${linked_conf}" =~ ${plat} ]]; then
			echo "${SCRIPT_NAME} - NOTICE: Relinking the" \
				"nvpmodel conf from non-${plat} to ${plat}"
			return 0
		elif [[ ! "${machine}" =~ ${plat} ]] && \
			[[ "${linked_conf}" =~ ${plat} ]]; then
			echo "${SCRIPT_NAME} - NOTICE: Relinking the" \
				"nvpmodel conf from ${plat} to non-${plat}"
			return 0
		fi
	done

	return 1
}

function create_nvpmodel_symlink()
{
	conf_file=""

	if [ -e "${NVPM_SYMLINK}" ]; then
		if is_nvpm_relinking_needed; then
			nvpm_back="/etc/nvpmodel_nvbackup.conf"
			echo "${SCRIPT_NAME} - NOTICE: Backing up the" \
				"existing ${NVPM_SYMLINK} to ${nvpm_back}!"
			cp -b -S .bak "${NVPM_SYMLINK}" "${nvpm_back}"

			unlink "${NVPM_SYMLINK}"
			ret="$?"
			if [ "${ret}" -ne "0" ]; then
				echo "${SCRIPT_NAME} - Error: Failed" \
					"to unlink ${NVPM_SYMLINK}!"
			else
				rm /var/lib/nvpmodel/status
			fi
		fi
	fi

	# create /etc/nvpmodel.conf symlink
	if [ ! -e "${NVPM_SYMLINK}" ]; then
		if [ "${SOCFAMILY}" = "tegra194" ]; then
			if [ "${machine}" = "e3900" ]; then
				if [ -d "/sys/devices/gpu.0" ] &&
					[ -d "/sys/devices/17000000.gv11b" ]; then
					conf_file="/etc/nvpmodel/nvpmodel_t194_e3900_iGPU.conf"
				else
					conf_file="/etc/nvpmodel/nvpmodel_t194_e3900_dGPU.conf"
				fi
			elif [ "${machine}" = "p2972-0006" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_t194_8gb.conf"
			elif [ "${machine}" = "p3668" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_t194_p3668.conf"
			elif [ "${machine}" = "p3668-emul" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_t194_p3668_emul.conf"
			elif [ "${machine}" = "jetson-xavier-industrial" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_t194_agxi.conf"
			else
				conf_file="/etc/nvpmodel/nvpmodel_t194.conf"
			fi
		elif [ "${SOCFAMILY}" = "tegra234" ]; then
			if [ "${machine}" = "p3701-0000-as-p3767-0000" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0000.conf"
			elif [ "${machine}" = "p3701-0000-as-p3767-0001" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0001.conf"
			elif [ "${machine}" = "p3701-0000-as-p3767-0003" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0003.conf"
			elif [ "${machine}" = "p3701-0000-as-p3767-0004" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0004.conf"
			elif [ "${machine}" = "p3701-0000-as-pxxxx" ] || \
				[ "${machine}" = "e2421-1099-as-pxxxx" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_pxxxx.conf"
			elif [ "${machine}" = "p3701-0002" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3701_0002.conf"
			elif [ "${machine}" = "p3701-0000-as-p3701-0004" ] || \
				[ "${machine}" = "p3701-0004" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3701_0004.conf"
			elif [ "${machine}" = "p3701-0008-safety" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_igx_orin_safety.conf"
			elif [ "${machine}" = "p3701-0008" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3701_0008.conf"
				if [ "${cvb}" = "p3740-0002-c01" ] || \
					[ "${cvb}" = "p3971" ]; then
					conf_file="/etc/nvpmodel/nvpmodel_igx_orin.conf"
				fi
			elif [ "${machine}" = "p3767-0000" ] || \
				[ "${machine}" = "p3767-0002" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0000.conf"
			elif [ "${machine}" = "p3767-0000-super" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0000_super.conf"
			elif [ "${machine}" = "p3767-0000-px1" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0000_px1.conf"
			elif [ "${machine}" = "p3767-0000-as-p3767-0001" ] || \
				[ "${machine}" = "p3767-0001" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0001.conf"
			elif [ "${machine}" = "p3767-0001-super" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0001_super.conf"
			elif [ "${machine}" = "p3767-0003" ] || \
				[ "${machine}" = "p3767-0005" ] || \
				[ "${machine}" = "p3767-0000-as-p3767-0003" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0003.conf"
			elif [ "${machine}" = "p3767-0003-super" ] || \
				[ "${machine}" = "p3767-0005-super" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0003_super.conf"
			elif [ "${machine}" = "p3767-0004" ] || \
				[ "${machine}" = "p3767-0000-as-p3767-0004" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0004.conf"
			elif [ "${machine}" = "p3767-0004-super" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_p3767_0004_super.conf"
			else
				conf_file="/etc/nvpmodel/nvpmodel_p3701_0000.conf"
			fi
		elif [ "${SOCFAMILY}" = "tegra239" ]; then
			if [ "${machine}" = "e2423" ]; then
				conf_file="/etc/nvpmodel/nvpmodel_e2423.conf"
			fi
		fi

		if [ "${conf_file}" != "" ]; then
			if [ -e "${conf_file}" ]; then
				ln -sf "${conf_file}" "${NVPM_SYMLINK}"
			else
				echo "${SCRIPT_NAME} - WARNING: file ${conf_file} not found!"
			fi
		fi
	fi
}

function create_nvfancontrol_symlink()
{
	conf_file=""
	if [ ! -e "/etc/nvfancontrol.conf" ]; then
		if [ "${SOCFAMILY}" = "tegra194" ]; then
			if [ "${machine}" = "e3900" ]; then
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_e3900.conf"
			elif [ "${machine}" = "p3668" ]; then
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3668.conf"
			else
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p2888.conf"
			fi
		fi

		if [ "${SOCFAMILY}" = "tegra234" ]; then
			if [[ "${machine}" =~ "p3701" ]]; then
				# Use p3701_0000 as default fan settings for p3701 series
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3701_0000.conf"
				if [[ "${machine}" =~ "p3701-0008" ]]; then
					conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3701_0008.conf"
				fi
			elif [[ "${machine}" =~ "p3767" ]]; then
				# All p3767 series share the same fan settings
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3767_0000.conf"
			else
				# Use p3701_0000 as default fan settings for other tegra234 platforms
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3701_0000.conf"
			fi

			if [ "${cvb}" = "p3711-0000" ]; then
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3711_0000.conf"
			elif [ "${cvb}" = "p3740-0002-b01" ]; then
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3740_0002_b01.conf"
			elif [ "${cvb}" = "p3740-0002-c01" ]; then
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_p3740_0002_c01.conf"
			fi
		fi

		if [ "${SOCFAMILY}" = "tegra239" ]; then
			if [ "${machine}" = "e2423" ]; then
				conf_file="/etc/nvpower/nvfancontrol/nvfancontrol_e2423.conf"
			fi
		fi

		if [ "${conf_file}" != "" ]; then
			if [ -e "${conf_file}" ]; then
				ln -sf "${conf_file}" /etc/nvfancontrol.conf
			else
				echo "${SCRIPT_NAME} - WARNING: file ${conf_file} not found!"
			fi
		fi
	fi
}

# CPU hotplug helper function that turns on/off the CPU cores in the specified range
# * Parameters:
# * ${1}: desired online status (0 for offline, 1 for online)
# * ${2}: cpu core starting index (inclusive)
# * ${3}: cpu core ending index (inclusive)
function cpu_hotplug_helper()
{
	desired_status="$1"
	cpu_core_from="$2"
	cpu_core_to="$3"
	retry_count=0
	retry_limit=3

	for (( core=cpu_core_from; core<=cpu_core_to;)); do
		if [ -e "/sys/devices/system/cpu/cpu${core}/online" ]; then
			echo "${desired_status}" > "/sys/devices/system/cpu/cpu${core}/online"
			ret="$?"
			if [ "${ret}" -ne "0" ] && [ "${retry_count}" -lt "${retry_limit}" ]; then
				# Retry CPU hotplug after 100ms
				retry_count=$((retry_count+1))
				sleep 0.1
			else
				retry_count=0
				core=$((core+1))
			fi
		else
			core=$((core+1))
		fi
	done
}

function cpu_hotplug()
{
	# CPU hotplug
	if [ "$SOCFAMILY" = "tegra194" ]; then
		if [ "$machine" = "p2972-0006" -o "$machine" = "p3668" ]; then
			cpu_hotplug_helper 0 6 7
		fi
	elif [ "$SOCFAMILY" = "tegra234" ]; then
		if [ "${machine}" = "p3701-0000-as-p3767-0001" ] || \
			[ "${machine}" = "p3701-0000-as-p3767-0003" ] || \
			[ "${machine}" = "p3701-0000-as-p3767-0004" ]; then
			cpu_hotplug_helper 0 6 7
		fi
	fi
}

function set_cpufreq_governor()
{
	# CPU governor setting
	CPUFREQ_AVAIL_GOVS="/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors"
	SCHEDUTIL="/sys/devices/system/cpu/cpufreq/schedutil"
	RATE_LIMIT_US="${SCHEDUTIL}/rate_limit_us"
	UP_LIMIT_US="${SCHEDUTIL}/up_rate_limit_us"
	DOWN_LIMIT_US="${SCHEDUTIL}/down_rate_limit_us"
	CAPACITY_MARGIN="${SCHEDUTIL}/capacity_margin"
	cpu_cores=$(nproc)
	desired_cpufreq_gov="performance"

	if [ ${IS_SAFETY_PLATFORM} -eq 0 ]; then
		# Enable SCHEDUTIL governor for all SoCs
		desired_cpufreq_gov="schedutil"
	fi

	if [ -e "${CPUFREQ_AVAIL_GOVS}" ]; then
		read governors < "${CPUFREQ_AVAIL_GOVS}"
		if [[ ! "${governors}" =~ ${desired_cpufreq_gov} ]]; then
			echo "${SCRIPT_NAME} - WARNING: ${desired_cpufreq_gov}" \
				"is not a valid cpufreq governor in [${governors}]"
			return
		fi
	else
		echo "${SCRIPT_NAME} - WARNING: failed to find ${CPUFREQ_AVAIL_GOVS}"
		return
	fi

	for (( core=0; core<cpu_cores; core++ )); do
		read is_cpu_online < "/sys/devices/system/cpu/cpu${core}/online"
		if [[ "${is_cpu_online}" != "0" ]]; then
			echo "${desired_cpufreq_gov}" > \
				"/sys/devices/system/cpu/cpu${core}/cpufreq/scaling_governor"
		fi
	done

	case "${SOCFAMILY}" in
		tegra194 | tegra234 | tegra239)
			if [ "${desired_cpufreq_gov}" = "schedutil" ]; then
				if [ -e "${RATE_LIMIT_US}" ]; then
					echo 2000 > "${RATE_LIMIT_US}"
				fi
				if [ -e "${UP_LIMIT_US}" ]; then
					echo 0 > "${UP_LIMIT_US}"
				fi
				if [ -e "${DOWN_LIMIT_US}" ]; then
					echo 500 > "${DOWN_LIMIT_US}"
				fi
				if [ -e "${CAPACITY_MARGIN}" ]; then
					echo 1024 > "${CAPACITY_MARGIN}"
				fi
			fi
			;;
		*)
			;;
	esac
}

function set_cpu_floor_freq()
{
	cpu_cores=$(nproc)

	case "${SOCFAMILY}" in
		tegra234)
			CPU_FLOOR_FREQ=729600
			for (( core=0; core<cpu_cores; core++ )); do
				read is_cpu_online < "/sys/devices/system/cpu/cpu${core}/online"
				if [ "${is_cpu_online}" != "0" ]; then
					echo ${CPU_FLOOR_FREQ} > \
						"/sys/devices/system/cpu/cpu${core}/cpufreq/scaling_min_freq"
				fi
			done
			;;
		*)
			;;
	esac
}

# devfreq governor configurations helper function
# * Parameters:
# * ${1}: devfreq governor
# * ${2}: sysfs path to engine's devfreq node
function configure_devfreq_gov()
{
	devfreq_gov="$1"
	devfreq_eng="$2"

	if [ "$devfreq_gov" = "tegra_wmark" ]; then
		# Common governor settings
		echo 100 > "${devfreq_eng}/tegra_wmark/down_wmark_margin"
		echo 100 > "${devfreq_eng}/tegra_wmark/up_wmark_margin"
		echo 1 > "${devfreq_eng}/tegra_wmark/down_freq_margin"
		echo 4 > "${devfreq_eng}/tegra_wmark/up_freq_margin"

		# Fine-tuned governor settings
		case "${devfreq_eng}" in
			*vic)
				echo 600 > "${devfreq_eng}/tegra_wmark/load_target"
				;;
			*)
				echo 800 > "${devfreq_eng}/tegra_wmark/load_target"
				;;
		esac
	fi
}

function set_devfreq_governor()
{
	DEVFREQ_DEVS="/sys/class/devfreq/*"
	desired_devfreq_gov="userspace"

	if [ ${IS_SAFETY_PLATFORM} -ne 0 ]; then
		desired_devfreq_gov="performance"
	else
		desired_devfreq_gov="tegra_wmark"
	fi

	for devfreq_dev in ${DEVFREQ_DEVS}; do
		if [ ${IS_SAFETY_PLATFORM} -eq 0 ]; then
			# Leave default devfreq configuration as specified in the driver
			if [[ "${devfreq_dev}" =~ "gpu" ]] || [[ "${devfreq_dev}" =~ "nvjpg" ]]; then
				continue
			fi
		fi

		if [ -e "${devfreq_dev}/available_governors" ]; then
			read governors < "${devfreq_dev}/available_governors"
		else
			echo "${SCRIPT_NAME} - WARNING: ${devfreq_dev}/available_governors" \
				"not found"
			continue
		fi

		if [[ "${governors}" =~ ${desired_devfreq_gov} ]]; then
			echo "${desired_devfreq_gov}" > "${devfreq_dev}/governor"
			configure_devfreq_gov "${desired_devfreq_gov}" "${devfreq_dev}"
		else
			echo "${SCRIPT_NAME} - WARNING: ${desired_devfreq_gov}" \
				"is not a valid devfreq governor under ${devfreq_dev}"
		fi
	done
}

function lock_se_frequency()
{
	# Lock SE clock at MinFreq to reduce vdd_soc power
	if [ "${SOCFAMILY}" = "tegra194" ]; then
		if [ -d "/sys/kernel/debug/bpmp/debug/clk/nafll_se" ]; then
			echo 1 > /sys/kernel/debug/bpmp/debug/clk/nafll_se/mrq_rate_locked
			cat /sys/kernel/debug/bpmp/debug/clk/nafll_se/min_rate > \
				/sys/kernel/debug/bpmp/debug/clk/nafll_se/rate
		fi
	fi
}

# validate the correct nvpmodel.conf file for the xavier-nx platform
function check_nvpmodel_param()
{
	if ! grep -q "< PARAM TYPE=${1} NAME=${2} >" "${NVPM_SYMLINK}"; then
		echo "nvpmodel: ${2} cap is not set" > /dev/kmsg
		return 1
	fi

	return 0
}

function validate_nvpmodel_conf()
{
	if [ "${SOCFAMILY}" == "tegra194" ]; then
		if [ "${machine}" == "p3668" ]; then
			state=0
			SOC_CLOCKS=("NVENC" "NVENC1" "NVDEC" "NVDEC1" "NVJPG" "SE1" "SE2" "SE3" "SE4")

			for clk in "${SOC_CLOCKS[@]}"; do
				check_nvpmodel_param "CLOCK" "${clk}"
				state=$((state | $?))
			done

			check_nvpmodel_param "HWMON" "VDDIN_OC_LIMIT"
			state=$((state | $?))
			if [ "${state}" == 1 ]; then
				echo "nvpmodel: incompatible nvpmodel.conf file. Regenerate latest conf file \
using PowerEstimator web tool - https://jetson-tools.nvidia.com/powerestimator" > /dev/kmsg
			fi
		fi
	fi
}

# HWMON device configurations helper function
# * Parameters:
# * ${1}: device node name
# * ${2}: setup node name
# * ${3}: setup value
# * ${4}: condition (optional)
# * ${5}: power limit in mW (optional)
# * ${6}: bus voltage channel to calculate critical current (optional)
# case ina3221: use first channel label name as the condition
#               since there could be multiple ina3221 nodes.
function config_hwmon()
{
	for dir in /sys/class/hwmon/*
	do
		# Match device node name
		if [ "$(< ${dir}/name)" != "${1}" ]; then continue; fi
		# Check the optional condition
		if [[ ! -z "${4}" ]]
		then
			case "${1}" in
			ina3221)
				if [ "$(< ${dir}/in1_label)" != "${4}" ]
				then continue
				fi
				;;
			*)
				echo "unsupported condition"
				;;
			esac
		fi

		# if power limit and bus voltage channel are provided
		# in $5 & $6, then calculate critical current limit
		# or else directly write $3 value into $2 node.
		if [[ ! -z "${5}" && ! -z "${6}" ]]; then
			power_limit="${5}";
			bus_volt="$(cat ${dir}/${6})"
			crit_limit="$((power_limit * 1000 / bus_volt))"
			echo "${crit_limit}" > "${dir}/${2}"
		else
			echo "${3}" > "${dir}/${2}"
		fi
	done
}

function setup_hwmon()
{
	case "${SOCFAMILY}" in
	tegra194 | tegra234 | tegra239)
		# Set averaging mode to use 512 samples
		config_hwmon ina3221 samples 512
		# Set update interval to 430ms
		# (2 * 140us convertion time * 3 channels * 512)
		config_hwmon ina3221 update_interval 430
		if [[ "${machine}" == p3701-0000* || "${machine}" == p3701-0005* ]]; then
			# Below power budget is applicable for real platform as well as
			# emulation platforms such as p3701-0000-as-p3767-0000.
			config_hwmon ina3221 curr4_crit 3380 VDD_GPU_SOC 65000 in1_input
		elif [ "${machine}" = "p3701-0004" ]; then
			config_hwmon ina3221 curr4_crit 3380 VDD_GPU_SOC 45000 in1_input
		elif [ "${machine}" = "p3701-0008" ] || [ "${machine}" = "p3701-0008-safety" ]; then
			config_hwmon ina3221 curr4_crit 4180 VDD_GPU_SOC 80000 in1_input
		elif [ "${machine}" = "p3767-0000-px1" ]; then
			config_hwmon ina3221 curr1_max 1578 VDD_IN 30000 in1_input
			config_hwmon ina3221 curr1_crit 1842 VDD_IN 35000 in1_input
		elif [ "${machine}" = "p3767-0000" ] || [ "${machine}" = "p3767-0002" ]; then
			config_hwmon ina3221 curr1_max 1315 VDD_IN 25000 in1_input
			config_hwmon ina3221 curr1_crit 1578 VDD_IN 30000 in1_input
		elif [ "${machine}" = "p3767-0000-super" ]; then
			config_hwmon ina3221 curr1_max 2105 VDD_IN 40000 in1_input
			config_hwmon ina3221 curr1_crit 2105 VDD_IN 40000 in1_input
		elif [ "${machine}" = "p3767-0001" ]; then
			config_hwmon ina3221 curr1_max 1052 VDD_IN 20000 in1_input
			config_hwmon ina3221 curr1_crit 1315 VDD_IN 25000 in1_input
		elif [ "${machine}" = "p3767-0001-super" ]; then
			config_hwmon ina3221 curr1_max 2105 VDD_IN 40000 in1_input
			config_hwmon ina3221 curr1_crit 2105 VDD_IN 40000 in1_input
		elif [ "${machine}" = "p3767-0003" ] || [ "${machine}" = "p3767-0005" ]; then
			config_hwmon ina3221 curr1_max 3000 VDD_IN 15000 in1_input
			config_hwmon ina3221 curr1_crit 4000 VDD_IN 20000 in1_input
		elif [ "${machine}" = "p3767-0003-super" ] || \
			[ "${machine}" = "p3767-0005-super" ]; then
			config_hwmon ina3221 curr1_max 5000 VDD_IN 25000 in1_input
			config_hwmon ina3221 curr1_crit 5000 VDD_IN 25000 in1_input
		elif [ "${machine}" = "p3767-0004" ]; then
			config_hwmon ina3221 curr1_max 2000 VDD_IN 10000 in1_input
			config_hwmon ina3221 curr1_crit 3000 VDD_IN 15000 in1_input
		elif [ "${machine}" = "p3767-0004-super" ]; then
			config_hwmon ina3221 curr1_max 5000 VDD_IN 25000 in1_input
			config_hwmon ina3221 curr1_crit 5000 VDD_IN 25000 in1_input
		fi
		;;
	*)
		# no need to set
		;;
	esac
}

function setup_actmon()
{
	debugfs_actmon="/sys/kernel/debug/tegra-host1x/actmon"

	# Actmon settings should be the same across engines
	tee ${debugfs_actmon}/*/sample_period 1>/dev/null <<< 1500
	tee ${debugfs_actmon}/*/module0/consec_upper_num 1>/dev/null <<< 7
	tee ${debugfs_actmon}/*/module0/consec_lower_num 1>/dev/null <<< 7
	tee ${debugfs_actmon}/*/module0/k 1>/dev/null <<< 2
}

SOCFAMILY=""

set_socfamily
set_safety_flag
set_power_state_perm
create_nvpmodel_symlink
create_nvfancontrol_symlink
cpu_hotplug
set_cpufreq_governor
set_cpu_floor_freq
set_devfreq_governor
lock_se_frequency
validate_nvpmodel_conf
setup_hwmon
setup_actmon
