Skip to content

Commit c3bfcd4

Browse files
mrhpearsonjwrdegoede
authored andcommitted
platform/x86: thinkpad_acpi: Add platform profile support
Add support to thinkpad_acpi for Lenovo platforms that have DYTC version 5 support or newer to use the platform profile feature. This will allow users to determine and control the platform modes between low-power, balanced operation and performance modes. Signed-off-by: Mark Pearson <markpearson@lenovo.com> Reviewed-by: Hans de Goede <hdegoede@redhat.com> Link: https://lore.kernel.org/r/20210111162237.3469-1-markpearson@lenovo.com Signed-off-by: Hans de Goede <hdegoede@redhat.com>
1 parent effe55a commit c3bfcd4

File tree

1 file changed

+288
-6
lines changed

1 file changed

+288
-6
lines changed

drivers/platform/x86/thinkpad_acpi.c

Lines changed: 288 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
#include <linux/acpi.h>
6767
#include <linux/pci.h>
6868
#include <linux/power_supply.h>
69+
#include <linux/platform_profile.h>
6970
#include <sound/core.h>
7071
#include <sound/control.h>
7172
#include <sound/initval.h>
@@ -9855,16 +9856,27 @@ static bool has_lapsensor;
98559856
static bool palm_state;
98569857
static bool lap_state;
98579858

