#!/bin/sh
# kernel configuration script for power user kernel
# (c) Copyright 2010 by Thomas Tanner <maemo@tannerlab.com>
# licensed under GPLv3
# version 0.3 (16. May 2010)

if test $# -eq 0; then
    echo "$0 command [options]"
    echo "commands:"
    echo "  show        - show current settings"
    echo "  load <file> - load settings from file"
    echo "  save <file> - save settings to file (abs. path or filename in /home/user/.kernel)"
    echo "  default [<file>] - set file or the current settings as default settings"
    echo "  limits min max - set the frequency limits ('-' keeps the current setting)"
    echo "  lock freq [volt] [dsp] - lock CPU at frequency with specific settings"
    echo "  unlock       - unlock CPU frequency"
    echo "filenames: absolute path or filename in search path or none=defaults"
    echo "search path: .,/home/user/.kernel,/usr/share/kernel-power-settings/"
    exit 1
fi
if test "`id -u`" -ne 0; then
    sudo $0 $* # run as root
    exit $?
fi

cfr=/sys/devices/system/cpu/cpu0/cpufreq
cfd=$cfr/ondemand
cfc=$cfr/conservative
pwr=/sys/power

if test -f $pwr/vdd1_opps_vsel -a -f $pwr/dsp_opps_rate; then :
else
    echo This kernel version `uname -r` is not supported
    echo Please make sure that kernel-power is installed and running.
    echo If you have just installed the kernel, you need to shutdown and boot to activate it.
    echo To reinstall the power user kernel run:
    echo \'apt-get install --reinstall kernel-power kernel-power-flasher\'
    exit 1
fi

allfreq=
for f in `cat $cfr/scaling_available_frequencies`; do
    allfreq="$f $allfreq"
done
vsel=`cat $pwr/vdd1_opps_vsel` 
rate=`cat $pwr/dsp_opps_rate` 

cmd=$1
test "$cmd" = setdefault && cmd=default # old name
case " show load save default limits lock unlock " in
*" $cmd "*) ;;
*)  echo "invalid command $cmd";  exit 1 ;;
esac
arg=
shift
if test "$cmd" = unlock; then
    echo 'unlocking frequency. reset to defaults'
    cmd=loaddef
fi
if test "$cmd" = default && test $# -eq 0; then
    echo 'saving current settings as defaults'
    arg=/etc/default/kernel-power
    cmd=savedef
fi

popvsel() { curvsel=$1; shift; vsel="$*"; }
poprate() { currate=$1; shift; rate="$*"; }

case $cmd in
show|save|savedef)
    minfreq=$((`cat $cfr/scaling_min_freq`/1000))
    maxfreq=$((`cat $cfr/scaling_max_freq`/1000))
    test $maxfreq = 599 && maxfreq=600
    gov=`cat $cfr/scaling_governor`
    avoid=
    test $gov = ondemand && avoid=`cat $cfd/avoid_frequencies`
    freqs=
    for f in $allfreq; do
        popvsel $vsel
        poprate $rate
        case " $avoid " in *" $f "*) ;;
        *) freqs="$freqs$((f/1000)):$curvsel,$currate " ;;
        esac
    done
    if test $cmd = show; then
        echo "current kernel configuration:"
        echo "current frequency:" $((`cat $cfr/scaling_cur_freq`/1000))
        echo -n "supported frequencies: "
        for f in $allfreq; do echo -n "$((f/1000)) "; done
        echo
        echo "min. frequency:" $minfreq
        echo "max. frequency:" $maxfreq
        echo -n "avoid frequencies: "
        for f in $avoid; do echo -n "$((f/1000)) "; done
        echo
        echo "active frequencies: $freqs"
        echo "SmartReflex" "VDD1=`cat $pwr/sr_vdd1_autocomp`, VDD2=`cat $pwr/sr_vdd2_autocomp`"
        echo -n "governor $gov:"
        if test $gov = ondemand; then
            echo -n " ignore nice load=" `cat $cfd/ignore_nice_load` 
	    echo -n ", up threshold=" `cat $cfd/up_threshold`
    	    echo -n ", sampling rate=" `cat $cfd/sampling_rate`
    	    echo ", powersave bias=" `cat $cfd/powersave_bias`
    	else
    	    echo
    	fi
    else
        test $cmd = save && arg=$1
	if test -z "$arg"; then
	    echo "filename argument is mssing"
	    exit 1
	fi
        if test `basename $arg` = $arg; then
	    u=/home/user/.kernel
	    arg=$u/$arg
	    if test ! -d $u; then
		mkdir -p $u
		chown user.users $u
	    fi
	    touch $arg
	    chown user.users $arg
	fi
        rm -f $arg
        echo "saving to $arg"
        echo "# kernel configuration file generated by $0" > $arg
        echo "MINFREQ=$minfreq" >> $arg
        echo "MAXFREQ=$maxfreq" >> $arg
        echo "FREQS=\"$freqs\"" >> $arg
        echo "SMARTREFLEX_VDD1=`cat $pwr/sr_vdd1_autocomp`" >> $arg
        echo "SMARTREFLEX_VDD2=`cat $pwr/sr_vdd2_autocomp`" >> $arg
        echo "GOVERNOR=$gov" >> $arg
        if test $gov = ondemand; then
            echo "IGNORE_NICE_LOAD=`cat $cfd/ignore_nice_load`" >> $arg
            echo "UP_THRESHOLD=`cat $cfd/up_threshold`" >> $arg
	    echo "SAMPLING_RATE=`cat $cfd/sampling_rate`" >> $arg
            echo "POWERSAVE_BIAS=`cat $cfd/powersave_bias`" >> $arg
        fi
    fi
    ;;
