Skip to content

Commit 4c71ae4

Browse files
Shyam Sundar S Kjwrdegoede
Shyam Sundar S K
authored andcommitted
platform/x86/amd/pmf: Add support SPS PMF feature
SPS (a.k.a. Static Power Slider) gives a feel of Windows performance power slider for the Linux users, where the user selects a certain mode (like "balanced", "low-power" or "performance") and the thermals associated with each selected mode gets applied from the silicon side via the mailboxes defined through PMFW. PMF driver hooks to platform_profile by reading the PMF ACPI fn9 to see if the support is being advertised by ACPI interface. If supported, the PMF driver reacts to platform_profile selection choices made by the user and adjust the system thermal behavior. Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Shyam Sundar S K <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Hans de Goede <[email protected]>
1 parent 5eb315e commit 4c71ae4

File tree

5 files changed

+243
-1
lines changed

5 files changed

+243
-1
lines changed

drivers/platform/x86/amd/pmf/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
#
66

77
obj-$(CONFIG_AMD_PMF) += amd-pmf.o
8-
amd-pmf-objs := core.o acpi.o
8+
amd-pmf-objs := core.o acpi.o sps.o

drivers/platform/x86/amd/pmf/acpi.c

+10
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
9393
return !!(pdev->supported_func & BIT(index - 1));
9494
}
9595

96+
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
97+
struct apmf_static_slider_granular_output *data)
98+
{
99+
if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
100+
return -EINVAL;
101+
102+
return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR,
103+
data, sizeof(*data));
104+
}
105+
96106
static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
97107
{
98108
struct apmf_verify_interface output;

drivers/platform/x86/amd/pmf/core.c

+26
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/module.h>
1313
#include <linux/pci.h>
1414
#include <linux/platform_device.h>
15+
#include <linux/power_supply.h>
1516
#include "pmf.h"
1617

1718
/* PMF-SMU communication registers */
@@ -45,6 +46,14 @@
4546
#define DELAY_MIN_US 2000
4647
#define DELAY_MAX_US 3000
4748

49+
int amd_pmf_get_power_source(void)
50+
{
51+
if (power_supply_is_system_supplied() > 0)
52+
return POWER_SOURCE_AC;
53+
else
54+
return POWER_SOURCE_DC;
55+
}
56+
4857
static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
4958
{
5059
return ioread32(dev->regbase + reg_offset);
@@ -138,6 +147,21 @@ static const struct pci_device_id pmf_pci_ids[] = {
138147
{ }
139148
};
140149

150+
static void amd_pmf_init_features(struct amd_pmf_dev *dev)
151+
{
152+
/* Enable Static Slider */
153+
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
154+
amd_pmf_init_sps(dev);
155+
dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
156+
}
157+
}
158+
159+
static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
160+
{
161+
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
162+
amd_pmf_deinit_sps(dev);
163+
}
164+
141165
static const struct acpi_device_id amd_pmf_acpi_ids[] = {
142166
{"AMDI0102", 0},
143167
{ }
@@ -206,6 +230,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
206230

207231
apmf_acpi_init(dev);
208232
platform_set_drvdata(pdev, dev);
233+
amd_pmf_init_features(dev);
209234

210235
mutex_init(&dev->lock);
211236
dev_info(dev->dev, "registered PMF device successfully\n");
@@ -218,6 +243,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
218243
struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
219244

220245
mutex_destroy(&dev->lock);
246+
amd_pmf_deinit_features(dev);
221247
kfree(dev->buf);
222248
return 0;
223249
}

drivers/platform/x86/amd/pmf/pmf.h

+63
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
#define PMF_H
1313

1414
#include <linux/acpi.h>
15+
#include <linux/platform_profile.h>
1516

1617
/* APMF Functions */
1718
#define APMF_FUNC_VERIFY_INTERFACE 0
1819
#define APMF_FUNC_GET_SYS_PARAMS 1
20+
#define APMF_FUNC_STATIC_SLIDER_GRANULAR 9
1921

2022
/* Message Definitions */
2123
#define SET_SPL 0x03 /* SPL: Sustained Power Limit */
@@ -36,6 +38,8 @@
3638
#define GET_STT_LIMIT_APU 0x20
3739
#define GET_STT_LIMIT_HS2 0x21
3840

41+
#define ARG_NONE 0
42+
3943
/* AMD PMF BIOS interfaces */
4044
struct apmf_verify_interface {
4145
u16 size;
@@ -51,6 +55,30 @@ struct apmf_system_params {
5155
u8 command_code;
5256
} __packed;
5357

58+
enum amd_stt_skin_temp {
59+
STT_TEMP_APU,
60+
STT_TEMP_HS2,
61+
STT_TEMP_COUNT,
62+
};
63+
64+
enum amd_slider_op {
65+
SLIDER_OP_GET,
66+
SLIDER_OP_SET,
67+
};
68+
69+
enum power_source {
70+
POWER_SOURCE_AC,
71+
POWER_SOURCE_DC,
72+
POWER_SOURCE_MAX,
73+
};
74+
75+
enum power_modes {
76+
POWER_MODE_PERFORMANCE,
77+
POWER_MODE_BALANCED_POWER,
78+
POWER_MODE_POWER_SAVER,
79+
POWER_MODE_MAX,
80+
};
81+
5482
struct amd_pmf_dev {
5583
void __iomem *regbase;
5684
void __iomem *smu_virt_addr;
@@ -60,10 +88,45 @@ struct amd_pmf_dev {
6088
struct device *dev;
6189
struct mutex lock; /* protects the PMF interface */
6290
u32 supported_func;
91+
enum platform_profile_option current_profile;
92+
struct platform_profile_handler pprof;
93+
};
94+
95+
struct apmf_sps_prop_granular {
96+
u32 fppt;
97+
u32 sppt;
98+
u32 sppt_apu_only;
99+
u32 spl;
100+
u32 stt_min;
101+
u8 stt_skin_temp[STT_TEMP_COUNT];
102+
u32 fan_id;
103+
} __packed;
104+
105+
/* Static Slider */
106+
struct apmf_static_slider_granular_output {
107+
u16 size;
108+
struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX * POWER_MODE_MAX];
109+
} __packed;
110+
111+
struct amd_pmf_static_slider_granular {
112+
u16 size;
113+
struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
63114
};
64115

65116
/* Core Layer */
66117
int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
118+
int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index);
67119
int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
120+
int amd_pmf_get_power_source(void);
121+
122+
/* SPS Layer */
123+
u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
124+
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
125+
struct amd_pmf_static_slider_granular *table);
126+
int amd_pmf_init_sps(struct amd_pmf_dev *dev);
127+
void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
128+
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
129+
struct apmf_static_slider_granular_output *output);
130+
68131

