Merge pull request #43 from system76/wip/fan

Fan curve improvements
This commit is contained in:
Jeremy Soller 2020-04-04 13:00:12 -06:00 committed by GitHub
commit 9ee71097f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 415 additions and 46 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/build/
backup.rom
power.csv

63
power.sh Executable file
View File

@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -e
./ectool.sh info
header=1
if [ -e power.csv ]
then
header=0
fi
while true
do
if [ "${header}" == "1" ]
then
F="Time "
F="${F}\tBAT W"
F="${F}\tCPU W"
F="${F}\tCPU C"
F="${F}\tFAN %"
else
F="$(date "+%T")"
uV="$(cat /sys/class/power_supply/BAT0/voltage_now)"
V="$(echo "${uV}/1000000" | bc -lq)"
uA="$(cat /sys/class/power_supply/BAT0/current_now)"
A="$(echo "${uA}/1000000" | bc -lq)"
bat_W="$(echo "${V} * ${A}" | bc -lq)"
F="${F}\t$(printf "%.2f" "${bat_W}")"
last_E="$(cat /sys/class/powercap/intel-rapl\:0/energy_uj)"
sleep 1
next_E="$(cat /sys/class/powercap/intel-rapl\:0/energy_uj)"
W="$(echo "(${next_E} - ${last_E})/1000000" | bc -lq)"
F="${F}\t$(printf "%.1f" "${W}")"
T="$(cat /sys/devices/platform/coretemp.0/hwmon/hwmon*/temp1_input)"
C="$(echo "${T}/1000" | bc -lq)"
F="${F}\t$(printf "%.1f" "${C}")"
D="$(sudo tool/target/release/system76_ectool fan 0)"
P="$(echo "(${D} * 100)/255" | bc -lq)"
F="${F}\t$(printf "%.0f" "${P}")"
fi
for file in /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
do
if [ "${header}" == "1" ]
then
id="$(basename "$(dirname "$(dirname "${file}")")")"
F="${F}\t${id}"
else
KHz="$(cat "${file}")"
MHz="$(echo "${KHz}/1000" | bc -lq)"
F="${F}\t$(printf "%.0f" "${MHz}")"
fi
done
echo -e "${F}" | tee -a power.csv
header=0
done

View File

@ -7,6 +7,12 @@
#include <ec/gpio.h>
#include <ec/pwm.h>
// Fan speed is the lowest requested over HEATUP seconds
#define HEATUP 5
// Fan speed is the highest HEATUP speed over COOLDOWN seconds
#define COOLDOWN 10
// Tjunction = 100C for i7-8565U (and probably the same for all WHL-U)
#define T_JUNCTION 100
@ -30,7 +36,7 @@ struct FanPoint __code FAN_POINTS[] = {
FAN_POINT(70, 45),
FAN_POINT(75, 55),
FAN_POINT(80, 75),
FAN_POINT(84, 100),
FAN_POINT(84, 100)
};
// Get duty cycle based on temperature, adapted from
@ -51,12 +57,14 @@ uint8_t fan_duty(int16_t temp) {
// If in between current temp and previous temp, interpolate
if (temp > prev->temp) {
int16_t dtemp = (cur->temp - prev->temp);
int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty);
return (uint8_t)(
((int16_t)prev->duty) +
((temp - prev->temp) * dduty) / dtemp
);
return prev->duty;
// int16_t dtemp = (cur->temp - prev->temp);
// int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty);
// return (uint8_t)(
// ((int16_t)prev->duty) +
// ((temp - prev->temp) * dduty) / dtemp
// );
}
}
}
@ -66,6 +74,40 @@ uint8_t fan_duty(int16_t temp) {
return PWM_DUTY(100);
}
uint8_t fan_heatup(uint8_t duty) {
static uint8_t history[HEATUP] = { 0 };
uint8_t lowest = duty;
int i;
for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) {
uint8_t value = history[i + 1];
if (value < lowest) {
lowest = value;
}
history[i] = value;
}
history[i] = duty;
return lowest;
}
uint8_t fan_cooldown(uint8_t duty) {
static uint8_t history[COOLDOWN] = { 0 };
uint8_t highest = duty;
int i;
for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) {
uint8_t value = history[i + 1];
if (value > highest) {
highest = value;
}
history[i] = value;
}
history[i] = duty;
return highest;
}
void peci_init(void) {
// Allow PECI pin to be used
GCR2 |= (1 << 4);
@ -123,8 +165,10 @@ void peci_event(void) {
peci_duty = PWM_DUTY(0);
}
if (peci_duty != DCR2) {
DCR2 = peci_duty;
DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, peci_duty);
uint8_t heatup_duty = fan_heatup(peci_duty);
uint8_t cooldown_duty = fan_cooldown(heatup_duty);
if (cooldown_duty != DCR2) {
DCR2 = cooldown_duty;
DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, cooldown_duty);
}
}

