1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Driver for Dell laptop extras 4 * 5 * Copyright (c) Lyndon Sanche <lsanche@lyndeno.ca> 6 * 7 * Based on documentation in the libsmbios package: 8 * Copyright (C) 2005-2014 Dell Inc. 9 */ 10 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13 #include <linux/bitfield.h> 14 #include <linux/bits.h> 15 #include <linux/dmi.h> 16 #include <linux/err.h> 17 #include <linux/init.h> 18 #include <linux/kernel.h> 19 #include <linux/module.h> 20 #include <linux/platform_profile.h> 21 #include <linux/slab.h> 22 23 #include "dell-smbios.h" 24 25 static const struct dmi_system_id dell_device_table[] __initconst = { 26 { 27 .ident = "Dell Inc.", 28 .matches = { 29 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 30 }, 31 }, 32 { 33 .ident = "Dell Computer Corporation", 34 .matches = { 35 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 36 }, 37 }, 38 { } 39 }; 40 MODULE_DEVICE_TABLE(dmi, dell_device_table); 41 42 /* Derived from smbios-thermal-ctl 43 * 44 * cbClass 17 45 * cbSelect 19 46 * User Selectable Thermal Tables(USTT) 47 * cbArg1 determines the function to be performed 48 * cbArg1 0x0 = Get Thermal Information 49 * cbRES1 Standard return codes (0, -1, -2) 50 * cbRES2, byte 0 Bitmap of supported thermal modes. A mode is supported if 51 * its bit is set to 1 52 * Bit 0 Balanced 53 * Bit 1 Cool Bottom 54 * Bit 2 Quiet 55 * Bit 3 Performance 56 * cbRES2, byte 1 Bitmap of supported Active Acoustic Controller (AAC) modes. 57 * Each mode corresponds to the supported thermal modes in 58 * byte 0. A mode is supported if its bit is set to 1. 59 * Bit 0 AAC (Balanced) 60 * Bit 1 AAC (Cool Bottom 61 * Bit 2 AAC (Quiet) 62 * Bit 3 AAC (Performance) 63 * cbRes3, byte 0 Current Thermal Mode 64 * Bit 0 Balanced 65 * Bit 1 Cool Bottom 66 * Bit 2 Quiet 67 * Bit 3 Performanc 68 * cbRes3, byte 1 AAC Configuration type 69 * 0 Global (AAC enable/disable applies to all supported USTT modes) 70 * 1 USTT mode specific 71 * cbRes3, byte 2 Current Active Acoustic Controller (AAC) Mode 72 * If AAC Configuration Type is Global, 73 * 0 AAC mode disabled 74 * 1 AAC mode enabled 75 * If AAC Configuration Type is USTT mode specific (multiple bits may be set), 76 * Bit 0 AAC (Balanced) 77 * Bit 1 AAC (Cool Bottom 78 * Bit 2 AAC (Quiet) 79 * Bit 3 AAC (Performance) 80 * cbRes3, byte 3 Current Fan Failure Mode 81 * Bit 0 Minimal Fan Failure (at least one fan has failed, one fan working) 82 * Bit 1 Catastrophic Fan Failure (all fans have failed) 83 * 84 * cbArg1 0x1 (Set Thermal Information), both desired thermal mode and 85 * desired AAC mode shall be applied 86 * cbArg2, byte 0 Desired Thermal Mode to set 87 * (only one bit may be set for this parameter) 88 * Bit 0 Balanced 89 * Bit 1 Cool Bottom 90 * Bit 2 Quiet 91 * Bit 3 Performance 92 * cbArg2, byte 1 Desired Active Acoustic Controller (AAC) Mode to set 93 * If AAC Configuration Type is Global, 94 * 0 AAC mode disabled 95 * 1 AAC mode enabled 96 * If AAC Configuration Type is USTT mode specific 97 * (multiple bits may be set for this parameter), 98 * Bit 0 AAC (Balanced) 99 * Bit 1 AAC (Cool Bottom 100 * Bit 2 AAC (Quiet) 101 * Bit 3 AAC (Performance) 102 */ 103 104 #define DELL_ACC_GET_FIELD GENMASK(19, 16) 105 #define DELL_ACC_SET_FIELD GENMASK(11, 8) 106 #define DELL_THERMAL_SUPPORTED GENMASK(3, 0) 107 108 static struct platform_profile_handler *thermal_handler; 109 110 enum thermal_mode_bits { 111 DELL_BALANCED = BIT(0), 112 DELL_COOL_BOTTOM = BIT(1), 113 DELL_QUIET = BIT(2), 114 DELL_PERFORMANCE = BIT(3), 115 }; 116 117 static int thermal_get_mode(void) 118 { 119 struct calling_interface_buffer buffer; 120 int state; 121 int ret; 122 123 dell_fill_request(&buffer, 0x0, 0, 0, 0); 124 ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); 125 if (ret) 126 return ret; 127 state = buffer.output[2]; 128 if (state & DELL_BALANCED) 129 return DELL_BALANCED; 130 else if (state & DELL_COOL_BOTTOM) 131 return DELL_COOL_BOTTOM; 132 else if (state & DELL_QUIET) 133 return DELL_QUIET; 134 else if (state & DELL_PERFORMANCE) 135 return DELL_PERFORMANCE; 136 else 137 return -ENXIO; 138 } 139 140 static int thermal_get_supported_modes(int *supported_bits) 141 { 142 struct calling_interface_buffer buffer; 143 int ret; 144 145 dell_fill_request(&buffer, 0x0, 0, 0, 0); 146 ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); 147 /* Thermal function not supported */ 148 if (ret == -ENXIO) { 149 *supported_bits = 0; 150 return 0; 151 } 152 if (ret) 153 return ret; 154 *supported_bits = FIELD_GET(DELL_THERMAL_SUPPORTED, buffer.output[1]); 155 return 0; 156 } 157 158 static int thermal_get_acc_mode(int *acc_mode) 159 { 160 struct calling_interface_buffer buffer; 161 int ret; 162 163 dell_fill_request(&buffer, 0x0, 0, 0, 0); 164 ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); 165 if (ret) 166 return ret; 167 *acc_mode = FIELD_GET(DELL_ACC_GET_FIELD, buffer.output[3]); 168 return 0; 169 } 170 171 static int thermal_set_mode(enum thermal_mode_bits state) 172 { 173 struct calling_interface_buffer buffer; 174 int ret; 175 int acc_mode; 176 177 ret = thermal_get_acc_mode(&acc_mode); 178 if (ret) 179 return ret; 180 181 dell_fill_request(&buffer, 0x1, FIELD_PREP(DELL_ACC_SET_FIELD, acc_mode) | state, 0, 0); 182 return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); 183 } 184 185 static int thermal_platform_profile_set(struct platform_profile_handler *pprof, 186 enum platform_profile_option profile) 187 { 188 switch (profile) { 189 case PLATFORM_PROFILE_BALANCED: 190 return thermal_set_mode(DELL_BALANCED); 191 case PLATFORM_PROFILE_PERFORMANCE: 192 return thermal_set_mode(DELL_PERFORMANCE); 193 case PLATFORM_PROFILE_QUIET: 194 return thermal_set_mode(DELL_QUIET); 195 case PLATFORM_PROFILE_COOL: 196 return thermal_set_mode(DELL_COOL_BOTTOM); 197 default: 198 return -EOPNOTSUPP; 199 } 200 } 201 202 static int thermal_platform_profile_get(struct platform_profile_handler *pprof, 203 enum platform_profile_option *profile) 204 { 205 int ret; 206 207 ret = thermal_get_mode(); 208 if (ret < 0) 209 return ret; 210 211 switch (ret) { 212 case DELL_BALANCED: 213 *profile = PLATFORM_PROFILE_BALANCED; 214 break; 215 case DELL_PERFORMANCE: 216 *profile = PLATFORM_PROFILE_PERFORMANCE; 217 break; 218 case DELL_COOL_BOTTOM: 219 *profile = PLATFORM_PROFILE_COOL; 220 break; 221 case DELL_QUIET: 222 *profile = PLATFORM_PROFILE_QUIET; 223 break; 224 default: 225 return -EINVAL; 226 } 227 228 return 0; 229 } 230 231 static int thermal_init(void) 232 { 233 int ret; 234 int supported_modes; 235 236 /* If thermal commands are not supported, exit without error */ 237 if (!dell_smbios_class_is_supported(CLASS_INFO)) 238 return 0; 239 240 /* If thermal modes are not supported, exit without error */ 241 ret = thermal_get_supported_modes(&supported_modes); 242 if (ret < 0) 243 return ret; 244 if (!supported_modes) 245 return 0; 246 247 thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL); 248 if (!thermal_handler) 249 return -ENOMEM; 250 thermal_handler->profile_get = thermal_platform_profile_get; 251 thermal_handler->profile_set = thermal_platform_profile_set; 252 253 if (supported_modes & DELL_QUIET) 254 set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices); 255 if (supported_modes & DELL_COOL_BOTTOM) 256 set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices); 257 if (supported_modes & DELL_BALANCED) 258 set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices); 259 if (supported_modes & DELL_PERFORMANCE) 260 set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices); 261 262 /* Clean up if failed */ 263 ret = platform_profile_register(thermal_handler); 264 if (ret) { 265 kfree(thermal_handler); 266 thermal_handler = NULL; 267 } 268 269 return ret; 270 } 271 272 static void thermal_cleanup(void) 273 { 274 if (thermal_handler) { 275 platform_profile_remove(); 276 kfree(thermal_handler); 277 } 278 } 279 280 static int __init dell_init(void) 281 { 282 int ret; 283 284 if (!dmi_check_system(dell_device_table)) 285 return -ENODEV; 286 287 /* Do not fail module if thermal modes not supported, just skip */ 288 ret = thermal_init(); 289 if (ret) 290 goto fail_thermal; 291 292 return 0; 293 294 fail_thermal: 295 thermal_cleanup(); 296 return ret; 297 } 298 299 static void __exit dell_exit(void) 300 { 301 thermal_cleanup(); 302 } 303 304 module_init(dell_init); 305 module_exit(dell_exit); 306 307 MODULE_AUTHOR("Lyndon Sanche <lsanche@lyndeno.ca>"); 308 MODULE_DESCRIPTION("Dell PC driver"); 309 MODULE_LICENSE("GPL"); 310