#!/bin/sh -eu

MODULE=
CPU=
GOVERNOR_AC_ON=
GOVERNOR_AC_OFF=
EPP_AC_ON=
EPP_AC_OFF=
. shell-error

[ -f /etc/sysconfig/cpufreq-simple ] && . /etc/sysconfig/cpufreq-simple

MODPROBE="modprobe -qb"
SD_BOOTED=sd_booted
RUNLEVEL=runlevel
CHKCONFIG=chkconfig
SYSTEMCTL=systemctl
CPUFREQ=/sys/devices/system/cpu/cpu0/cpufreq

DEFAULT_GOVERNOR_AC_ON=ondemand
DEFAULT_GOVERNOR_AC_OFF=ondemand
# Dirk Brandewie stated that in case of intel_pstate driver
# "powersave roughly equal to ondemand".
# See https://bugzilla.kernel.org/show_bug.cgi?id=73421#c1
# for details.
DEFAULT_INTEL_PSTATE_GOVERNOR_AC_ON=powersave
DEFAULT_INTEL_PSTATE_GOVERNOR_AC_OFF=powersave

DEFAULT_AMD_PSTATE_EPP_GOVERNOR_AC_ON=powersave
DEFAULT_AMD_PSTATE_EPP_GOVERNOR_AC_OFF=powersave

# Default energy performance policy for intel_pstate and amd-pstate-epp drivers
DEFAULT_EPP_AC_ON=balance_performance
DEFAULT_EPP_AC_OFF=balance_power

PATH='/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin'
CPUFREQ=/sys/devices/system/cpu/cpu0/cpufreq
GOVERNORS="$CPUFREQ/scaling_available_governors"
SCALING_DRIVER="$CPUFREQ/scaling_driver"
AVAILABLE_EPP="$CPUFREQ/energy_performance_available_preferences"

is_module_loaded()
{
	[ -f "$CPUFREQ/scaling_governor" ] && [ -f "$CPUFREQ/scaling_available_governors" ]
}

load_module()
{
	# Don't try to load already loaded module
	if is_module_loaded; then
		return 0
	fi

	# try autodetecting if not specified already
	if [ -z "$MODULE" ]; then
		MODULE="$(detect-cpufreq-module)"

		# check whether still none
		if [ -z "$MODULE" ]; then
			echo "No cpufreq module specified or detected" 1>&2
			# don't treat this as error
			return 0
		fi
	fi

	if ! $MODPROBE "$MODULE"; then
		echo "Loading cpufreq module $MODULE failed" 1>&2
		return 1
	fi

	return 0
}

get_ac_state()
{
	local state="off-line"

	# Treat error as on-line state
	if on_ac_power || [ $? -eq 255 ]; then
		state="on-line"
	fi

	echo "$state"
}

get_scaling_driver()
{
	if [ -f "$SCALING_DRIVER" ]; then
		cat "$SCALING_DRIVER"
	fi
}

init_command()
{
	local ac_state="$(get_ac_state)"
	[ -n "$ac_state" ] && echo "ac-$ac_state"
}

set_cpupower()
{
	local cpu_list=

	for i in $CPU; do
		cpu_list="$cpu_list${cpu_list:+,}$i"
	done

	[ -n "$cpu_list" ] || cpu_list="all"

	cpupower --cpu "$cpu_list" "$@"
}

set_governor()
{
	grep -Fqw -- "$1" "$GOVERNORS" || $MODPROBE "cpufreq_$1"

	set_cpupower frequency-set -g "$1"
}

set_epp()
{
	if [ ! -e "$AVAILABLE_EPP" ] || ! grep -Fqw "$1" "$AVAILABLE_EPP"; then
		return 0
	fi

	set_cpupower set -e "$1"
}

status()
{
	[ -e "$GOVERNORS" ] || fatal "system not configured correctly for CPU frequency scaling"

	cpupower frequency-info -d | grep --color=never -o 'driver: .*$'
	echo -n "available governors: "
	cat "$GOVERNORS"
	cpupower frequency-info -o

	[ -e "$AVAILABLE_EPP" ] || return 0

	echo
	echo -n "available EPP: "
	cat "$AVAILABLE_EPP"
	local n= i=
	n="$(nproc --all 2>/dev/null)" ||:
	if [ -n "$n" ]; then
		n="$((n-1))"
	else
		n=0
	fi
	for i in $(seq 0 $n); do
		local epp="/sys/devices/system/cpu/cpu$i/cpufreq/energy_performance_preference"
		[ -e "$epp" ] || continue
		echo -n "EPP CPU$i: "
		cat "$epp"
	done
}

is_service_enabled()
{
	if ! type "$SD_BOOTED" >/dev/null 2>&1 || "$SD_BOOTED"; then
		$SYSTEMCTL is-enabled --quiet cpufreq-simple.service
	else
		type "$RUNLEVEL" >/dev/null 2>&1 || return 1
		type "$CHKCONFIG" >/dev/null 2>&1 || return 1
		local rl="$($RUNLEVEL | cut -c3)"
		[ -n "$rl" ] || return 1
		$CHKCONFIG --level "$rl" cpufreq-simple >/dev/null 2>&1
	fi
}

# getopt is overkill for now
case "${1-}" in
	-c|--check|--check-service)
		is_service_enabled || exit 0
		shift
		;;
	--*)
		fatal "Unknown option $1"
		;;
esac

cmd="${1-}"
[ -n "$cmd" ] || cmd="$(init_command)"

if [ -z "$cmd" ]; then
	fatal "couldn't apply initial settings"
fi

if load_module; then
	# If cpufreq module not detected then exit
	if ! is_module_loaded; then
		exit 0
	fi
else
	exit 1
fi

case "$cmd" in
	ac-on-line)
		if [ -z "$GOVERNOR_AC_ON" ]; then
			case "$(get_scaling_driver)" in
				intel_pstate) GOVERNOR_AC_ON="$DEFAULT_INTEL_PSTATE_GOVERNOR_AC_ON" ;;
				amd-pstate-epp) GOVERNOR_AC_ON="$DEFAULT_AMD_PSTATE_EPP_GOVERNOR_AC_ON" ;;
				*) GOVERNOR_AC_ON="$DEFAULT_GOVERNOR_AC_ON" ;;
			esac
		fi
		set_governor "$GOVERNOR_AC_ON"
		set_epp "${EPP_AC_ON:-$DEFAULT_EPP_AC_ON}"
		;;
	ac-off-line)
		if [ -z "$GOVERNOR_AC_OFF" ]; then
			case "$(get_scaling_driver)" in
				intel_pstate) GOVERNOR_AC_OFF="$DEFAULT_INTEL_PSTATE_GOVERNOR_AC_OFF" ;;
				amd-pstate-epp) GOVERNOR_AC_OFF="$DEFAULT_AMD_PSTATE_EPP_GOVERNOR_AC_OFF" ;;
				*) GOVERNOR_AC_OFF="$DEFAULT_GOVERNOR_AC_OFF" ;;
			esac
		fi
		set_governor "$GOVERNOR_AC_OFF"
		set_epp "${EPP_AC_OFF:-$DEFAULT_EPP_AC_OFF}"
		;;
	status)
		status
		;;
	*)
		fatal "unknown command: '$cmd'"
		;;
esac