69132
#endif /* PMF_H */

drivers/platform/x86/amd/pmf/sps.c

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* AMD Platform Management Framework (PMF) Driver
4+
*
5+
* Copyright (c) 2022, Advanced Micro Devices, Inc.
6+
* All Rights Reserved.
7+
*
8+
* Author: Shyam Sundar S K <[email protected]>
9+
*/
10+
11+
#include "pmf.h"
12+
13+
static struct amd_pmf_static_slider_granular config_store;
14+
15+
static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
16+
{
17+
struct apmf_static_slider_granular_output output;
18+
int i, j, idx = 0;
19+
20+
memset(&config_store, 0, sizeof(config_store));
21+
apmf_get_static_slider_granular(dev, &output);
22+
23+
for (i = 0; i < POWER_SOURCE_MAX; i++) {
24+
for (j = 0; j < POWER_MODE_MAX; j++) {
25+
config_store.prop[i][j].spl = output.prop[idx].spl;
26+
config_store.prop[i][j].sppt = output.prop[idx].sppt;
27+
config_store.prop[i][j].sppt_apu_only =
28+
output.prop[idx].sppt_apu_only;
29+
config_store.prop[i][j].fppt = output.prop[idx].fppt;
30+
config_store.prop[i][j].stt_min = output.prop[idx].stt_min;
31+
config_store.prop[i][j].stt_skin_temp[STT_TEMP_APU] =
32+
output.prop[idx].stt_skin_temp[STT_TEMP_APU];
33+
config_store.prop[i][j].stt_skin_temp[STT_TEMP_HS2] =
34+
output.prop[idx].stt_skin_temp[STT_TEMP_HS2];
35+
config_store.prop[i][j].fan_id = output.prop[idx].fan_id;
36+
idx++;
37+
}
38+
}
39+
}
40+
41+
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
42+
struct amd_pmf_static_slider_granular *table)
43+
{
44+
int src = amd_pmf_get_power_source();
45+
46+
if (op == SLIDER_OP_SET) {
47+
amd_pmf_send_cmd(dev, SET_SPL, false, config_store.prop[src][idx].spl, NULL);
48+
amd_pmf_send_cmd(dev, SET_FPPT, false, config_store.prop[src][idx].fppt, NULL);
49+
amd_pmf_send_cmd(dev, SET_SPPT, false, config_store.prop[src][idx].sppt, NULL);
50+
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false,
51+
config_store.prop[src][idx].sppt_apu_only, NULL);
52+
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
53+
config_store.prop[src][idx].stt_min, NULL);
54+
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
55+
config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
56+
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
57+
config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
58+
} else if (op == SLIDER_OP_GET) {
59+
amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
60+
amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
61+
amd_pmf_send_cmd(dev, GET_SPPT, true, ARG_NONE, &table->prop[src][idx].sppt);
62+
amd_pmf_send_cmd(dev, GET_SPPT_APU_ONLY, true, ARG_NONE,
63+
&table->prop[src][idx].sppt_apu_only);
64+
amd_pmf_send_cmd(dev, GET_STT_MIN_LIMIT, true, ARG_NONE,
65+
&table->prop[src][idx].stt_min);
66+
amd_pmf_send_cmd(dev, GET_STT_LIMIT_APU, true, ARG_NONE,
67+
(u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_APU]);
68+
amd_pmf_send_cmd(dev, GET_STT_LIMIT_HS2, true, ARG_NONE,
69+
(u32 *)&table->prop[src][idx].stt_skin_temp[STT_TEMP_HS2]);
70+
}
71+
}
72+
73+
static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
74+
enum platform_profile_option *profile)
75+
{
76+
struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
77+
78+
*profile = pmf->current_profile;
79+
return 0;
80+
}
81+
82+
u8 amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
83+
{
84+
u8 mode;
85+
86+
switch (pmf->current_profile) {
87+
case PLATFORM_PROFILE_PERFORMANCE:
88+
mode = POWER_MODE_PERFORMANCE;
89+
break;
90+
case PLATFORM_PROFILE_BALANCED:
91+
mode = POWER_MODE_BALANCED_POWER;
92+
break;
93+
case PLATFORM_PROFILE_LOW_POWER:
94+
mode = POWER_MODE_POWER_SAVER;
95+
break;
96+
default:
97+
dev_err(pmf->dev, "Unknown Platform Profile.\n");
98+
break;
99+
}
100+
101+
return mode;
102+
}
103+
104+
static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
105+
enum platform_profile_option profile)
106+
{
107+
struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
108+
u8 mode;
109+
110+
pmf->current_profile = profile;
111+
mode = amd_pmf_get_pprof_modes(pmf);
112+
amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
113+
return 0;
114+
}
115+
116+
int amd_pmf_init_sps(struct amd_pmf_dev *dev)
117+
{
118+
int err;
119+
120+
dev->current_profile = PLATFORM_PROFILE_BALANCED;
121+
amd_pmf_load_defaults_sps(dev);
122+
123+
dev->pprof.profile_get = amd_pmf_profile_get;
124+
dev->pprof.profile_set = amd_pmf_profile_set;
125+
126+
/* Setup supported modes */
127+
set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices);
128+
set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices);
129+
set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices);
130+
131+
/* Create platform_profile structure and register */
132+
err = platform_profile_register(&dev->pprof);
133+
if (err)
134+
dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
135+
err);
136+
137+
return err;
138+
}
139+
140+
void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
141+
{
142+
platform_profile_remove();
143+
}

0 commit comments

Comments
 (0)