diff --git a/src/tuxedo_io/tuxedo_io.c b/src/tuxedo_io/tuxedo_io.c index d25583a..605e81d 100644 --- a/src/tuxedo_io/tuxedo_io.c +++ b/src/tuxedo_io/tuxedo_io.c @@ -34,7 +34,7 @@ MODULE_DESCRIPTION("Hardware interface for TUXEDO laptops"); MODULE_AUTHOR("TUXEDO Computers GmbH "); -MODULE_VERSION("0.2.4"); +MODULE_VERSION("0.2.6"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CLEVO_INTERFACES(); @@ -259,6 +259,86 @@ static long clevo_ioctl_interface(struct file *file, unsigned int cmd, unsigned return 0; } +static int has_universal_ec_fan_control(void) { + int ret; + u8 data; + + ret = uniwill_read_ec_ram(0x078e, &data); + if (ret < 0) { + return ret; + } + return (data >> 6) & 1; +} + +static int set_full_fan_mode(bool enable) { + u8 mode_data; + + uniwill_read_ec_ram(0x0751, &mode_data); + + if (enable && !(mode_data & 0x40)) { + // If not "full fan mode" (i.e. 0x40 bit not set) switch to it (required for old fancontrol) + return uniwill_write_ec_ram(0x0751, mode_data | 0x40); + } + else if (mode_data & 0x40){ + // If "full fan mode" (i.e. 0x40 bit set) turn it off (required for new fancontrol) + return uniwill_write_ec_ram(0x0751, mode_data & ~0x40); + } + + return 0; +} + +static bool fans_initialized = false; + +static int uw_init_fan(void) { + int i; + + u16 addr_use_custom_fan_table_0 = 0x07c5; // use different tables for both fans (0x0f00-0x0f2f and 0x0f30-0x0f5f respectivly) + u16 addr_use_custom_fan_table_1 = 0x07c6; // enable 0x0fxx fantables + u8 offset_use_custom_fan_table_0 = 7; + u8 offset_use_custom_fan_table_1 = 2; + u8 value_use_custom_fan_table_0; + u8 value_use_custom_fan_table_1; + u16 addr_cpu_custom_fan_table_end_temp = 0x0f00; + u16 addr_cpu_custom_fan_table_start_temp = 0x0f10; + u16 addr_cpu_custom_fan_table_fan_speed = 0x0f20; + u16 addr_gpu_custom_fan_table_end_temp = 0x0f30; + u16 addr_gpu_custom_fan_table_start_temp = 0x0f40; + u16 addr_gpu_custom_fan_table_fan_speed = 0x0f50; + + if (!fans_initialized && (has_universal_ec_fan_control() == 1)) { + set_full_fan_mode(false); + + uniwill_read_ec_ram(addr_use_custom_fan_table_0, &value_use_custom_fan_table_0); + if (!((value_use_custom_fan_table_0 >> offset_use_custom_fan_table_0) & 1)) { + uniwill_write_ec_ram_with_retry(addr_use_custom_fan_table_0, value_use_custom_fan_table_0 + (1 << offset_use_custom_fan_table_0), 3); + } + + uniwill_write_ec_ram_with_retry(addr_cpu_custom_fan_table_end_temp, 0xff, 3); + uniwill_write_ec_ram_with_retry(addr_cpu_custom_fan_table_start_temp, 0x00, 3); + uniwill_write_ec_ram_with_retry(addr_cpu_custom_fan_table_fan_speed, 0x00, 3); + uniwill_write_ec_ram_with_retry(addr_gpu_custom_fan_table_end_temp, 0xff, 3); + uniwill_write_ec_ram_with_retry(addr_gpu_custom_fan_table_start_temp, 0x00, 3); + uniwill_write_ec_ram_with_retry(addr_gpu_custom_fan_table_fan_speed, 0x00, 3); + for (i = 0x1; i <= 0xf; ++i) { + uniwill_write_ec_ram_with_retry(addr_cpu_custom_fan_table_end_temp + i, 0xff, 3); + uniwill_write_ec_ram_with_retry(addr_cpu_custom_fan_table_start_temp + i, 0xff, 3); + uniwill_write_ec_ram_with_retry(addr_cpu_custom_fan_table_fan_speed + i, 0x00, 3); + uniwill_write_ec_ram_with_retry(addr_gpu_custom_fan_table_end_temp + i, 0xff, 3); + uniwill_write_ec_ram_with_retry(addr_gpu_custom_fan_table_start_temp + i, 0xff, 3); + uniwill_write_ec_ram_with_retry(addr_gpu_custom_fan_table_fan_speed + i, 0x00, 3); + } + + uniwill_read_ec_ram(addr_use_custom_fan_table_1, &value_use_custom_fan_table_1); + if (!((value_use_custom_fan_table_1 >> offset_use_custom_fan_table_1) & 1)) { + uniwill_write_ec_ram_with_retry(addr_use_custom_fan_table_1, value_use_custom_fan_table_1 + (1 << offset_use_custom_fan_table_1), 3); + } + } + + fans_initialized = true; + + return 0; +} + static u32 uw_set_fan(u32 fan_index, u8 fan_speed) { u32 i; @@ -267,30 +347,47 @@ static u32 uw_set_fan(u32 fan_index, u8 fan_speed) u16 addr_fan1 = 0x1809; u16 addr_for_fan; - if (fan_index == 0) - addr_for_fan = addr_fan0; - else if (fan_index == 1) - addr_for_fan = addr_fan1; - else - return -EINVAL; + u16 addr_cpu_custom_fan_table_fan_speed = 0x0f20; + u16 addr_gpu_custom_fan_table_fan_speed = 0x0f50; + + if (has_universal_ec_fan_control() == 1) { + uw_init_fan(); + + if (fan_index == 0) + addr_for_fan = addr_cpu_custom_fan_table_fan_speed; + else if (fan_index == 1) + addr_for_fan = addr_gpu_custom_fan_table_fan_speed; + else + return -EINVAL; - // Check current mode - uniwill_read_ec_ram(0x0751, &mode_data); - if (!(mode_data & 0x40)) { - // If not "full fan mode" (i.e. 0x40 bit set) switch to it (required for fancontrol) - uniwill_write_ec_ram(0x0751, mode_data | 0x40); - // Attempt to write both fans as quick as possible before complete ramp-up - pr_debug("prevent ramp-up start\n"); - for (i = 0; i < 10; ++i) { - uniwill_write_ec_ram(addr_fan0, fan_speed & 0xff); - uniwill_write_ec_ram(addr_fan1, fan_speed & 0xff); - msleep(10); - } - pr_debug("prevent ramp-up done\n"); - } else { - // Otherwise just set the chosen fan uniwill_write_ec_ram(addr_for_fan, fan_speed & 0xff); } + else { // old workaround using full fan mode + if (fan_index == 0) + addr_for_fan = addr_fan0; + else if (fan_index == 1) + addr_for_fan = addr_fan1; + else + return -EINVAL; + + // Check current mode + uniwill_read_ec_ram(0x0751, &mode_data); + if (!(mode_data & 0x40)) { + // If not "full fan mode" (i.e. 0x40 bit set) switch to it (required for fancontrol) + set_full_fan_mode(true); + // Attempt to write both fans as quick as possible before complete ramp-up + pr_debug("prevent ramp-up start\n"); + for (i = 0; i < 10; ++i) { + uniwill_write_ec_ram(addr_fan0, fan_speed & 0xff); + uniwill_write_ec_ram(addr_fan1, fan_speed & 0xff); + msleep(10); + } + pr_debug("prevent ramp-up done\n"); + } else { + // Otherwise just set the chosen fan + uniwill_write_ec_ram(addr_for_fan, fan_speed & 0xff); + } + } return 0; } @@ -298,10 +395,15 @@ static u32 uw_set_fan(u32 fan_index, u8 fan_speed) static u32 uw_set_fan_auto(void) { u8 mode_data; - // Get current mode - uniwill_read_ec_ram(0x0751, &mode_data); - // Switch off "full fan mode" (i.e. unset 0x40 bit) - uniwill_write_ec_ram(0x0751, mode_data & 0xbf); + + if (has_universal_ec_fan_control() == 1) { + } + else { + // Get current mode + uniwill_read_ec_ram(0x0751, &mode_data); + // Switch off "full fan mode" (i.e. unset 0x40 bit) + uniwill_write_ec_ram(0x0751, mode_data & 0xbf); + } return 0; } @@ -473,6 +575,26 @@ static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigne result = byte_data; copy_result = copy_to_user((void *) arg, &result, sizeof(result)); break; + case R_UW_FANS_OFF_AVAILABLE: + result = has_universal_ec_fan_control(); + if (result == 1) { + result = 0; + } + else if (result == 0) { + result = 1; + } + copy_result = copy_to_user((void *) arg, &result, sizeof(result)); + break; + case R_UW_FANS_MIN_SPEED: + result = has_universal_ec_fan_control(); + if (result == 1) { + result = 20; + } + else if (result == 0) { + result = 0; + } + copy_result = copy_to_user((void *) arg, &result, sizeof(result)); + break; case R_UW_TDP0: result = uw_get_tdp(0); copy_result = copy_to_user((void *) arg, &result, sizeof(result)); @@ -517,7 +639,6 @@ static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigne result = 3; copy_result = copy_to_user((void *) arg, &result, sizeof(result)); break; - #ifdef DEBUG case R_TF_BC: copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg)); diff --git a/src/tuxedo_io/tuxedo_io_ioctl.h b/src/tuxedo_io/tuxedo_io_ioctl.h index 596dbfe..4a7c60e 100644 --- a/src/tuxedo_io/tuxedo_io_ioctl.h +++ b/src/tuxedo_io/tuxedo_io_ioctl.h @@ -80,18 +80,20 @@ #define R_UW_MODE _IOR(MAGIC_READ_UW, 0x14, int32_t*) #define R_UW_MODE_ENABLE _IOR(MAGIC_READ_UW, 0x15, int32_t*) +#define R_UW_FANS_OFF_AVAILABLE _IOR(MAGIC_READ_UW, 0x16, int32_t*) +#define R_UW_FANS_MIN_SPEED _IOR(MAGIC_READ_UW, 0x17, int32_t*) -#define R_UW_TDP0 _IOR(MAGIC_READ_UW, 0x16, int32_t*) -#define R_UW_TDP1 _IOR(MAGIC_READ_UW, 0x17, int32_t*) -#define R_UW_TDP2 _IOR(MAGIC_READ_UW, 0x18, int32_t*) -#define R_UW_TDP0_MIN _IOR(MAGIC_READ_UW, 0x19, int32_t*) -#define R_UW_TDP1_MIN _IOR(MAGIC_READ_UW, 0x1a, int32_t*) -#define R_UW_TDP2_MIN _IOR(MAGIC_READ_UW, 0x1b, int32_t*) -#define R_UW_TDP0_MAX _IOR(MAGIC_READ_UW, 0x1c, int32_t*) -#define R_UW_TDP1_MAX _IOR(MAGIC_READ_UW, 0x1d, int32_t*) -#define R_UW_TDP2_MAX _IOR(MAGIC_READ_UW, 0x1e, int32_t*) +#define R_UW_TDP0 _IOR(MAGIC_READ_UW, 0x18, int32_t*) +#define R_UW_TDP1 _IOR(MAGIC_READ_UW, 0x19, int32_t*) +#define R_UW_TDP2 _IOR(MAGIC_READ_UW, 0x1a, int32_t*) +#define R_UW_TDP0_MIN _IOR(MAGIC_READ_UW, 0x1b, int32_t*) +#define R_UW_TDP1_MIN _IOR(MAGIC_READ_UW, 0x1c, int32_t*) +#define R_UW_TDP2_MIN _IOR(MAGIC_READ_UW, 0x1d, int32_t*) +#define R_UW_TDP0_MAX _IOR(MAGIC_READ_UW, 0x1e, int32_t*) +#define R_UW_TDP1_MAX _IOR(MAGIC_READ_UW, 0x1f, int32_t*) +#define R_UW_TDP2_MAX _IOR(MAGIC_READ_UW, 0x20, int32_t*) -#define R_UW_PROFS_AVAILABLE _IOR(MAGIC_READ_UW, 0x1f, int32_t*) +#define R_UW_PROFS_AVAILABLE _IOR(MAGIC_READ_UW, 0x21, int32_t*) // Write #define W_UW_FANSPEED _IOW(MAGIC_WRITE_UW, 0x10, int32_t*) diff --git a/src/uniwill_interfaces.h b/src/uniwill_interfaces.h index 219c814..ae95e39 100644 --- a/src/uniwill_interfaces.h +++ b/src/uniwill_interfaces.h @@ -37,6 +37,7 @@ typedef u32 (uniwill_read_ec_ram_t)(u16, u8*); typedef u32 (uniwill_write_ec_ram_t)(u16, u8); +typedef u32 (uniwill_write_ec_ram_with_retry_t)(u16, u8, int); typedef void (uniwill_event_callb_t)(u32); struct uniwill_interface_t { @@ -73,6 +74,7 @@ u32 uniwill_add_interface(struct uniwill_interface_t *new_interface); u32 uniwill_remove_interface(struct uniwill_interface_t *interface); uniwill_read_ec_ram_t uniwill_read_ec_ram; uniwill_write_ec_ram_t uniwill_write_ec_ram; +uniwill_write_ec_ram_with_retry_t uniwill_write_ec_ram_with_retry; u32 uniwill_get_active_interface_id(char **id_str); struct uniwill_device_features_t *uniwill_get_device_features(void); diff --git a/src/uniwill_keyboard.h b/src/uniwill_keyboard.h index 547e505..2dd048a 100644 --- a/src/uniwill_keyboard.h +++ b/src/uniwill_keyboard.h @@ -126,6 +126,32 @@ u32 uniwill_write_ec_ram(u16 address, u8 data) } EXPORT_SYMBOL(uniwill_write_ec_ram); +u32 uniwill_write_ec_ram_with_retry(u16 address, u8 data, int retries) +{ + u32 status; + int i; + u8 control_data; + + for (i = 0; i < retries; ++i) { + status = uniwill_write_ec_ram(address, data); + if (status != 0) { + msleep(50); + continue; + } + else { + status = uniwill_read_ec_ram(address, &control_data); + if (status != 0 || data != control_data) { + msleep(50); + continue; + } + break; + } + } + + return status; +} +EXPORT_SYMBOL(uniwill_write_ec_ram_with_retry); + static DEFINE_MUTEX(uniwill_interface_modification_lock); u32 uniwill_add_interface(struct uniwill_interface_t *interface)