View File

@ -386,11 +386,10 @@ void power_event(void) {
// state is S3
static bool ack_last = false;
bool ack_new = gpio_get(&SUSWARN_N);
#if LEVEL >= LEVEL_DEBUG
if (ack_new && !ack_last) {
DEBUG("%02X: SUSPWRDNACK asserted\n", main_cycle);
}
#if LEVEL >= LEVEL_DEBUG
else if (!ack_new && ack_last) {
} else if (!ack_new && ack_last) {
DEBUG("%02X: SUSPWRDNACK de-asserted\n", main_cycle);
}
#endif

View File

@ -9,6 +9,7 @@
#include <common/command.h>
#include <common/macro.h>
#include <common/version.h>
#include <ec/pwm.h>
// Shared memory host semaphore
volatile uint8_t __xdata __at(0x1022) SMHSR;
@ -143,6 +144,30 @@ static enum Result cmd_reset(void) {
return RES_ERR;
}
static enum Result cmd_fan_get(void) {
// If setting fan 0
if (smfi_cmd[2] == 0) {
// Get duty of fan 0
smfi_cmd[3] = DCR2;
return RES_OK;
}
// Failed if fan not found
return RES_ERR;
}
static enum Result cmd_fan_set(void) {
// If setting fan 0
if (smfi_cmd[2] == 0) {
// Set duty cycle of fan 0
DCR2 = smfi_cmd[3];
return RES_OK;
}
// Failed if fan not found
return RES_ERR;
}
void smfi_event(void) {
if (smfi_cmd[0]) {
switch (smfi_cmd[0]) {
@ -174,6 +199,14 @@ void smfi_event(void) {
case CMD_RESET:
smfi_cmd[1] = cmd_reset();
break;
#ifndef __SCRATCH__
case CMD_FAN_GET:
smfi_cmd[1] = cmd_fan_get();
break;
case CMD_FAN_SET:
smfi_cmd[1] = cmd_fan_set();
break;
#endif // __SCRATCH__
default:
// Command not found
smfi_cmd[1] = RES_ERR;

View File

@ -7,6 +7,12 @@
#include <ec/gpio.h>
#include <ec/pwm.h>
// Fan speed is the lowest requested over HEATUP seconds
#define HEATUP 5
// Fan speed is the highest HEATUP speed over COOLDOWN seconds
#define COOLDOWN 10
// Tjunction = 100C for i7-8565U (and probably the same for all WHL-U)
#define T_JUNCTION 100
@ -30,7 +36,7 @@ struct FanPoint __code FAN_POINTS[] = {
FAN_POINT(70, 45),
FAN_POINT(75, 55),
FAN_POINT(80, 75),
FAN_POINT(84, 100),
FAN_POINT(84, 100)
};
// Get duty cycle based on temperature, adapted from
@ -51,12 +57,14 @@ uint8_t fan_duty(int16_t temp) {
// If in between current temp and previous temp, interpolate
if (temp > prev->temp) {
int16_t dtemp = (cur->temp - prev->temp);
int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty);
return (uint8_t)(
((int16_t)prev->duty) +
((temp - prev->temp) * dduty) / dtemp
);
return prev->duty;
// int16_t dtemp = (cur->temp - prev->temp);
// int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty);
// return (uint8_t)(
// ((int16_t)prev->duty) +
// ((temp - prev->temp) * dduty) / dtemp
// );
}
}
}
@ -66,6 +74,40 @@ uint8_t fan_duty(int16_t temp) {
return PWM_DUTY(100);
}
uint8_t fan_heatup(uint8_t duty) {
static uint8_t history[HEATUP] = { 0 };
uint8_t lowest = duty;
int i;
for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) {
uint8_t value = history[i + 1];
if (value < lowest) {
lowest = value;
}
history[i] = value;
}
history[i] = duty;
return lowest;
}
uint8_t fan_cooldown(uint8_t duty) {
static uint8_t history[COOLDOWN] = { 0 };
uint8_t highest = duty;
int i;
for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) {
uint8_t value = history[i + 1];
if (value > highest) {
highest = value;
}
history[i] = value;
}
history[i] = duty;
return highest;
}
void peci_init(void) {
// Allow PECI pin to be used
GCR2 |= (1 << 4);
@ -123,8 +165,10 @@ void peci_event(void) {
peci_duty = PWM_DUTY(0);
}
if (peci_duty != DCR2) {
DCR2 = peci_duty;
DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, peci_duty);
uint8_t heatup_duty = fan_heatup(peci_duty);
uint8_t cooldown_duty = fan_cooldown(heatup_duty);
if (cooldown_duty != DCR2) {
DCR2 = cooldown_duty;
DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, cooldown_duty);
}
}