9858-
static int lapsensor_get(bool *present, bool *state)
9859+
static int dytc_command(int command, int *output)
98599860
{
98609861
acpi_handle dytc_handle;
9861-
int output;
98629862

9863-
*present = false;
9864-
if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle)))
9863+
if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) {
9864+
/* Platform doesn't support DYTC */
98659865
return -ENODEV;
9866-
if (!acpi_evalf(dytc_handle, &output, NULL, "dd", DYTC_CMD_GET))
9866+
}
9867+
if (!acpi_evalf(dytc_handle, output, NULL, "dd", command))
98679868
return -EIO;
9869+
return 0;
9870+
}
9871+
9872+
static int lapsensor_get(bool *present, bool *state)
9873+
{
9874+
int output, err;
9875+
9876+
*present = false;
9877+
err = dytc_command(DYTC_CMD_GET, &output);
9878+
if (err)
9879+
return err;
98689880

98699881
*present = true; /*If we get his far, we have lapmode support*/
98709882
*state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
@@ -9983,6 +9995,264 @@ static struct ibm_struct proxsensor_driver_data = {
99839995
.exit = proxsensor_exit,
99849996
};
99859997

9998+
#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
9999+
10000+
/*************************************************************************
10001+
* DYTC Platform Profile interface
10002+
*/
10003+
10004+
#define DYTC_CMD_QUERY 0 /* To get DYTC status - enable/revision */
10005+
#define DYTC_CMD_SET 1 /* To enable/disable IC function mode */
10006+
#define DYTC_CMD_RESET 0x1ff /* To reset back to default */
10007+
10008+
#define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */
10009+
#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */
10010+
#define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */
10011+
10012+
#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
10013+
#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */
10014+
10015+
#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */
10016+
#define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */
10017+
#define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */
10018+
10019+
#define DYTC_FUNCTION_STD 0 /* Function = 0, standard mode */
10020+
#define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */
10021+
#define DYTC_FUNCTION_MMC 11 /* Function = 11, desk mode */
10022+
10023+
#define DYTC_MODE_PERFORM 2 /* High power mode aka performance */
10024+
#define DYTC_MODE_LOWPOWER 3 /* Low power mode */
10025+
#define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */
10026+
10027+
#define DYTC_SET_COMMAND(function, mode, on) \
10028+
(DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \
10029+
(mode) << DYTC_SET_MODE_BIT | \
10030+
(on) << DYTC_SET_VALID_BIT)
10031+
10032+
#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0)
10033+
10034+
#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1)
10035+
10036+
static bool dytc_profile_available;
10037+
static enum platform_profile_option dytc_current_profile;
10038+
static atomic_t dytc_ignore_event = ATOMIC_INIT(0);
10039+
static DEFINE_MUTEX(dytc_mutex);
10040+
10041+
static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)
10042+
{
10043+
switch (dytcmode) {
10044+
case DYTC_MODE_LOWPOWER:
10045+
*profile = PLATFORM_PROFILE_LOW_POWER;
10046+
break;
10047+
case DYTC_MODE_BALANCE:
10048+
*profile = PLATFORM_PROFILE_BALANCED;
10049+
break;
10050+
case DYTC_MODE_PERFORM:
10051+
*profile = PLATFORM_PROFILE_PERFORMANCE;
10052+
break;
10053+
default: /* Unknown mode */
10054+
return -EINVAL;
10055+
}
10056+
return 0;
10057+
}
10058+
10059+
static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode)
10060+
{
10061+
switch (profile) {
10062+
case PLATFORM_PROFILE_LOW_POWER:
10063+
*perfmode = DYTC_MODE_LOWPOWER;
10064+
break;
10065+
case PLATFORM_PROFILE_BALANCED:
10066+
*perfmode = DYTC_MODE_BALANCE;
10067+
break;
10068+
case PLATFORM_PROFILE_PERFORMANCE:
10069+
*perfmode = DYTC_MODE_PERFORM;
10070+
break;
10071+
default: /* Unknown profile */
10072+
return -EOPNOTSUPP;
10073+
}
10074+
return 0;
10075+
}
10076+
10077+
/*
10078+
* dytc_profile_get: Function to register with platform_profile
10079+
* handler. Returns current platform profile.
10080+
*/
10081+
int dytc_profile_get(struct platform_profile_handler *pprof,
10082+
enum platform_profile_option *profile)
10083+
{
10084+
*profile = dytc_current_profile;
10085+
return 0;
10086+
}
10087+
10088+
/*
10089+
* Helper function - check if we are in CQL mode and if we are
10090+
* - disable CQL,
10091+
* - run the command
10092+
* - enable CQL
10093+
* If not in CQL mode, just run the command
10094+
*/
10095+
int dytc_cql_command(int command, int *output)
10096+
{
10097+
int err, cmd_err, dummy;
10098+
int cur_funcmode;
10099+
10100+
/* Determine if we are in CQL mode. This alters the commands we do */
10101+
err = dytc_command(DYTC_CMD_GET, output);
10102+
if (err)
10103+
return err;
10104+
10105+
cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF;
10106+
/* Check if we're OK to return immediately */
10107+
if ((command == DYTC_CMD_GET) && (cur_funcmode != DYTC_FUNCTION_CQL))
10108+
return 0;
10109+
10110+
if (cur_funcmode == DYTC_FUNCTION_CQL) {
10111+
atomic_inc(&dytc_ignore_event);
10112+
err = dytc_command(DYTC_DISABLE_CQL, &dummy);
10113+
if (err)
10114+
return err;
10115+
}
10116+
10117+
cmd_err = dytc_command(command, output);
10118+
/* Check return condition after we've restored CQL state */
10119+
10120+
if (cur_funcmode == DYTC_FUNCTION_CQL) {
10121+
err = dytc_command(DYTC_ENABLE_CQL, &dummy);
10122+
if (err)
10123+
return err;
10124+
}
10125+
10126+
return cmd_err;
10127+
}
10128+
10129+
/*
10130+
* dytc_profile_set: Function to register with platform_profile
10131+
* handler. Sets current platform profile.
10132+
*/
10133+
int dytc_profile_set(struct platform_profile_handler *pprof,
10134+
enum platform_profile_option profile)
10135+
{
10136+
int output;
10137+
int err;
10138+
10139+
if (!dytc_profile_available)
10140+
return -ENODEV;
10141+
10142+
err = mutex_lock_interruptible(&dytc_mutex);
10143+
if (err)
10144+
return err;
10145+
10146+
if (profile == PLATFORM_PROFILE_BALANCED) {
10147+
/* To get back to balanced mode we just issue a reset command */
10148+
err = dytc_command(DYTC_CMD_RESET, &output);
10149+
if (err)
10150+
goto unlock;
10151+
} else {
10152+
int perfmode;
10153+
10154+
err = convert_profile_to_dytc(profile, &perfmode);
10155+
if (err)
10156+
goto unlock;
10157+
10158+
/* Determine if we are in CQL mode. This alters the commands we do */
10159+
err = dytc_cql_command(DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1), &output);
10160+
if (err)
10161+
goto unlock;
10162+
}
10163+
/* Success - update current profile */
10164+
dytc_current_profile = profile;
10165+
unlock:
10166+
mutex_unlock(&dytc_mutex);
10167+
return err;
10168+
}
10169+
10170+
static void dytc_profile_refresh(void)
10171+
{
10172+
enum platform_profile_option profile;
10173+
int output, err;
10174+
int perfmode;
10175+
10176+
mutex_lock(&dytc_mutex);
10177+
err = dytc_cql_command(DYTC_CMD_GET, &output);
10178+
mutex_unlock(&dytc_mutex);
10179+
if (err)
10180+
return;
10181+
10182+
perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF;
10183+
convert_dytc_to_profile(perfmode, &profile);
10184+
if (profile != dytc_current_profile) {
10185+
dytc_current_profile = profile;
10186+
platform_profile_notify();
10187+
}
10188+
}
10189+
10190+
static struct platform_profile_handler dytc_profile = {
10191+
.profile_get = dytc_profile_get,
10192+
.profile_set = dytc_profile_set,
10193+
};
10194+
10195+
static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
10196+
{
10197+
int err, output;
10198+
10199+
/* Setup supported modes */
10200+
set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices);
10201+
set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices);
10202+
set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices);
10203+
10204+
dytc_profile_available = false;
10205+
err = dytc_command(DYTC_CMD_QUERY, &output);
10206+
/*
10207+
* If support isn't available (ENODEV) then don't return an error
10208+
* and don't create the sysfs group
10209+
*/
10210+
if (err == -ENODEV)
10211+
return 0;
10212+
/* For all other errors we can flag the failure */
10213+
if (err)
10214+
return err;
10215+
10216+
/* Check DYTC is enabled and supports mode setting */
10217+
if (output & BIT(DYTC_QUERY_ENABLE_BIT)) {
10218+
/* Only DYTC v5.0 and later has this feature. */
10219+
int dytc_version;
10220+
10221+
dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
10222+
if (dytc_version >= 5) {
10223+
dbg_printk(TPACPI_DBG_INIT,
10224+
"DYTC version %d: thermal mode available\n", dytc_version);
10225+
/* Create platform_profile structure and register */
10226+
err = platform_profile_register(&dytc_profile);
10227+
/*
10228+
* If for some reason platform_profiles aren't enabled
10229+
* don't quit terminally.
10230+
*/
10231+
if (err)
10232+
return 0;
10233+
10234+
dytc_profile_available = true;
10235+
/* Ensure initial values are correct */
10236+
dytc_profile_refresh();
10237+
}
10238+
}
10239+
return 0;
10240+
}
10241+
10242+
static void dytc_profile_exit(void)
10243+
{
10244+
if (dytc_profile_available) {
10245+
dytc_profile_available = false;
10246+
platform_profile_remove();
10247+
}
10248+
}
10249+
10250+
static struct ibm_struct dytc_profile_driver_data = {
10251+
.name = "dytc-profile",
10252+
.exit = dytc_profile_exit,
10253+
};
10254+
#endif /* CONFIG_ACPI_PLATFORM_PROFILE */
10255+
998610256
/*************************************************************************
998710257
* Keyboard language interface
998810258
*/
@@ -10204,8 +10474,14 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
1020410474
mutex_unlock(&kbdlight_mutex);
1020510475
}
1020610476

10207-
if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED)
10477+
if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) {
1020810478
lapsensor_refresh();
10479+
#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
10480+
/* If we are already accessing DYTC then skip dytc update */
10481+
if (!atomic_add_unless(&dytc_ignore_event, -1, 0))
10482+
dytc_profile_refresh();
10483+
#endif
10484+
}
1020910485
}
1021010486

1021110487
static void hotkey_driver_event(const unsigned int scancode)
@@ -10648,6 +10924,12 @@ static struct ibm_init_struct ibms_init[] __initdata = {
1064810924
.init = tpacpi_proxsensor_init,
1064910925
.data = &proxsensor_driver_data,
1065010926
},
10927+
#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
10928+
{
10929+
.init = tpacpi_dytc_profile_init,
10930+
.data = &dytc_profile_driver_data,
10931+
},
10932+
#endif
1065110933
{
1065210934
.init = tpacpi_kbdlang_init,
1065310935
.data = &kbdlang_driver_data,

0 commit comments

Comments
 (0)