load|loaddef)
    test $cmd = loaddef || arg=$1
    if test -z "$arg"; then
        arg=/etc/default/kernel-power
        test -f $arg || arg=/usr/share/kernel-power-settings/default
    fi
    if test `basename $arg` = $arg; then # not absolute
        for p in . /home/user/.kernel /usr/share/kernel-power-settings; do
            test -f $p/$arg && arg=$p/$arg && break
        done
    fi
    if test ! -f "$arg"; then
        echo "file not found $arg"
        exit 1
    fi
    echo "loading $arg"
    source $arg
    if test -n "$VDD1_OPPS_VSEL" -o -n "$DSP_OPPS_RATE"; then
        echo "warning: old configuration format detected. please save in the new format."
        if test -n "$MAX_FREQ"; then
            test $MAX_FREQ = 600000 && MAX_FREQ=599000
            echo $MAX_FREQ > $cfr/scaling_max_freq
        fi
        test -n "$MIN_FREQ" && echo $MIN_FREQ > $cfr/scaling_min_freq
        test -n "$MAX_FREQ" && echo $MAX_FREQ > $cfr/scaling_max_freq
        test -n "$VDD1_OPPS_VSEL" && echo $VDD1_OPPS_VSEL > $pwr/vdd1_opps_vsel
        test -n "$DSP_OPPS_RATE" && echo $DSP_OPPS_RATE > $pwr/dsp_opps_rate
        test -n "$SMARTREFLEX_VDD1" && echo $SMARTREFLEX_VDD1 > $pwr/sr_vdd1_autocomp
        test -n "$SMARTREFLEX_VDD2" && echo $SMARTREFLEX_VDD2 > $pwr/sr_vdd2_autocomp
        test -n "$IGNORE_NICE_LOAD" && echo $IGNORE_NICE_LOAD > $cfd/ignore_nice_load
        test -n "$UP_THRESHOLD" && echo $UP_THRESHOLD > $cfd/up_threshold
        test -n "$SAMPLING_RATE" && echo $SAMPLING_RATE > $cfd/sampling_rate
        exit 1
    fi
    active=
    minfreq=
    maxfreq=
    for f in $FREQS; do
        freq=`echo $f | cut -d: -f1`
        f=`echo $f | cut -d: -f2`
        volt=`echo $f | cut -d, -f1`
        dsp=`echo $f | cut -d, -f2`
        if test ! $freq = 0; then
            test -z "$minfreq" -o "$freq" -lt "$minfreq" && minfreq=$freq
            test -z "$maxfreq" -o "$freq" -gt "$maxfreq" && maxfreq=$freq
            freq=$((freq*1000))
            if test "$freq" = "750000"; then
                echo warning: $freq was changed to 720000;
                freq=720000
            fi
            case " $allfreq " in *" $freq "*) ;;
            *)  echo warning: $freq not supported; continue ;;
            esac
            active="$active $freq"
        fi
        test -z "$volt" && test -z "$dsp" && continue
        tvsel=
        trate=
        for fr in $allfreq; do
            if test $fr = $freq; then
                popvsel $vsel
                poprate $rate
                test -z "$volt" && volt=$curvsel
                test -z "$dsp" && dsp=$currate
                tvsel="$tvsel $volt $vsel"
                trate="$trate $dsp $rate"
                break
            fi
            popvsel $vsel
            tvsel="$tvsel $curvsel"
            poprate $rate
            trate="$trate $currate"
        done
        vsel=$tvsel
        rate=$trate
    done
    test -z "$GOVERNOR" && GOVERNOR=ondemand
    test -n "$MINFREQ" && minfreq=$MINFREQ
    test -n "$MAXFREQ" && maxfreq=$MAXFREQ
    test "$minfreq" -gt 100000 && minfreq=$((minfreq/1000))
    test "$maxfreq" -gt 100000 && maxfreq=$((maxfreq/1000))
    avoid=
    for f in $allfreq; do
        if test "$f" -lt $((minfreq*1000)); then
            avoid="$avoid $f"
            continue
        fi
        case " $active " in *" $f "*) ;;
        *) avoid="$avoid $f" ;;
        esac
    done
    echo $GOVERNOR > $cfr/scaling_governor
    if test $GOVERNOR = ondemand; then
	echo $avoid > $cfd/avoid_frequencies
	test -n "$IGNORE_NICE_LOAD" && echo $IGNORE_NICE_LOAD > $cfd/ignore_nice_load
        test -n "$UP_THRESHOLD" && echo $UP_THRESHOLD > $cfd/up_threshold
	test -n "$SAMPLING_RATE" && echo $SAMPLING_RATE > $cfd/sampling_rate
        test -n "$POWERSAVE_BIAS" && echo $POWERSAVE_BIAS > $cfd/powersave_bias
    fi
    echo $vsel > $pwr/vdd1_opps_vsel
    echo $rate > $pwr/dsp_opps_rate
    test $maxfreq = 600 && maxfreq=599
    echo $((maxfreq*1000)) > $cfr/scaling_max_freq
    echo $((minfreq*1000)) > $cfr/scaling_min_freq
    echo $((maxfreq*1000)) > $cfr/scaling_max_freq
    test -n "$SMARTREFLEX_VDD1" && echo $SMARTREFLEX_VDD1 > $pwr/sr_vdd1_autocomp
    test -n "$SMARTREFLEX_VDD2" && echo $SMARTREFLEX_VDD2 > $pwr/sr_vdd2_autocomp
    echo "successfully loaded."
    ;;