View File

@ -383,11 +383,10 @@ void power_event(void) {
// state is S3
static bool ack_last = false;
bool ack_new = gpio_get(&SUSWARN_N);
#if LEVEL >= LEVEL_DEBUG
if (ack_new && !ack_last) {
DEBUG("%02X: SUSPWRDNACK asserted\n", main_cycle);
}
#if LEVEL >= LEVEL_DEBUG
else if (!ack_new && ack_last) {
} else if (!ack_new && ack_last) {
DEBUG("%02X: SUSPWRDNACK de-asserted\n", main_cycle);
}
#endif

View File

@ -9,6 +9,7 @@
#include <common/command.h>
#include <common/macro.h>
#include <common/version.h>
#include <ec/pwm.h>
// Shared memory host semaphore
volatile uint8_t __xdata __at(0x1022) SMHSR;
@ -143,6 +144,30 @@ static enum Result cmd_reset(void) {
return RES_ERR;
}
static enum Result cmd_fan_get(void) {
// If setting fan 0
if (smfi_cmd[2] == 0) {
// Get duty of fan 0
smfi_cmd[3] = DCR2;
return RES_OK;
}
// Failed if fan not found
return RES_ERR;
}
static enum Result cmd_fan_set(void) {
// If setting fan 0
if (smfi_cmd[2] == 0) {
// Set duty cycle of fan 0
DCR2 = smfi_cmd[3];
return RES_OK;
}
// Failed if fan not found
return RES_ERR;
}
void smfi_event(void) {
if (smfi_cmd[0]) {
switch (smfi_cmd[0]) {
@ -174,6 +199,14 @@ void smfi_event(void) {
case CMD_RESET:
smfi_cmd[1] = cmd_reset();
break;
#ifndef __SCRATCH__
case CMD_FAN_GET:
smfi_cmd[1] = cmd_fan_get();
break;
case CMD_FAN_SET:
smfi_cmd[1] = cmd_fan_set();
break;
#endif // __SCRATCH__
default:
// Command not found
smfi_cmd[1] = RES_ERR;

View File

@ -7,6 +7,12 @@
#include <ec/gpio.h>
#include <ec/pwm.h>
// Fan speed is the lowest requested over HEATUP seconds
#define HEATUP 5
// Fan speed is the highest HEATUP speed over COOLDOWN seconds
#define COOLDOWN 10
// Tjunction = 100C for i7-8565U (and probably the same for all WHL-U)
#define T_JUNCTION 100
@ -30,7 +36,7 @@ struct FanPoint __code FAN_POINTS[] = {
FAN_POINT(70, 45),
FAN_POINT(75, 55),
FAN_POINT(80, 75),
FAN_POINT(84, 100),
FAN_POINT(84, 100)
};
// Get duty cycle based on temperature, adapted from
@ -51,12 +57,14 @@ uint8_t fan_duty(int16_t temp) {
// If in between current temp and previous temp, interpolate
if (temp > prev->temp) {
int16_t dtemp = (cur->temp - prev->temp);
int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty);
return (uint8_t)(
((int16_t)prev->duty) +
((temp - prev->temp) * dduty) / dtemp
);
return prev->duty;
// int16_t dtemp = (cur->temp - prev->temp);
// int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty);
// return (uint8_t)(
// ((int16_t)prev->duty) +
// ((temp - prev->temp) * dduty) / dtemp
// );
}
}
}
@ -66,6 +74,40 @@ uint8_t fan_duty(int16_t temp) {
return PWM_DUTY(100);
}
uint8_t fan_heatup(uint8_t duty) {
static uint8_t history[HEATUP] = { 0 };
uint8_t lowest = duty;
int i;
for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) {
uint8_t value = history[i + 1];
if (value < lowest) {
lowest = value;
}
history[i] = value;
}
history[i] = duty;
return lowest;
}
uint8_t fan_cooldown(uint8_t duty) {
static uint8_t history[COOLDOWN] = { 0 };
uint8_t highest = duty;
int i;
for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) {
uint8_t value = history[i + 1];
if (value > highest) {
highest = value;
}
history[i] = value;
}
history[i] = duty;
return highest;
}
void peci_init(void) {
// Allow PECI pin to be used
GCR2 |= (1 << 4);
@ -123,8 +165,10 @@ void peci_event(void) {
peci_duty = PWM_DUTY(0);
}
if (peci_duty != DCR2) {
DCR2 = peci_duty;
DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, peci_duty);
uint8_t heatup_duty = fan_heatup(peci_duty);
uint8_t cooldown_duty = fan_cooldown(heatup_duty);
if (cooldown_duty != DCR2) {
DCR2 = cooldown_duty;
DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, cooldown_duty);
}
}

