Skip to content

Commit

Permalink
box firmware: fan control
Browse files Browse the repository at this point in the history
Implement a small algorithm to control the fans via the PWM
to stay in a specific RPM range.

The chip should be able to do this by itself, but we never
managed to get it working, and even the linux-kernel implementation
does not try to ask the chip to keep specific RPM.
  • Loading branch information
krokodilerian committed Nov 10, 2024
1 parent 116078a commit 27fb1b5
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 16 deletions.
1 change: 1 addition & 0 deletions hardware/firmware/box_rp2040/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ void init(void) {
void update(void) {
usb_task();
io_task();
pwr_brd_fan_task();
}

int main(void) {
Expand Down
87 changes: 71 additions & 16 deletions hardware/firmware/box_rp2040/src/pwr_brd_ctl/fan_ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@

#define FAN_CTL_I2C_TIMEOUT 200000

uint64_t time_last_cmd[NUMFAN];
uint16_t desired_fan_speed[NUMFAN];

void pwr_brd_fan_init() {
for (int i = 0; i < NUMFAN; i++) {
time_last_cmd[i] = 0;
desired_fan_speed[i] = DESIRED_RPM;
}
}

bool fan_ctl_i2c_read(uint8_t reg_id, uint8_t* dest) {
return pwr_brd_i2c_read_reg(PWR_BRD_FAN_CTL_ADDR, reg_id, dest, 1);
}
Expand All @@ -26,12 +36,15 @@ bool fan_ctl_i2c_write_and_check(uint8_t reg_id, uint8_t val) {
if (!pwr_brd_i2c_read_reg(PWR_BRD_FAN_CTL_ADDR, reg_id, &rval, 1)) {
return false;
}

if (rval != val) {
return false;
}
return true;
}

#define FAN_TACH_CONSTANT (7864320)

bool fan_ctl_get_fan_speed(uint8_t fan_id, uint16_t* dest) {
uint8_t lsb, msb;
if (!fan_ctl_i2c_read(EMC2301_REG_TACHREADMSB + 0x10*fan_id, &msb)) {
Expand All @@ -50,7 +63,7 @@ bool fan_ctl_get_fan_speed(uint8_t fan_id, uint16_t* dest) {
// Based on the FOSDEM fans, documentation and guesswork
// rpm = 7864320 / tacho

uint16_t rpm = 7864320 / tacho;
uint16_t rpm = FAN_TACH_CONSTANT / tacho;

*dest = rpm;
return true;
Expand All @@ -74,27 +87,69 @@ bool fan_ctl_set_pwm(uint8_t fan_id, uint8_t duty) {
}

bool fan_ctl_set_fan_speed(uint8_t fan_id, uint16_t speed) {
uint8_t fanconfig1;
if (!fan_ctl_i2c_read(EMC2301_REG_FANCONFIG1 + 0x10*fan_id, &fanconfig1)) {
return false;
}
fanconfig1 |= (1 << 7); // enable PID controller
if (!fan_ctl_i2c_write_and_check(EMC2301_REG_FANCONFIG1 + 0x10*fan_id, fanconfig1)) {
if (speed < 100 || speed > 12000)
return false;
}

uint8_t lsb = (speed << 3) && 0xf8;
uint8_t msb = (speed >> 5) && 0xff;
if (!fan_ctl_i2c_write(EMC2301_REG_TACHTARGETLSB + 0x10 * fan_id, lsb)) {
return false;
}
if (!fan_ctl_i2c_write(EMC2301_REG_TACHTARGETMSB + 0x10 * fan_id, msb)) {
return false;
}
desired_fan_speed[fan_id] = speed;
return true;
}

bool fan_ctl_get_fan_status(uint8_t* dest) {
return fan_ctl_i2c_read(EMC2301_REG_FANSTATUS, dest);
}


#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

/*
Standard fan control:
- if fan speed is 0, go to max
- if fan speed is above needed by more than 2x, lower pwm = pwm / 2
- if fan speed is above needed by less than 2x, lower pwm = pwm - 1
- if fan speed is below needed, pwm = pwm + 1
- any change is given 1s to take effect
- the fan speed is within a threshold, so if we are at less than desired + threshold and above desired - threshold, we do nothing
*/

void pwr_brd_fan_task() {
static uint16_t cnt=0;
int i;
uint64_t now;

cnt++;

if (likely(cnt % FAN_WAIT_LOOPS != 0))
return;

now = time_us_64();
for (i = 0; i < NUMFAN; i++) {
if (time_last_cmd[i] + CMD_WAIT_TIME_US < now) continue;

{
uint16_t fanspeed;
uint8_t pwm;

fan_ctl_get_fan_speed(i, &fanspeed);
if (fanspeed > desired_fan_speed[i] - DESIRED_RPM_THRESH && fanspeed < desired_fan_speed[i] + DESIRED_RPM_THRESH) {
continue;
} else if (fanspeed == 0) {
fan_ctl_set_pwm(i, 255);
} else if (fanspeed > desired_fan_speed[i] * 2) {
fan_ctl_get_pwm(i, &pwm);
fan_ctl_set_pwm(i, pwm / 2 );
} else if (fanspeed > desired_fan_speed[i] + DESIRED_RPM_THRESH) {
fan_ctl_get_pwm(i, &pwm);
fan_ctl_set_pwm(i, pwm - 1 );
} else if (fanspeed < desired_fan_speed[i] - DESIRED_RPM_THRESH) {
fan_ctl_get_pwm(i, &pwm);
fan_ctl_set_pwm(i, pwm + 1 );
}
time_last_cmd[i] = now;
}
}

}
1 change: 1 addition & 0 deletions hardware/firmware/box_rp2040/src/pwr_brd_ctl/pwr_brd_ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ void pwr_brd_ctl_init() {
gpio_set_function(PWR_BRD_I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(PWR_BRD_I2C_SCL, GPIO_FUNC_I2C);
expander_init(&expander, PWR_BRD_I2C_INST, PWR_BRD_EXPANDER_ADDR, PWR_BRD_I2C_TIMEOUT_US);
pwr_brd_fan_init();
}

bool pwr_brd_raw_gpio_read(uint8_t* val) {
Expand Down
9 changes: 9 additions & 0 deletions hardware/firmware/box_rp2040/src/pwr_brd_ctl/pwr_brd_ctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
#include <stdbool.h>
#include <stdint.h>

#define FAN_WAIT_LOOPS (1000)
#define CMD_WAIT_TIME_US (1000*1000)
#define DESIRED_RPM (3000)
#define DESIRED_RPM_THRESH (1000)
#define NUMFAN (5)


void pwr_brd_ctl_init();
void pwr_brd_fan_task();
void pwr_brd_fan_init();
bool pwr_brd_raw_gpio_read(uint8_t* val);
void pwr_brd_i2c_bus_scan();
void pwr_brd_i2c_dump_all_regs(uint8_t addr);
Expand Down

0 comments on commit 27fb1b5

Please sign in to comment.