Compare commits
8
Commits
v1.0
...
13aa5b023e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13aa5b023e | ||
|
|
4c5321bc4e | ||
|
|
4cb9ef42a6 | ||
|
|
34bbcf7883 | ||
|
|
afdddabc0c | ||
|
|
73f7038eaa | ||
|
|
113e296347 | ||
|
|
9f29076d52 |
+8
-1
@@ -4,13 +4,20 @@ OBJS = evga-icx.o evga-card.o icx3.o
|
||||
LDLIBS = -li2c
|
||||
CFLAGS = -MD
|
||||
|
||||
ifdef USE_NVML
|
||||
LDLIBS += -lnvidia-ml
|
||||
CFLAGS += -DUSE_NVML
|
||||
OBJS += nvidia-sensors.o
|
||||
endif
|
||||
|
||||
evga-icx : $(OBJS)
|
||||
|
||||
debug : CFLAGS += -g -O0
|
||||
debug : evga-icx
|
||||
|
||||
clean :
|
||||
rm evga-icx $(OBJS)
|
||||
rm evga-icx
|
||||
rm *.o
|
||||
rm *.d
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
@@ -20,10 +20,16 @@ Access to the `/dev/i2c` device files, which means either:
|
||||
|
||||
## Dependencies
|
||||
* libi2c-dev
|
||||
* libnvidia-ml-dev (if building with `USE_NVML=1`)
|
||||
|
||||
## Building
|
||||
`make`
|
||||
|
||||
## Optional features
|
||||
|
||||
### NVML support
|
||||
Add the make flag `USE_NVML=1` and the it will also display the main GPU temperature ("GPU1") as reported by the NVIDIA driver. It will also display the performance cap/clock reason. This requires NVML development files to be installed.
|
||||
|
||||
## Usage
|
||||
Note that when controlling fans directly through iCX3 they will fall offline from the Nvidia driver and show as 0 RPM until you return them to automatic mode.
|
||||
|
||||
@@ -38,6 +44,8 @@ Available options:
|
||||
[+/-]N to set that fan to an RPM offset from the GPU-controlled speed
|
||||
--reset : Reset all fans to their default mode
|
||||
--sensors : Print sensor readings even if setting a fan speed
|
||||
--compact : Print sensor reading in a compact one-line per card format
|
||||
--watch N : Keep printing output every N seconds
|
||||
```
|
||||
|
||||
### Examples:
|
||||
@@ -45,19 +53,27 @@ Read sensors:
|
||||
```text
|
||||
$ ./evga-icx
|
||||
#0: EVGA GeForce RTX 3090 FTW3 Ultra v2 (/dev/i2c-3) @ c:00.0
|
||||
Fan 0: 1094 RPM (36/0%, Auto)
|
||||
Fan 1: 1164 RPM (38/0%, Auto)
|
||||
Fan 2: 1161 RPM (38/0%, Offset)
|
||||
Fan 0: 1122 RPM (37/0%, Auto)
|
||||
Fan 1: 1177 RPM (39/0%, Auto)
|
||||
Fan 2: 1189 RPM (39/0%, Offset)
|
||||
Ext. fan: 0 RPM (0/0%, Offset)
|
||||
GPU2: 35.8 C
|
||||
MEM1: 35.3 C
|
||||
MEM2: 35.6 C
|
||||
MEM3: 35.9 C
|
||||
PWR1: 35.0 C
|
||||
PWR2: 35.6 C
|
||||
PWR3: 36.3 C
|
||||
PWR4: 36.9 C
|
||||
PWR5: 36.3 C
|
||||
GPU1: +36°C
|
||||
GPU2: +41.2°C
|
||||
MEM1: +42.8°C
|
||||
MEM2: +41.1°C
|
||||
MEM3: +41.5°C
|
||||
PWR1: +40.5°C
|
||||
PWR2: +41.2°C
|
||||
PWR3: +41.9°C
|
||||
PWR4: +42.5°C
|
||||
PWR5: +42.0°C
|
||||
Clock reasons: GPU idle (0x1)
|
||||
```
|
||||
|
||||
Compact one-line mode:
|
||||
```text
|
||||
$ ./evga-icx --compact
|
||||
#0 FAN 37 39 39 0% GPU 35 41 MEM 41 41 41 PWR 40 41 42 42 42°C CLK Idle
|
||||
```
|
||||
|
||||
Set external fan to follow Nvidia driver controlled speed with a -500 RPM offset:
|
||||
|
||||
+17
-4
@@ -13,6 +13,7 @@ int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus)
|
||||
char i2c_devices_path[NAME_MAX];
|
||||
char device_path[NAME_MAX];
|
||||
char dev_file[NAME_MAX];
|
||||
char *pci_addr;
|
||||
|
||||
FILE *test_fd;
|
||||
DIR *dir;
|
||||
@@ -39,6 +40,11 @@ int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus)
|
||||
strcpy(device_path, i2c_devices_path);
|
||||
strcat(device_path, ent->d_name);
|
||||
|
||||
/* Only check Nvidia devices */
|
||||
pci_addr = read_nvidia_pci_address(device_path);
|
||||
if (pci_addr == NULL)
|
||||
continue;
|
||||
|
||||
/* Read the PCI info for the underlying device */
|
||||
pci_vendor = read_pci_id(device_path, "/device/vendor");
|
||||
pci_device = read_pci_id(device_path, "/device/device");
|
||||
@@ -56,10 +62,11 @@ int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus)
|
||||
/* Matched all PCI IDs, check for good firmware read */
|
||||
strcpy(dev_file, "/dev/");
|
||||
strcat(dev_file, ent->d_name);
|
||||
if (check_for_icx3(dev_file)) {
|
||||
infos[num_gpus].i2c_dev_path = dev_file;
|
||||
if (icx3_init(&infos[num_gpus]) > 0) {
|
||||
/* Write our card info into the provided struct array */
|
||||
infos[num_gpus].card_name = evga_pci_ids[i].card_name;
|
||||
infos[num_gpus].pci_id = read_nvidia_pci_address(device_path);
|
||||
infos[num_gpus].pci_id = pci_addr;
|
||||
infos[num_gpus].i2c_dev_path = calloc(strlen(dev_file) + 1, sizeof(char));
|
||||
strcpy(infos[num_gpus].i2c_dev_path, dev_file);
|
||||
num_gpus++;
|
||||
@@ -111,12 +118,18 @@ char *read_nvidia_pci_address(char *device_path)
|
||||
|
||||
FILE *fp = fopen(file_path, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
if (fp == NULL) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fscanf(fp, "NVIDIA i2c adapter %*u at %16s", ret);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (strlen(ret) == 0) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef EVGA_CARD_H
|
||||
#define EVGA_CARD_H
|
||||
|
||||
#define NVIDIA_VEN 0x10DE
|
||||
|
||||
#define NVIDIA_RTX3060_DEV 0x2503
|
||||
@@ -84,6 +87,8 @@ struct card_info {
|
||||
char *card_name;
|
||||
char *pci_id;
|
||||
char *i2c_dev_path;
|
||||
int i2c_fd;
|
||||
int product_id;
|
||||
};
|
||||
|
||||
struct gpu_pci_info {
|
||||
@@ -158,3 +163,5 @@ static struct gpu_pci_info evga_pci_ids[] =
|
||||
int find_evga_gpu_i2cs(struct card_info *infos, int max_gpus);
|
||||
unsigned short read_pci_id(char *device_path, char *field);
|
||||
char *read_nvidia_pci_address(char *device_path);
|
||||
|
||||
#endif
|
||||
|
||||
+71
-19
@@ -5,6 +5,10 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef USE_NVML
|
||||
#include "nvidia-sensors.h"
|
||||
#endif
|
||||
|
||||
#include "icx3.h"
|
||||
#include "evga-card.h"
|
||||
|
||||
@@ -19,16 +23,20 @@ static const char helpstring[] = "Available options:\n"
|
||||
" N to set the fan to that manual % speed\n"
|
||||
" [+/-]N to set that fan to an RPM offset from the GPU-controlled speed\n"
|
||||
"--reset : Reset all fans to their default mode\n"
|
||||
"--sensors : Print sensor readings even if setting a fan speed \n";
|
||||
"--sensors : Print sensor readings even if setting a fan speed \n"
|
||||
"--compact : Print sensor reading in a compact one-line per card format\n"
|
||||
"--watch N : Keep printing output every N seconds\n";
|
||||
|
||||
void print_gpu_info(int gpu_num, struct card_info gpus[]);
|
||||
void print_gpu_info(int gpu_num, struct card_info gpus[], int compact);
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
struct card_info gpus[MAX_GPUS];
|
||||
int gpu_count;
|
||||
int print_info = 0;
|
||||
int compact = 0;
|
||||
int gpu_num = -1; /* Card to control */
|
||||
unsigned int watch = 0;
|
||||
char *fan_speed[ICX3_MAX_FANS] = {NULL};
|
||||
|
||||
/* Input parsing */
|
||||
@@ -65,6 +73,16 @@ int main (int argc, char **argv)
|
||||
fan_speed[j] = "auto";
|
||||
} else if (strcmp(argv[i], "--sensors") == 0) {
|
||||
print_info = 1;
|
||||
} else if (strcmp(argv[i], "--compact") == 0) {
|
||||
compact = 1;
|
||||
} else if (strcmp(argv[i], "--watch") == 0) {
|
||||
i++;
|
||||
if (i < argc) {
|
||||
watch = atoi(argv[i]);
|
||||
} else {
|
||||
printf(helpstring);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
printf(helpstring);
|
||||
return 0;
|
||||
@@ -101,35 +119,69 @@ int main (int argc, char **argv)
|
||||
for (int i = 0; i < gpu_count; i++){
|
||||
for (int j = 0; j < ICX3_MAX_FANS; j++) {
|
||||
if (fan_speed[j] != NULL)
|
||||
set_fan(j, fan_speed[j], gpus[i].i2c_dev_path);
|
||||
/* printf("gpu %d fan %d : %s\n", i, j, fan_speed[j]); */
|
||||
set_fan(j, fan_speed[j], &gpus[i]);
|
||||
}
|
||||
}
|
||||
} else if (gpu_num <= gpu_count - 1) {
|
||||
for (int j = 0; j < ICX3_MAX_FANS; j++) {
|
||||
if (fan_speed[j] != NULL)
|
||||
set_fan(gpu_num, fan_speed[j], gpus[gpu_num].i2c_dev_path);
|
||||
/* printf("gpu %d fan %d : %s\n", gpu_num, j, fan_speed[j]);*/
|
||||
set_fan(gpu_num, fan_speed[j], &gpus[gpu_num]);
|
||||
}
|
||||
}
|
||||
|
||||
/* NVML init */
|
||||
#ifdef USE_NVML
|
||||
int nvml_ok = init_nvml();
|
||||
nvmlDevice_t* device[MAX_GPUS];
|
||||
#endif
|
||||
|
||||
/* print sensor info */
|
||||
if (print_info) {
|
||||
if (gpu_num == -1) {
|
||||
for (int i = 0; i < gpu_count; i++){
|
||||
print_gpu_info(i, gpus);
|
||||
do {
|
||||
if (gpu_num == -1) {
|
||||
for (int i = 0; i < gpu_count; i++){
|
||||
print_gpu_info(i, &gpus[i], compact);
|
||||
}
|
||||
} else if (gpu_num <= gpu_count - 1) {
|
||||
print_gpu_info(gpu_num, &gpus[gpu_num], compact);
|
||||
}
|
||||
} else if (gpu_num <= gpu_count - 1) {
|
||||
print_gpu_info(gpu_num, gpus);
|
||||
}
|
||||
sleep(watch);
|
||||
} while (watch > 0);
|
||||
}
|
||||
|
||||
#ifdef USE_NVML
|
||||
nvmlShutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_gpu_info(int gpu_num, struct card_info *gpu, int compact) {
|
||||
if (compact) {
|
||||
/* One line per GPU */
|
||||
printf("#%d ", gpu_num);
|
||||
print_icx3_fans_oneline(gpu);
|
||||
printf(" GPU");
|
||||
#ifdef USE_NVML
|
||||
print_nvml_temp(1, gpu);
|
||||
#endif
|
||||
print_icx3_temps_oneline(gpu);
|
||||
printf("°C");
|
||||
#ifdef USE_NVML
|
||||
print_nvml_clock_reason(1, gpu);
|
||||
#endif
|
||||
printf("\n");
|
||||
} else {
|
||||
/* One line per GPU sensor */
|
||||
printf("#%d: %s (%s) @ %s\n", gpu_num, gpu->card_name, gpu->i2c_dev_path, gpu->pci_id);
|
||||
print_icx3_fans(gpu);
|
||||
#ifdef USE_NVML
|
||||
print_nvml_temp(0, gpu);
|
||||
#endif
|
||||
print_icx3_temps(gpu);
|
||||
#ifdef USE_NVML
|
||||
print_nvml_clock_reason(0, gpu);
|
||||
#endif
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void print_gpu_info(int gpu_num, struct card_info gpus[]) {
|
||||
printf("#%d: %s (%s) @ %s\n", gpu_num, gpus[gpu_num].card_name, gpus[gpu_num].i2c_dev_path, gpus[gpu_num].pci_id);
|
||||
print_icx3_fans(gpus[gpu_num].i2c_dev_path);
|
||||
print_icx3_temps(gpus[gpu_num].i2c_dev_path);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,99 +9,96 @@
|
||||
|
||||
#include "icx3.h"
|
||||
|
||||
/* Check an I2C device file for an ICX3 controller, returns the product id, or ICX3_PRODUCT_NONE on bad read. */
|
||||
enum icx3_product_id check_for_icx3(char *i2c_dev_path)
|
||||
int icx3_init(struct card_info *card)
|
||||
{
|
||||
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||
int fd, read_result;
|
||||
int read_result;
|
||||
struct icx3_info *temp_info;
|
||||
|
||||
fd = open_i2c_dev(i2c_dev_path);
|
||||
int fd = open(card->i2c_dev_path, O_RDONLY);
|
||||
|
||||
if (fd == -1)
|
||||
return ICX3_PRODUCT_NONE;
|
||||
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);
|
||||
close(fd);
|
||||
if (read_result == ICX3_READINFORMATION_SIZE) {
|
||||
temp_info = (struct icx3_info*)&data;
|
||||
if (temp_info->slave_address == ICX3_I2C_ADDR)
|
||||
return temp_info->product_id;
|
||||
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 ICX3_PRODUCT_NONE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void print_icx3_fans(char *i2c_dev_path)
|
||||
void print_icx3_fans(struct card_info *card)
|
||||
{
|
||||
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||
int fd, read_result;
|
||||
|
||||
struct icx3_fan_control *fan_status;
|
||||
|
||||
/* First thing is to check for the product ID to figure out how many fans we have */
|
||||
char fans_avail[ICX3_MAX_FANS] = {0};
|
||||
get_available_fans(i2c_dev_path, fans_avail);
|
||||
|
||||
fd = open_i2c_dev(i2c_dev_path);
|
||||
if (fd == -1)
|
||||
return;
|
||||
struct icx3_fan_control fans[ICX3_MAX_FANS];
|
||||
get_fan_status(fans, card);
|
||||
|
||||
for (int i=0; i < ICX3_MAX_FANS; i++) {
|
||||
if (!fans_avail[i])
|
||||
continue;
|
||||
|
||||
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_FANCONTROL + i, ICX3_FANCONTROL_SIZE, data);
|
||||
if (read_result != ICX3_FANCONTROL_SIZE) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
fan_status = (struct icx3_fan_control*) &data;
|
||||
printf("%s: %d RPM (%d/%d%%, %s)\n",
|
||||
icx3_fan_names[i],
|
||||
fan_status->rpm_status,
|
||||
fan_status->duty_status,
|
||||
fan_status->duty,
|
||||
icx3_fan_mode_names[fan_status->fanmode]
|
||||
fans[i].rpm_status,
|
||||
fans[i].duty_status,
|
||||
fans[i].duty,
|
||||
icx3_fan_mode_names[fans[i].fanmode]
|
||||
);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void print_icx3_temps(char *i2c_dev_path)
|
||||
void print_icx3_fans_oneline(struct card_info *card)
|
||||
{
|
||||
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||
int fd, read_result;
|
||||
struct icx3_temp_sensors *temp_sensors;
|
||||
struct icx3_fan_control fans[ICX3_MAX_FANS];
|
||||
get_fan_status(fans, card);
|
||||
|
||||
fd = open_i2c_dev(i2c_dev_path);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
read_result = i2c_smbus_read_i2c_block_data(fd, ICX3_REG_TEMPSENSOR, ICX3_TEMPSENSOR_SIZE, data);
|
||||
if (read_result != ICX3_TEMPSENSOR_SIZE) {
|
||||
close(fd);
|
||||
return;
|
||||
printf("FAN");
|
||||
for (int i=0; i < ICX3_MAX_FANS; i++) {
|
||||
printf(" %3d", fans[i].duty_status);
|
||||
}
|
||||
temp_sensors = (struct icx3_temp_sensors*) &data;
|
||||
printf("%%");
|
||||
}
|
||||
|
||||
float cur_temp;
|
||||
short cur_data;
|
||||
void print_icx3_temps(struct card_info *card)
|
||||
{
|
||||
float temps[ICX3_NUM_TEMP_SENSORS];
|
||||
|
||||
get_temp_sensors(temps, card);
|
||||
|
||||
for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) {
|
||||
/* endian swap */
|
||||
cur_data = (short)(temp_sensors->data[2*i+1] << 8) | (short)(temp_sensors->data[2*i]);
|
||||
/* temp is reported in tenths of deg C */
|
||||
cur_temp = (float)cur_data/10;
|
||||
printf("%s: %.1f C\n",
|
||||
printf("%s: %+.1f°C\n",
|
||||
icx3_temp_sensor_names[i],
|
||||
cur_temp);
|
||||
temps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void get_available_fans(char *i2c_dev_path, char *fans_avail)
|
||||
void print_icx3_temps_oneline(struct card_info *card)
|
||||
{
|
||||
int product_id = check_for_icx3(i2c_dev_path);
|
||||
float temps[ICX3_NUM_TEMP_SENSORS];
|
||||
|
||||
get_temp_sensors(temps, card);
|
||||
|
||||
for (int i=0; i<ICX3_NUM_TEMP_SENSORS; i++) {
|
||||
/* If this math seems a little jank, it's so we can optionally inject the NVML and vram temps into the oneline */
|
||||
if (i > 0 && strncmp(icx3_temp_sensor_names[i], icx3_temp_sensor_names[i-1], 3))
|
||||
printf(" %.3s", icx3_temp_sensor_names[i]);
|
||||
printf(" %3.0f", temps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void get_available_fans(char *fans_avail, struct card_info *card)
|
||||
{
|
||||
int product_id = card->product_id;
|
||||
|
||||
/* From ICX3TotalFanCtrl.cs */
|
||||
switch(product_id) {
|
||||
@@ -155,54 +152,77 @@ void get_available_fans(char *i2c_dev_path, char *fans_avail)
|
||||
}
|
||||
}
|
||||
|
||||
int open_i2c_dev(char *i2c_dev_path)
|
||||
void get_fan_status(struct icx3_fan_control *fans, struct card_info *card)
|
||||
{
|
||||
int fd = open(i2c_dev_path, O_RDONLY);
|
||||
char data[I2C_SMBUS_BLOCK_MAX] = {};
|
||||
int read_result;
|
||||
|
||||
/* Error silently here because we should have already checked we can read the i2c in check_for_icx3() */
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
char fans_avail[ICX3_MAX_FANS] = {0};
|
||||
get_available_fans(fans_avail, card);
|
||||
|
||||
if (ioctl(fd, I2C_SLAVE, ICX3_I2C_ADDR) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
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));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
void enable_write(int enable, char *i2c_dev_path)
|
||||
void get_temp_sensors(float *temps, struct card_info *card)
|
||||
{
|
||||
int fd = open_i2c_dev(i2c_dev_path);
|
||||
if (fd == -1)
|
||||
return;
|
||||
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; i<ICX3_NUM_TEMP_SENSORS; i++) {
|
||||
/* endian swap */
|
||||
cur_data = (short)(temp_sensors->data[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(fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, data);
|
||||
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(fd, ICX3_REG_ENABLEWRITE, ICX3_ENABLEWRITE_SIZE, read_result);
|
||||
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", i2c_dev_path);
|
||||
|
||||
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, char *i2c_dev_path)
|
||||
void set_fan(int fan, char *setting, struct card_info *card)
|
||||
{
|
||||
char fans_avail[ICX3_MAX_FANS] = {0};
|
||||
|
||||
@@ -213,16 +233,12 @@ void set_fan(int fan, char *setting, char *i2c_dev_path)
|
||||
int write_result;
|
||||
|
||||
/* Check to make sure we're setting a valid fan */
|
||||
get_available_fans(i2c_dev_path, fans_avail);
|
||||
get_available_fans(fans_avail, card);
|
||||
if (fans_avail[fan] == 0) {
|
||||
printf("Fan %d does not exist on this card \n", fan);
|
||||
return;
|
||||
}
|
||||
|
||||
int fp = open_i2c_dev(i2c_dev_path);
|
||||
if (fp == -1)
|
||||
return;
|
||||
|
||||
fan_control.length = ICX3_FANCONTROL_SIZE - 1;
|
||||
if (strcmp(setting, "auto") == 0) {
|
||||
/* auto setting */
|
||||
@@ -240,16 +256,15 @@ void set_fan(int fan, char *setting, char *i2c_dev_path)
|
||||
fan_control.duty = atoi(setting);
|
||||
}
|
||||
|
||||
enable_write(1, i2c_dev_path);
|
||||
enable_write(1, card);
|
||||
|
||||
i2c_smbus_write_i2c_block_data(fp, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_control);
|
||||
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(fp, reg, ICX3_FANCONTROL_SIZE, (char *)&fan_readback);
|
||||
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, i2c_dev_path);
|
||||
close(fp);
|
||||
printf("Error setting fan %d on %s\n", fan, card->i2c_fd);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
#ifndef ICX3_H
|
||||
#define ICX3_H
|
||||
|
||||
#include "evga-card.h"
|
||||
|
||||
#define ICX3_I2C_ADDR 0x2D
|
||||
|
||||
#define ICX3_REG_FANCONTROL 80
|
||||
@@ -111,10 +116,15 @@ static char *icx3_temp_sensor_names[] = {
|
||||
"PWR5",
|
||||
};
|
||||
|
||||
enum icx3_product_id check_for_icx3(char *i2c_dev_path);
|
||||
void print_icx3_fans(char *i2c_dev_path);
|
||||
void print_icx3_temps(char *i2c_dev_path);
|
||||
void get_available_fans(char *i2c_dev_path, char *fans_avail);
|
||||
int open_i2c_dev(char *i2c_dev_path);
|
||||
void enable_write(int enable, char *i2c_dev_path);
|
||||
void set_fan(int fan, char *setting, char *i2c_dev_path);
|
||||
int icx3_init(struct card_info *card);
|
||||
void print_icx3_fans(struct card_info *card);
|
||||
void print_icx3_fans_oneline(struct card_info *card);
|
||||
void print_icx3_temps(struct card_info *card);
|
||||
void print_icx3_temps_oneline(struct card_info *card);
|
||||
void get_available_fans(char *fans_avail, struct card_info *card);
|
||||
void get_fan_status(struct icx3_fan_control *fans, struct card_info *card);
|
||||
void get_temp_sensors(float *temps, struct card_info *card);
|
||||
void enable_write(int enable, struct card_info *card);
|
||||
void set_fan(int fan, char *setting, struct card_info *card);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nvidia-sensors.h"
|
||||
|
||||
int init_nvml()
|
||||
{
|
||||
nvmlReturn_t result;
|
||||
result = nvmlInit_v2();
|
||||
if (result != NVML_SUCCESS) {
|
||||
printf("Could not init NVML: %s\n", nvmlErrorString(result));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void print_nvml_temp(int compact, struct card_info *card)
|
||||
{
|
||||
if (compact)
|
||||
printf(" %3d", get_nvml_temp(card));
|
||||
else
|
||||
printf("GPU1: %+d°C\n", get_nvml_temp(card));
|
||||
}
|
||||
|
||||
void print_nvml_clock_reason(int compact, struct card_info *card)
|
||||
{
|
||||
unsigned long long reasons = get_nvml_clock_reasons(card);
|
||||
int single_reason = 1;
|
||||
|
||||
if (compact)
|
||||
printf(" CLK ");
|
||||
else
|
||||
printf("Clock reasons: ");
|
||||
|
||||
for (int i = 0; i < (sizeof(clock_reason_names) / sizeof(struct clock_reason)); i++) {
|
||||
if (reasons & clock_reason_names[i].mask) {
|
||||
if (!single_reason) {
|
||||
if (compact)
|
||||
printf(",");
|
||||
else
|
||||
printf(", ");
|
||||
}
|
||||
single_reason = 0;
|
||||
|
||||
if (compact)
|
||||
printf("%s", clock_reason_names[i].short_name);
|
||||
else
|
||||
printf("%s", clock_reason_names[i].long_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (single_reason)
|
||||
printf("None");
|
||||
|
||||
if (!compact)
|
||||
printf(" (0x%llx)\n", reasons);
|
||||
}
|
||||
|
||||
unsigned int get_nvml_temp(struct card_info *card)
|
||||
{
|
||||
nvmlReturn_t result;
|
||||
nvmlDevice_t nvml_device;
|
||||
result = nvmlDeviceGetHandleByPciBusId_v2(card->pci_id, &nvml_device);
|
||||
if (result != NVML_SUCCESS) {
|
||||
printf("Failed to get device handle for card at %s: %s\n", card->pci_id, nvmlErrorString(result));
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int temp;
|
||||
result = nvmlDeviceGetTemperature(nvml_device, NVML_TEMPERATURE_GPU, &temp);
|
||||
if (result != NVML_SUCCESS) {
|
||||
printf("Failed to get temperature for card at %s: %s\n", card->pci_id, nvmlErrorString(result));
|
||||
return 0;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
unsigned long long get_nvml_clock_reasons(struct card_info *card)
|
||||
{
|
||||
nvmlReturn_t result;
|
||||
nvmlDevice_t nvml_device;
|
||||
result = nvmlDeviceGetHandleByPciBusId_v2(card->pci_id, &nvml_device);
|
||||
if (result != NVML_SUCCESS) {
|
||||
printf("Failed to get device handle for card at %s: %s\n", card->pci_id, nvmlErrorString(result));
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long reasons;
|
||||
result = nvmlDeviceGetCurrentClocksEventReasons(nvml_device, &reasons) ;
|
||||
if (result != NVML_SUCCESS) {
|
||||
printf("Failed to get clock reasons for card at %s: %s\n", card->pci_id, nvmlErrorString(result));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reasons;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <nvml.h>
|
||||
|
||||
#include "evga-card.h"
|
||||
|
||||
struct clock_reason {
|
||||
unsigned long long mask;
|
||||
char *short_name;
|
||||
char *long_name;
|
||||
};
|
||||
|
||||
static struct clock_reason clock_reason_names[] =
|
||||
{
|
||||
{nvmlClocksEventReasonGpuIdle, "Idle", "GPU idle"},
|
||||
{nvmlClocksEventReasonApplicationsClocksSetting, "AppClk", "Application clocks"},
|
||||
{nvmlClocksEventReasonSwPowerCap, "Pwr", "Power cap"},
|
||||
{nvmlClocksThrottleReasonHwSlowdown, "HWSlow", "Hardware slowdown"},
|
||||
{nvmlClocksEventReasonSyncBoost, "Sync", "Sync boost"},
|
||||
{nvmlClocksEventReasonSwThermalSlowdown, "SWTherm", "Software thermal"},
|
||||
{nvmlClocksThrottleReasonHwThermalSlowdown, "HWTherm", "Hardware thermal"},
|
||||
{nvmlClocksThrottleReasonHwPowerBrakeSlowdown, "HWPower", "Hardware power brake"},
|
||||
{nvmlClocksEventReasonDisplayClockSetting, "DispClk", "Display clock"}
|
||||
};
|
||||
|
||||
int init_nvml();
|
||||
void print_nvml_temp(int compact, struct card_info *card);
|
||||
void print_nvml_clock_reason(int compact, struct card_info *card);
|
||||
unsigned int get_nvml_temp(struct card_info *card);
|
||||
unsigned long long get_nvml_clock_reasons(struct card_info *card);
|
||||
Reference in New Issue
Block a user