default)
    arg=$1
    if test -z "$arg"; then
        echo "filename missing"
        exit 1
    fi
    if test `basename $arg` = $arg; then # not absolute
        for p in . /home/user/.kernel /usr/share/kernel-power-settings; do
            test -f $p/$arg && arg=$p/$arg && break
        done
    fi
    def=/etc/default/kernel-power
    rm -f $def
    case $arg in 
    /usr/share/kernel-power-settings/*) ln -s $arg $def ;;
    *) cp $arg $def ;;
    esac
    echo "defaults set to $arg"
    ;;
limits)
    minfreq=$1
    maxfreq=$2
    if test "x$minfreq" = x -o "x$maxfreq" = x; then
        echo "frequency limit arguments missing"
        exit 1
    fi
    test "x$minfreq" = "x-" && minfreq=$((`cat $cfr/scaling_min_freq`/1000))
    test "x$maxfreq" = "x-" && maxfreq=$((`cat $cfr/scaling_max_freq`/1000))
    limits="$minfreq,$maxfreq"
    minfreq=$((minfreq*1000))
    maxfreq=$((maxfreq*1000))
    case " $allfreq " in *" $minfreq "*) ;;
    *) echo "invalid lower limit $1"; exit 1; ;;
    esac
    case " $allfreq " in *" $maxfreq "*) ;;
    *) echo "invalid upper limit $2"; exit 1; ;;
    esac
    if test $minfreq -gt $maxfreq; then
	echo "minimum must not be greater than maximum: $limits"
	exit 1
    fi
    test $maxfreq = 600000 && maxfreq=599000
    echo $maxfreq > $cfr/scaling_max_freq
    echo $minfreq > $cfr/scaling_min_freq
    echo $maxfreq > $cfr/scaling_max_freq
    echo "the limits were set to [$limits]"
    ;;
lock)
    arg=$1
    altfreq=
    for f in $allfreq; do
        test "$f" = "$arg" && continue
        altfreq=$f
        break
    done
    vsel=
    dsp=
    freq=$((arg*1000))
    ifreq=
    i=1
    avoid=
    for f in $allfreq; do
        i=$((i+1))
        if test $f = $freq; then
            ifreq=$i
            break
        fi
        avoid="$avoid $f"
    done
    if test -z "ifreq"; then
        echo invalid frequency $freq
        exit 1
    fi
    echo userspace > $cfr/scaling_governor 
    echo $altfreq > $cfr/scaling_setspeed # temporarily set alternative frequencies
    volt=$2
    if test -n "$volt"; then
        i=0
        vsel=
        for v in `cat $pwr/vdd1_opps_vsel`; do
            i=$((i+1))
            test $i = $ifreq && v=$volt
            vsel="$vsel $v"
        done
        echo $vsel > $pwr/vdd1_opps_vsel
    fi
    dsp=$3
    if test -n "$dsp"; then
        i=0
        dsp=
        for r in `cat $pwr/dsp_opps_rate`; do
            i=$((i+1))
            test $i = $ifreq && r=$dsp
            dsp="$dsp $r"
        done
        echo $dsp > $pwr/dsp_opps_rate
    fi
    echo locking frequency $freq
    echo ondemand > $cfr/scaling_governor 
    echo $avoid > $cfd/avoid_frequencies # avoid all other freqs
    maxfreq=$freq
    test $maxfreq = 600000 && maxfreq=599000
    # min after max? kernel docs are wrong?
    echo $maxfreq > $cfr/scaling_max_freq
    echo $freq > $cfr/scaling_min_freq
    echo $maxfreq > $cfr/scaling_max_freq
    ;;
esac
exit 0
