brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 972385c Raw
310 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  Driver for Dell laptop extras4 *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 ": " fmt12 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 25static 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};40MODULE_DEVICE_TABLE(dmi, dell_device_table);41 42/* Derived from smbios-thermal-ctl43 *44 * cbClass 1745 * cbSelect 1946 * User Selectable Thermal Tables(USTT)47 * cbArg1 determines the function to be performed48 * cbArg1 0x0 = Get Thermal Information49 *  cbRES1         Standard return codes (0, -1, -2)50 *  cbRES2, byte 0  Bitmap of supported thermal modes. A mode is supported if51 *                  its bit is set to 152 *     Bit 0 Balanced53 *     Bit 1 Cool Bottom54 *     Bit 2 Quiet55 *     Bit 3 Performance56 *  cbRES2, byte 1 Bitmap of supported Active Acoustic Controller (AAC) modes.57 *                 Each mode corresponds to the supported thermal modes in58 *                  byte 0. A mode is supported if its bit is set to 1.59 *     Bit 0 AAC (Balanced)60 *     Bit 1 AAC (Cool Bottom61 *     Bit 2 AAC (Quiet)62 *     Bit 3 AAC (Performance)63 *  cbRes3, byte 0 Current Thermal Mode64 *     Bit 0 Balanced65 *     Bit 1 Cool Bottom66 *     Bit 2 Quiet67 *     Bit 3 Performanc68 *  cbRes3, byte 1  AAC Configuration type69 *          0       Global (AAC enable/disable applies to all supported USTT modes)70 *          1       USTT mode specific71 *  cbRes3, byte 2  Current Active Acoustic Controller (AAC) Mode72 *     If AAC Configuration Type is Global,73 *          0       AAC mode disabled74 *          1       AAC mode enabled75 *     If AAC Configuration Type is USTT mode specific (multiple bits may be set),76 *          Bit 0 AAC (Balanced)77 *          Bit 1 AAC (Cool Bottom78 *          Bit 2 AAC (Quiet)79 *          Bit 3 AAC (Performance)80 *  cbRes3, byte 3  Current Fan Failure Mode81 *     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 and85 *               desired AAC mode shall be applied86 * cbArg2, byte 0  Desired Thermal Mode to set87 *                  (only one bit may be set for this parameter)88 *     Bit 0 Balanced89 *     Bit 1 Cool Bottom90 *     Bit 2 Quiet91 *     Bit 3 Performance92 * cbArg2, byte 1  Desired Active Acoustic Controller (AAC) Mode to set93 *     If AAC Configuration Type is Global,94 *         0  AAC mode disabled95 *         1  AAC mode enabled96 *     If AAC Configuration Type is USTT mode specific97 *     (multiple bits may be set for this parameter),98 *         Bit 0 AAC (Balanced)99 *         Bit 1 AAC (Cool Bottom100 *         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 108static struct platform_profile_handler *thermal_handler;109 110enum 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 117static 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	else137		return -ENXIO;138}139 140static 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 158static 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 171static 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 185static 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 202static 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 231static 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 272static void thermal_cleanup(void)273{274	if (thermal_handler) {275		platform_profile_remove();276		kfree(thermal_handler);277	}278}279 280static 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 294fail_thermal:295	thermal_cleanup();296	return ret;297}298 299static void __exit dell_exit(void)300{301	thermal_cleanup();302}303 304module_init(dell_init);305module_exit(dell_exit);306 307MODULE_AUTHOR("Lyndon Sanche <lsanche@lyndeno.ca>");308MODULE_DESCRIPTION("Dell PC driver");309MODULE_LICENSE("GPL");310