View File

@ -383,11 +383,10 @@ void power_event(void) {
// state is S3
static bool ack_last = false;
bool ack_new = gpio_get(&SUSWARN_N);
#if LEVEL >= LEVEL_DEBUG
if (ack_new && !ack_last) {
DEBUG("%02X: SUSPWRDNACK asserted\n", main_cycle);
}
#if LEVEL >= LEVEL_DEBUG
else if (!ack_new && ack_last) {
} else if (!ack_new && ack_last) {
DEBUG("%02X: SUSPWRDNACK de-asserted\n", main_cycle);
}
#endif

View File

@ -9,6 +9,7 @@
#include <common/command.h>
#include <common/macro.h>
#include <common/version.h>
#include <ec/pwm.h>
// Shared memory host semaphore
volatile uint8_t __xdata __at(0x1022) SMHSR;
@ -143,6 +144,30 @@ static enum Result cmd_reset(void) {
return RES_ERR;
}
static enum Result cmd_fan_get(void) {
// If setting fan 0
if (smfi_cmd[2] == 0) {
// Get duty of fan 0
smfi_cmd[3] = DCR2;
return RES_OK;
}
// Failed if fan not found
return RES_ERR;
}
static enum Result cmd_fan_set(void) {
// If setting fan 0
if (smfi_cmd[2] == 0) {
// Set duty cycle of fan 0
DCR2 = smfi_cmd[3];
return RES_OK;
}
// Failed if fan not found
return RES_ERR;
}
void smfi_event(void) {
if (smfi_cmd[0]) {
switch (smfi_cmd[0]) {
@ -174,6 +199,14 @@ void smfi_event(void) {
case CMD_RESET:
smfi_cmd[1] = cmd_reset();
break;
#ifndef __SCRATCH__
case CMD_FAN_GET:
smfi_cmd[1] = cmd_fan_get();
break;
case CMD_FAN_SET:
smfi_cmd[1] = cmd_fan_set();
break;
#endif // __SCRATCH__
default:
// Command not found
smfi_cmd[1] = RES_ERR;

View File

@ -16,6 +16,10 @@ enum Command {
CMD_SPI = 5,
// Reset EC
CMD_RESET = 6,
// Get fan speeds
CMD_FAN_GET = 7,
// Set fan speeds
CMD_FAN_SET = 8,
//TODO
};

BIN
tool/src/.ec.rs.swp Normal file

Binary file not shown.

View File

@ -19,12 +19,14 @@ pub enum Cmd {
Print = 4,
Spi = 5,
Reset = 6,
FanGet = 7,
FanSet = 8,
}
pub const CMD_SPI_FLAG_READ: u8 = (1 << 0);
pub const CMD_SPI_FLAG_DISABLE: u8 = (1 << 1);
pub const CMD_SPI_FLAG_SCRATCH: u8 = (1 << 2);
pub const CMD_SPI_FLAG_BACKUP: u8 = (1 << 3);
pub const CMD_SPI_FLAG_READ: u8 = 1 << 0;
pub const CMD_SPI_FLAG_DISABLE: u8 = 1 << 1;
pub const CMD_SPI_FLAG_SCRATCH: u8 = 1 << 2;
pub const CMD_SPI_FLAG_BACKUP: u8 = 1 << 3;
pub struct Ec<T: Timeout> {
cmd: u16,
@ -181,6 +183,18 @@ impl<T: Timeout> Ec<T> {
pub unsafe fn reset(&mut self) -> Result<(), Error> {
self.command(Cmd::Reset)
}
pub unsafe fn fan_get(&mut self, index: u8) -> Result<u8, Error> {
self.write(2, index);
self.command(Cmd::FanGet)?;
Ok(self.read(3))
}
pub unsafe fn fan_set(&mut self, index: u8, duty: u8) -> Result<(), Error> {
self.write(2, index);
self.write(3, duty);
self.command(Cmd::FanSet)
}
}
pub struct EcSpi<'a, T: Timeout> {

View File

@ -274,9 +274,33 @@ unsafe fn print(message: &[u8]) -> Result<(), Error> {
Ok(())
}
unsafe fn fan_get(index: u8) -> Result<(), Error> {
iopl();
let mut ec = Ec::new(
StdTimeout::new(Duration::new(1, 0)),
)?;
let duty = ec.fan_get(index)?;
println!("{}", duty);
Ok(())
}
unsafe fn fan_set(index: u8, duty: u8) -> Result<(), Error> {
iopl();
let mut ec = Ec::new(
StdTimeout::new(Duration::new(1, 0)),
)?;
ec.fan_set(index, duty)
}
fn usage() {
eprintln!(" console");
eprintln!(" flash [file]");
eprintln!(" fan [index] <duty>");
eprintln!(" info");
eprintln!(" print [message]");
}
@ -293,6 +317,40 @@ fn main() {
process::exit(1);
},
},
"fan" => match args.next() {
Some(index_str) => match index_str.parse::<u8>() {
Ok(index) => match args.next() {
Some(duty_str) => match duty_str.parse::<u8>() {
Ok(duty) => match unsafe { fan_set(index, duty) } {
Ok(()) => (),
Err(err) => {
eprintln!("failed to set fan {} to {}: {:X?}", index, duty, err);
process::exit(1);
},
},
Err(err) => {
eprintln!("failed to parse '{}': {:X?}", duty_str, err);
process::exit(1);
},
},
None => match unsafe { fan_get(index) } {
Ok(()) => (),
Err(err) => {
eprintln!("failed to get fan {}: {:X?}", index, err);
process::exit(1);
},
},
},
Err(err) => {
eprintln!("failed to parse '{}': {:X?}", index_str, err);
process::exit(1);
},
},
None => {
eprintln!("no index provided");
process::exit(1);
},
},
"flash" => match args.next() {
Some(path) => match unsafe { flash(&path) } {
Ok(()) => (),