1 // SPDX-License-Identifier: GPL-2.0
3 * AMD Platform Management Framework Driver - Smart PC Capabilities
5 * Copyright (c) 2023, Advanced Micro Devices, Inc.
8 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9 * Patil Rajesh Reddy <Patil.Reddy@amd.com>
12 #include <acpi/button.h>
13 #include <linux/amd-pmf-io.h>
14 #include <linux/power_supply.h>
15 #include <linux/units.h>
18 #ifdef CONFIG_AMD_PMF_DEBUG
19 static const char *platform_type_as_str(u16 platform_type
)
21 switch (platform_type
) {
35 return "PRESENTATION";
43 static const char *laptop_placement_as_str(u16 device_state
)
45 switch (device_state
) {
49 return "ON_LAP_MOTION";
59 static const char *ta_slider_as_str(unsigned int state
)
62 case TA_BEST_PERFORMANCE
:
64 case TA_BETTER_PERFORMANCE
:
69 return "Unknown TA Slider State";
73 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
)
75 dev_dbg(dev
->dev
, "==== TA inputs START ====\n");
76 dev_dbg(dev
->dev
, "Slider State: %s\n", ta_slider_as_str(in
->ev_info
.power_slider
));
77 dev_dbg(dev
->dev
, "Power Source: %s\n", amd_pmf_source_as_str(in
->ev_info
.power_source
));
78 dev_dbg(dev
->dev
, "Battery Percentage: %u\n", in
->ev_info
.bat_percentage
);
79 dev_dbg(dev
->dev
, "Designed Battery Capacity: %u\n", in
->ev_info
.bat_design
);
80 dev_dbg(dev
->dev
, "Fully Charged Capacity: %u\n", in
->ev_info
.full_charge_capacity
);
81 dev_dbg(dev
->dev
, "Drain Rate: %d\n", in
->ev_info
.drain_rate
);
82 dev_dbg(dev
->dev
, "Socket Power: %u\n", in
->ev_info
.socket_power
);
83 dev_dbg(dev
->dev
, "Skin Temperature: %u\n", in
->ev_info
.skin_temperature
);
84 dev_dbg(dev
->dev
, "Avg C0 Residency: %u\n", in
->ev_info
.avg_c0residency
);
85 dev_dbg(dev
->dev
, "Max C0 Residency: %u\n", in
->ev_info
.max_c0residency
);
86 dev_dbg(dev
->dev
, "GFX Busy: %u\n", in
->ev_info
.gfx_busy
);
87 dev_dbg(dev
->dev
, "LID State: %s\n", in
->ev_info
.lid_state
? "close" : "open");
88 dev_dbg(dev
->dev
, "User Presence: %s\n", in
->ev_info
.user_present
? "Present" : "Away");
89 dev_dbg(dev
->dev
, "Ambient Light: %d\n", in
->ev_info
.ambient_light
);
90 dev_dbg(dev
->dev
, "Platform type: %s\n", platform_type_as_str(in
->ev_info
.platform_type
));
91 dev_dbg(dev
->dev
, "Laptop placement: %s\n",
92 laptop_placement_as_str(in
->ev_info
.device_state
));
93 dev_dbg(dev
->dev
, "Custom BIOS input1: %u\n", in
->ev_info
.bios_input1
);
94 dev_dbg(dev
->dev
, "Custom BIOS input2: %u\n", in
->ev_info
.bios_input2
);
95 dev_dbg(dev
->dev
, "==== TA inputs END ====\n");
98 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
) {}
101 static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev
*pdev
,
102 struct ta_pmf_enact_table
*in
)
104 if (!pdev
->req
.pending_req
)
107 switch (pdev
->req
.pending_req
) {
108 case BIT(NOTIFY_CUSTOM_BIOS_INPUT1
):
109 in
->ev_info
.bios_input1
= pdev
->req
.custom_policy
[APMF_SMARTPC_CUSTOM_BIOS_INPUT1
];
111 case BIT(NOTIFY_CUSTOM_BIOS_INPUT2
):
112 in
->ev_info
.bios_input2
= pdev
->req
.custom_policy
[APMF_SMARTPC_CUSTOM_BIOS_INPUT2
];
115 dev_dbg(pdev
->dev
, "Invalid preq for BIOS input: 0x%x\n", pdev
->req
.pending_req
);
118 /* Clear pending requests after handling */
119 memset(&pdev
->req
, 0, sizeof(pdev
->req
));
122 static void amd_pmf_get_c0_residency(u16
*core_res
, size_t size
, struct ta_pmf_enact_table
*in
)
127 /* Get the avg and max C0 residency of all the cores */
129 for (i
= 0; i
< size
; i
++) {
131 if (core_res
[i
] > max
)
134 avg
= DIV_ROUND_CLOSEST(avg
, size
);
135 in
->ev_info
.avg_c0residency
= avg
;
136 in
->ev_info
.max_c0residency
= max
;
139 static void amd_pmf_get_smu_info(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
)
141 /* Get the updated metrics table data */
142 memset(dev
->buf
, 0, dev
->mtable_size
);
143 amd_pmf_send_cmd(dev
, SET_TRANSFER_TABLE
, 0, 7, NULL
);
145 switch (dev
->cpu_id
) {
147 memcpy(&dev
->m_table
, dev
->buf
, dev
->mtable_size
);
148 in
->ev_info
.socket_power
= dev
->m_table
.apu_power
+ dev
->m_table
.dgpu_power
;
149 in
->ev_info
.skin_temperature
= dev
->m_table
.skin_temp
;
150 in
->ev_info
.gfx_busy
= dev
->m_table
.avg_gfx_activity
;
151 amd_pmf_get_c0_residency(dev
->m_table
.avg_core_c0residency
,
152 ARRAY_SIZE(dev
->m_table
.avg_core_c0residency
), in
);
154 case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT
:
155 case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT
:
156 memcpy(&dev
->m_table_v2
, dev
->buf
, dev
->mtable_size
);
157 in
->ev_info
.socket_power
= dev
->m_table_v2
.apu_power
+ dev
->m_table_v2
.dgpu_power
;
158 in
->ev_info
.skin_temperature
= dev
->m_table_v2
.skin_temp
;
159 in
->ev_info
.gfx_busy
= dev
->m_table_v2
.gfx_activity
;
160 amd_pmf_get_c0_residency(dev
->m_table_v2
.core_c0residency
,
161 ARRAY_SIZE(dev
->m_table_v2
.core_c0residency
), in
);
164 dev_err(dev
->dev
, "Unsupported CPU id: 0x%x", dev
->cpu_id
);
168 static const char * const pmf_battery_supply_name
[] = {
173 static int amd_pmf_get_battery_prop(enum power_supply_property prop
)
175 union power_supply_propval value
;
176 struct power_supply
*psy
;
179 for (i
= 0; i
< ARRAY_SIZE(pmf_battery_supply_name
); i
++) {
180 psy
= power_supply_get_by_name(pmf_battery_supply_name
[i
]);
184 ret
= power_supply_get_property(psy
, prop
, &value
);
186 power_supply_put(psy
);
194 static int amd_pmf_get_battery_info(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
)
198 val
= amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT
);
204 in
->ev_info
.bat_percentage
= amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY
);
205 /* all values in mWh metrics */
206 in
->ev_info
.bat_design
= amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
) /
208 in
->ev_info
.full_charge_capacity
= amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL
) /
210 in
->ev_info
.drain_rate
= amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW
) /
216 static int amd_pmf_get_slider_info(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
)
220 switch (dev
->current_profile
) {
221 case PLATFORM_PROFILE_PERFORMANCE
:
222 val
= TA_BEST_PERFORMANCE
;
224 case PLATFORM_PROFILE_BALANCED
:
225 val
= TA_BETTER_PERFORMANCE
;
227 case PLATFORM_PROFILE_LOW_POWER
:
228 val
= TA_BEST_BATTERY
;
231 dev_err(dev
->dev
, "Unknown Platform Profile.\n");
234 in
->ev_info
.power_slider
= val
;
239 static void amd_pmf_get_sensor_info(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
)
241 struct amd_sfh_info sfh_info
;
243 /* Get the latest information from SFH */
244 in
->ev_info
.user_present
= false;
247 if (!amd_get_sfh_info(&sfh_info
, MT_ALS
))
248 in
->ev_info
.ambient_light
= sfh_info
.ambient_light
;
250 dev_dbg(dev
->dev
, "ALS is not enabled/detected\n");
253 if (!amd_get_sfh_info(&sfh_info
, MT_HPD
)) {
254 if (sfh_info
.user_present
== SFH_USER_PRESENT
)
255 in
->ev_info
.user_present
= true;
257 dev_dbg(dev
->dev
, "HPD is not enabled/detected\n");
260 /* Get SRA (Secondary Accelerometer) data */
261 if (!amd_get_sfh_info(&sfh_info
, MT_SRA
)) {
262 in
->ev_info
.platform_type
= sfh_info
.platform_type
;
263 in
->ev_info
.device_state
= sfh_info
.laptop_placement
;
265 dev_dbg(dev
->dev
, "SRA is not enabled/detected\n");
269 void amd_pmf_populate_ta_inputs(struct amd_pmf_dev
*dev
, struct ta_pmf_enact_table
*in
)
271 /* TA side lid open is 1 and close is 0, hence the ! here */
272 in
->ev_info
.lid_state
= !acpi_lid_open();
273 in
->ev_info
.power_source
= amd_pmf_get_power_source();
274 amd_pmf_get_smu_info(dev
, in
);
275 amd_pmf_get_battery_info(dev
, in
);
276 amd_pmf_get_slider_info(dev
, in
);
277 amd_pmf_get_sensor_info(dev
, in
);
278 amd_pmf_get_custom_bios_inputs(dev
, in
);