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