blob: 91862e8cae71a2255cf3606d843a00867570269f [file] [log] [blame] [edit]
#!/bin/bash
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Quick hack to monitor thermals on Exynos based platforms. Since we only have
# passive cooling, the only thing we can do is limit CPU temp.
#
# TODO: validate readings from hwmon sensors by comparing to each other.
# We should ignore readings with more than 10C differences from peers.
PROG=`basename $0`
PLATFORM=`mosys platform name`
let debug=0
if [[ "$1x" = '-dx' ]] ; then
let debug=1
fi
# if PLATFORM is empty, try again
for i in $(seq 5) ; do
if [[ "${PLATFORM}" != "" ]] ; then
break;
fi
sleep 1
logger -t "${PROG}" "Unable to get platform name, retry ${i} of 5"
PLATFORM=`mosys platform name`
done
# Log the platform
logger -t "${PROG}" "Platform ${PLATFORM}"
# cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
# 1700000 1600000 1500000 ...
declare -a EXYNOS5_CPU_FREQ=(1700000 1600000 1500000 1400000 1300000 \
1200000 1100000 1000000 900000 800000 700000 600000 500000 400000 \
300000 200000)
cpu_tpath="/sys/class/thermal/thermal_zone0"
# TODO(crosbug.com/p/17658) HACK: remove once characterized
if [[ "${PLATFORM}" = "Spring" ]] ; then
t0=$(( `cat ${cpu_tpath}/trip_point_0_temp` / 1000 - 1 ))
t1=$(( `cat ${cpu_tpath}/trip_point_1_temp` / 1000 - 1 ))
declare -a CPU_TEMP_MAP=($t0 $t0 $t0 $t0 $t0 $t0 $t0 $t0 $t0 $t0 $t1)
declare -a HWMON_TEMP_MAP=($t0 $t0 $t0 $t0 $t0 $t0 $t0 $t0 $t0 $t1)
else
# CPU temperature threshold we start limiting CPU Freq
# 63 -> 1.4Ghz, 69 -> 1.1 Ghz, 75 -> 800Mhz
declare -a CPU_TEMP_MAP=(60 61 62 63 65 67 68 69 71 73 75)
# 52 -> 1.4Ghz, 60->1.1Ghz, 65->800Mhz
declare -a HWMON_TEMP_MAP=(49 50 51 52 55 58 60 62 64 65)
fi
declare -a DAISY_CPU_TEMP=("${cpu_tpath}/temp")
#######################################
# Find all hwmon thermal sensors.
#
# It's OK if there are none.
#
# Globals:
# HWMON_TEMP_SENSOR
# Arguments:
# None
# Returns:
# None
#######################################
find_hwmon_sensors() {
local index=0
local hwmon_dir
local sensor
for hwmon_dir in /sys/class/hwmon/hwmon*/device; do
for sensor in ${hwmon_dir}/temp*_input; do
if [[ -r ${sensor} ]] ; then
HWMON_TEMP_SENSOR[${index}]=${sensor}
index=$((${index} + 1))
fi
done
done
}
read_temp() {
sensor="$1"
if [[ -r $sensor ]] ; then
# treat $1 as numeric and convert to C
local raw
raw=`cat $sensor 2> /dev/null`
if [[ -z "$raw" ]] ; then
return 1
fi
let t=$raw/1000
# valid CPU range is 25 to 125C. Give hwmon sensors more range.
if [[ $t -lt 15 || $t -gt 140 ]] ; then
# do nothing - ignore the reading
logger -t "$PROG" "ERROR: temp $t out of range"
return 1
fi
return $t
fi
logger -t "$PROG" "ERROR: could not read temp from $sensor"
# sleep so script isn't respawned so quickly and spew
sleep 10
exit 1
}
lookup_freq_idx() {
let t=$1
shift
declare -a temp_map=(${*})
let i=0
let n=${#temp_map[@]}
while [[ $i -lt $n ]]
do
if [[ $t -le ${temp_map[i]} ]] ; then
return $i
fi
let i+=1
done
# we ran off the end of the map. Use slowest speed in that map.
logger -t "$PROG" "ERROR: temp $t not in temp_map"
let i=$n-1
return $i
}
# Thermal loop steps
set_max_cpu_freq() {
max_freq=$1
for cpu in /sys/devices/system/cpu/cpu?/cpufreq; do
echo $max_freq > $cpu/scaling_max_freq
done
}
# Only update cpu Freq if we need to change.
let last_cpu_freq=0
find_hwmon_sensors
# Get frequency throttling set by the firmware to limit power draw
let power_cap=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq`
logger -t "$PROG" "CPU max freq set to $((${power_cap} / 1000)) Mhz at boot"
# Print power info on first pass, then every other 4 passes.
power_info_pass=4
while true; do
max_cpu_freq=${EXYNOS5_CPU_FREQ[0]}
# read the list of temp sensors
for sensor in ${DAISY_CPU_TEMP[*]}
do
read_temp $sensor
let cpu_temp=$?
lookup_freq_idx $cpu_temp "${CPU_TEMP_MAP[@]}"
let f=$?
let cpu_freq=${EXYNOS5_CPU_FREQ[f]}
if [[ $cpu_freq -gt 0 && $cpu_freq -lt $max_cpu_freq ]] ; then
max_cpu_freq=$cpu_freq
fi
done
declare -a temps
if [[ ${#HWMON_TEMP_SENSOR[@]} -gt 0 ]]; then
let j=0
for sensor in ${HWMON_TEMP_SENSOR[*]}
do
read_temp $sensor
let temp=$?
# record temps for (DEBUG and) validation later
temps[$j]=$temp
let j+=1
done
# TODO validate hwmon sensor readings.
# we should reject anything that is more than 5C off from all others.
let max_temp=${temps[0]}
for k in `seq 1 $j`
do
if [[ $max_temp -lt ${temps[k]} ]] ; then
let max_temp=${temps[k]}
fi
done
lookup_freq_idx $max_temp "${HWMON_TEMP_MAP[@]}"
let f=$?
let therm_cpu_freq=${EXYNOS5_CPU_FREQ[f]}
# we have a valid reading and it's lower than others
if [[ $therm_cpu_freq -gt 0 &&
$therm_cpu_freq -lt $max_cpu_freq ]] ; then
max_cpu_freq=$therm_cpu_freq
fi
fi
# Handle the power cap if the battery is too low.
if [[ "${PLATFORM}" = "Spring" && $power_cap -lt ${EXYNOS5_CPU_FREQ[0]} ]]
then
power_supply="/sys/class/power_supply/sbs-6-000b"
let battery_percent=$((`cat ${power_supply}/energy_now` * 100 / \
`cat ${power_supply}/energy_full`))
# if we have charged, restore the full CPU frequency range
if [[ $battery_percent -gt 5 ]] ; then
power_cap=${EXYNOS5_CPU_FREQ[0]}
logger -t "$PROG" "Freq cap reset to $((${power_cap} / 1000)) Mhz"
fi
# force to stay below the cap set to limit total power draw
if [[ $max_cpu_freq -gt $power_cap ]] ; then
max_cpu_freq=$power_cap
fi
fi
if [[ debug -gt 0 ]] ; then
echo `date +"%H:%M:%S"` , \
`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq`, \
$max_cpu_freq , $cpu_temp, ${temps[@]}
fi
if [[ $last_cpu_freq -ne $max_cpu_freq ]] ; then
let last_cpu_freq=$max_cpu_freq
logger -t "$PROG" "Max CPU Freq set to $max_cpu_freq \
(Celsius: $cpu_temp ${temps[@]})"
set_max_cpu_freq $max_cpu_freq
fi
if [[ "${PLATFORM}" = "Spring" ]] ; then
# Report charger type.
if [[ "$power_info_pass" = "4" ]] ; then
power_info_pass=0
# Charger type is 4-byte hex, but metric_client accepts only
# decimal. Sparse histograms use 32-bit bucket indices, but
# the 64-bit values produced by awk are truncated correctly.
charger_type=$(($(ectool powerinfo | awk \
'/USB Device Type:/ { print $4; }')))
if [[ -n "$charger_type" ]]; then
metrics_client -s Platform.SpringChargerType $charger_type
fi
fi
power_info_pass=$((power_info_pass + 1))
fi
sleep 15
done