#include #include #include #include #include #include #include #include #include "icx3.h" int icx3_init(struct card_info *card) { char data[I2C_SMBUS_BLOCK_MAX] = {}; int read_result; struct icx3_info *temp_info; int fd = open(card->i2c_dev_path, O_RDONLY); if (fd == -1) return -1; if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) { close(fd); return -1; } read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_READINFORMATION, ICX3_READINFORMATION_SIZE, data); if (read_result == ICX3_READINFORMATION_SIZE) { temp_info = (struct icx3_info*)&data; if (temp_info->slave_address == ICX3_I2C_ADDR) { card->product_id = temp_info->product_id; card->i2c_fd = fd; return fd; } else { close(fd); return -1; } } return -1; } void print_icx3_fans(struct card_info *card) { struct icx3_fan_control fans[ICX3_MAX_FANS]; get_fan_status(fans, card); for (int i=0; i < ICX3_MAX_FANS; i++) { printf("%s: %d RPM (%d/%d%%, %s)\n", icx3_fan_names[i], fans[i].rpm_status, fans[i].duty_status, fans[i].duty, icx3_fan_mode_names[fans[i].fanmode] ); } } void print_icx3_fans_oneline(struct card_info *card) { struct icx3_fan_control fans[ICX3_MAX_FANS]; get_fan_status(fans, card); for (int i=0; i < ICX3_MAX_FANS; i++) { printf(" %3d", fans[i].duty_status); } printf("%%"); } void get_available_fans(char *fans_avail, struct card_info *card) { int product_id = card->product_id; /* From ICX3TotalFanCtrl.cs */ switch(product_id) { case ICX3_PRODUCT_E221_E222_XC3: case ICX3_PRODUCT_E229_XC3: case ICX3_PRODUCT_E222_XC3_LPC54113: case ICX3_PRODUCT_E229_XC3_LPC54113: case ICX3_PRODUCT_E221_XC3_STM32L431: case ICX3_PRODUCT_E229_E237_XC3_STM32L431: /* XC3, default plus fan 3 */ fans_avail[0] = 1; fans_avail[1] = 1; fans_avail[2] = 1; break; case ICX3_PRODUCT_E227_E228_FTW3: case ICX3_PRODUCT_E230_FTW3: case ICX3_PRODUCT_E227_E228_FTW3_LPC54113: case ICX3_PRODUCT_E230_FTW3_LPC54113: case ICX3_PRODUCT_E230_E238_FTW3_STM32L431: case ICX3_PRODUCT_E250_FTW3: /* FTW3, default plus fan 3 and external fan */ fans_avail[0] = 1; fans_avail[1] = 1; fans_avail[2] = 1; fans_avail[3] = 1; break; case ICX3_PRODUCT_E223_KINGPIN_HYBRID: case ICX3_PRODUCT_E227_E228_FTW3_HYBRID: case ICX3_PRODUCT_E251_KINGPIN_HYBRID: case ICX3_PRODUCT_E227_E228_FTW3_HYBRID_LPC54113: case ICX3_PRODUCT_E250_FTW3_HYBRID: /* KINGPIN, default and external */ fans_avail[0] = 1; fans_avail[1] = 1; fans_avail[3] = 1; break; case ICX3_PRODUCT_E227_E228_FTW3_HC: case ICX3_PRODUCT_E223_KINGPIN_HC: case ICX3_PRODUCT_E251_KINGPIN_HC: /* External fan only */ fans_avail[3] = 1; break; default: /* default fans only */ fans_avail[0] = 1; fans_avail[1] = 1; break; case ICX3_PRODUCT_E221_E222_XC3_HC: /* Not sure what this case is for */ break; } } void get_fan_status(struct icx3_fan_control *fans, struct card_info *card) { char data[I2C_SMBUS_BLOCK_MAX] = {}; int read_result; char fans_avail[ICX3_MAX_FANS] = {0}; get_available_fans(fans_avail, card); for (int i=0; i < ICX3_MAX_FANS; i++) { if (!fans_avail[i]) { continue; } read_result = i2c_smbus_read_i2c_block_data(card->i2c_fd, ICX3_REG_FANCONTROL + i, ICX3_FANCONTROL_SIZE, data); if (read_result != ICX3_FANCONTROL_SIZE) { return; } memcpy(&fans[i], &data, sizeof(struct icx3_fan_control)); } } void get_temp_sensors(float *temps, struct card_info *card) { char data[I2C_SMBUS_BLOCK_MAX] = {}; int read_result; struct icx3_temp_sensors *temp_sensors; read_result = i2c_smbus_read_i2c_block_data(card->i2c_fd, ICX3_REG_TEMPSENSOR, ICX3_TEMPSENSOR_SIZE, data); if (read_result != ICX3_TEMPSENSOR_SIZE) return; temp_sensors = (struct icx3_temp_sensors*) &data; float cur_temp; short cur_data; for (int i=0; idata[2*i+1] << 8) | (short)(temp_sensors->data[2*i]); /* temp is reported in tenths of deg C */ temps[i] = (float)cur_data/10; } } void enable_write(int enable, struct card_info *card) { unsigned char *data; if (enable) data = icx3_write_enable; else data = icx3_write_disable; /* Enable or disable write */ i2c_smbus_write_i2c_block_data(card->i2c_fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, data); /* Read back the result to verify */ unsigned char read_result[ICX3_ENABLEWRITE_SIZE]; int write_ok = 0; i2c_smbus_read_i2c_block_data(card->i2c_fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, read_result); if (enable) write_ok = (read_result[1] == 0xFC); else write_ok = (read_result[1] == 0xFE); if (!write_ok) printf("Unable to enable/disable write on %s\n", card->i2c_dev_path); } /* Sets a given fan on the GPU according to a string setting 'auto' resets the fan to default (fans 1-2 go to GPU control, 3-4 to +0 RPM offset from GPU control) A number without a sign (e.g. 50) manually sets the fan to a given % duty cycle A number with a sign (e.g. +500) sets the fan to run at an RPM offset from GPU control */ void set_fan(int fan, char *setting, struct card_info *card) { char fans_avail[ICX3_MAX_FANS] = {0}; struct icx3_fan_control fan_control = {}; struct icx3_fan_control fan_readback = {}; char reg = ICX3_REG_FANCONTROL + fan; int write_result; /* Check to make sure we're setting a valid fan */ get_available_fans(fans_avail, card); if (fans_avail[fan] == 0) { printf("Fan %d does not exist on this card \n", fan); return; } fan_control.length = ICX3_FANCONTROL_SIZE - 1; if (strcmp(setting, "auto") == 0) { /* auto setting */ fan_control.rpm_offset = 0; if (fan <= 1) fan_control.fanmode = ICX3_FANMODE_GPU_CONTROL; else fan_control.fanmode = ICX3_FANMODE_GPU_RPM_OFFSET; } else if (setting[0] == '+' || setting[0] == '-') { /* RPM offset */ fan_control.fanmode = ICX3_FANMODE_GPU_RPM_OFFSET; fan_control.rpm_offset = atoi(setting); } else { fan_control.fanmode = ICX3_FANMODE_RPM; fan_control.duty = atoi(setting); } enable_write(1, card); i2c_smbus_write_i2c_block_data(card->i2c_fd, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_control); /* Read back data and verify we set the fan properly */ i2c_smbus_read_i2c_block_data(card->i2c_fd, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_readback); if (fan_readback.fanmode != fan_control.fanmode || fan_readback.rpm_offset != fan_control.rpm_offset || fan_readback.duty != fan_control.duty) printf("Error setting fan %d on %s\n", fan, card->i2c_dev_path); }