1 // SPDX-License-Identifier: GPL-2.0+
3 * Surface Platform Profile / Performance Mode driver for Surface System
4 * Aggregator Module (thermal and fan subsystem).
6 * Copyright (C) 2021-2022 Maximilian Luz <luzmaximilian@gmail.com>
9 #include <linux/unaligned.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/platform_profile.h>
13 #include <linux/types.h>
15 #include <linux/surface_aggregator/device.h>
17 // Enum for the platform performance profile sent to the TMP module.
18 enum ssam_tmp_profile
{
19 SSAM_TMP_PROFILE_NORMAL
= 1,
20 SSAM_TMP_PROFILE_BATTERY_SAVER
= 2,
21 SSAM_TMP_PROFILE_BETTER_PERFORMANCE
= 3,
22 SSAM_TMP_PROFILE_BEST_PERFORMANCE
= 4,
25 // Enum for the fan profile sent to the FAN module. This fan profile is
26 // only sent to the EC if the 'has_fan' property is set. The integers are
27 // not a typo, they differ from the performance profile indices.
28 enum ssam_fan_profile
{
29 SSAM_FAN_PROFILE_NORMAL
= 2,
30 SSAM_FAN_PROFILE_BATTERY_SAVER
= 1,
31 SSAM_FAN_PROFILE_BETTER_PERFORMANCE
= 3,
32 SSAM_FAN_PROFILE_BEST_PERFORMANCE
= 4,
35 struct ssam_tmp_profile_info
{
41 struct ssam_platform_profile_device
{
42 struct ssam_device
*sdev
;
43 struct platform_profile_handler handler
;
47 SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get
, struct ssam_tmp_profile_info
, {
48 .target_category
= SSAM_SSH_TC_TMP
,
52 SSAM_DEFINE_SYNC_REQUEST_CL_W(__ssam_tmp_profile_set
, __le32
, {
53 .target_category
= SSAM_SSH_TC_TMP
,
57 SSAM_DEFINE_SYNC_REQUEST_W(__ssam_fan_profile_set
, u8
, {
58 .target_category
= SSAM_SSH_TC_FAN
,
59 .target_id
= SSAM_SSH_TID_SAM
,
64 static int ssam_tmp_profile_get(struct ssam_device
*sdev
, enum ssam_tmp_profile
*p
)
66 struct ssam_tmp_profile_info info
;
69 status
= ssam_retry(__ssam_tmp_profile_get
, sdev
, &info
);
73 *p
= le32_to_cpu(info
.profile
);
77 static int ssam_tmp_profile_set(struct ssam_device
*sdev
, enum ssam_tmp_profile p
)
79 const __le32 profile_le
= cpu_to_le32(p
);
81 return ssam_retry(__ssam_tmp_profile_set
, sdev
, &profile_le
);
84 static int ssam_fan_profile_set(struct ssam_device
*sdev
, enum ssam_fan_profile p
)
88 return ssam_retry(__ssam_fan_profile_set
, sdev
->ctrl
, &profile
);
91 static int convert_ssam_tmp_to_profile(struct ssam_device
*sdev
, enum ssam_tmp_profile p
)
94 case SSAM_TMP_PROFILE_NORMAL
:
95 return PLATFORM_PROFILE_BALANCED
;
97 case SSAM_TMP_PROFILE_BATTERY_SAVER
:
98 return PLATFORM_PROFILE_LOW_POWER
;
100 case SSAM_TMP_PROFILE_BETTER_PERFORMANCE
:
101 return PLATFORM_PROFILE_BALANCED_PERFORMANCE
;
103 case SSAM_TMP_PROFILE_BEST_PERFORMANCE
:
104 return PLATFORM_PROFILE_PERFORMANCE
;
107 dev_err(&sdev
->dev
, "invalid performance profile: %d", p
);
113 static int convert_profile_to_ssam_tmp(struct ssam_device
*sdev
, enum platform_profile_option p
)
116 case PLATFORM_PROFILE_LOW_POWER
:
117 return SSAM_TMP_PROFILE_BATTERY_SAVER
;
119 case PLATFORM_PROFILE_BALANCED
:
120 return SSAM_TMP_PROFILE_NORMAL
;
122 case PLATFORM_PROFILE_BALANCED_PERFORMANCE
:
123 return SSAM_TMP_PROFILE_BETTER_PERFORMANCE
;
125 case PLATFORM_PROFILE_PERFORMANCE
:
126 return SSAM_TMP_PROFILE_BEST_PERFORMANCE
;
129 /* This should have already been caught by platform_profile_store(). */
130 WARN(true, "unsupported platform profile");
135 static int convert_profile_to_ssam_fan(struct ssam_device
*sdev
, enum platform_profile_option p
)
138 case PLATFORM_PROFILE_LOW_POWER
:
139 return SSAM_FAN_PROFILE_BATTERY_SAVER
;
141 case PLATFORM_PROFILE_BALANCED
:
142 return SSAM_FAN_PROFILE_NORMAL
;
144 case PLATFORM_PROFILE_BALANCED_PERFORMANCE
:
145 return SSAM_FAN_PROFILE_BETTER_PERFORMANCE
;
147 case PLATFORM_PROFILE_PERFORMANCE
:
148 return SSAM_FAN_PROFILE_BEST_PERFORMANCE
;
151 /* This should have already been caught by platform_profile_store(). */
152 WARN(true, "unsupported platform profile");
157 static int ssam_platform_profile_get(struct platform_profile_handler
*pprof
,
158 enum platform_profile_option
*profile
)
160 struct ssam_platform_profile_device
*tpd
;
161 enum ssam_tmp_profile tp
;
164 tpd
= container_of(pprof
, struct ssam_platform_profile_device
, handler
);
166 status
= ssam_tmp_profile_get(tpd
->sdev
, &tp
);
170 status
= convert_ssam_tmp_to_profile(tpd
->sdev
, tp
);
178 static int ssam_platform_profile_set(struct platform_profile_handler
*pprof
,
179 enum platform_profile_option profile
)
181 struct ssam_platform_profile_device
*tpd
;
184 tpd
= container_of(pprof
, struct ssam_platform_profile_device
, handler
);
186 tp
= convert_profile_to_ssam_tmp(tpd
->sdev
, profile
);
190 tp
= ssam_tmp_profile_set(tpd
->sdev
, tp
);
195 tp
= convert_profile_to_ssam_fan(tpd
->sdev
, profile
);
198 tp
= ssam_fan_profile_set(tpd
->sdev
, tp
);
204 static int surface_platform_profile_probe(struct ssam_device
*sdev
)
206 struct ssam_platform_profile_device
*tpd
;
208 tpd
= devm_kzalloc(&sdev
->dev
, sizeof(*tpd
), GFP_KERNEL
);
214 tpd
->handler
.profile_get
= ssam_platform_profile_get
;
215 tpd
->handler
.profile_set
= ssam_platform_profile_set
;
217 tpd
->has_fan
= device_property_read_bool(&sdev
->dev
, "has_fan");
219 set_bit(PLATFORM_PROFILE_LOW_POWER
, tpd
->handler
.choices
);
220 set_bit(PLATFORM_PROFILE_BALANCED
, tpd
->handler
.choices
);
221 set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE
, tpd
->handler
.choices
);
222 set_bit(PLATFORM_PROFILE_PERFORMANCE
, tpd
->handler
.choices
);
224 return platform_profile_register(&tpd
->handler
);
227 static void surface_platform_profile_remove(struct ssam_device
*sdev
)
229 platform_profile_remove();
232 static const struct ssam_device_id ssam_platform_profile_match
[] = {
233 { SSAM_SDEV(TMP
, SAM
, 0x00, 0x01) },
236 MODULE_DEVICE_TABLE(ssam
, ssam_platform_profile_match
);
238 static struct ssam_device_driver surface_platform_profile
= {
239 .probe
= surface_platform_profile_probe
,
240 .remove
= surface_platform_profile_remove
,
241 .match_table
= ssam_platform_profile_match
,
243 .name
= "surface_platform_profile",
244 .probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
247 module_ssam_device_driver(surface_platform_profile
);
249 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
250 MODULE_DESCRIPTION("Platform Profile Support for Surface System Aggregator Module");
251 MODULE_LICENSE("GPL");