Loading...
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * AMD Platform Management Framework Driver - Smart PC Capabilities
4 *
5 * Copyright (c) 2023, Advanced Micro Devices, Inc.
6 * All Rights Reserved.
7 *
8 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9 * Patil Rajesh Reddy <Patil.Reddy@amd.com>
10 */
11
12#include <acpi/button.h>
13#include <linux/amd-pmf-io.h>
14#include <linux/power_supply.h>
15#include <linux/units.h>
16#include "pmf.h"
17
18#ifdef CONFIG_AMD_PMF_DEBUG
19static const char *ta_slider_as_str(unsigned int state)
20{
21 switch (state) {
22 case TA_BEST_PERFORMANCE:
23 return "PERFORMANCE";
24 case TA_BETTER_PERFORMANCE:
25 return "BALANCED";
26 case TA_BEST_BATTERY:
27 return "POWER_SAVER";
28 default:
29 return "Unknown TA Slider State";
30 }
31}
32
33void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
34{
35 dev_dbg(dev->dev, "==== TA inputs START ====\n");
36 dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
37 dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
38 dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
39 dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
40 dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
41 dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
42 dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
43 dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
44 dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
45 dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
46 dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
47 dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
48 dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
49 dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
50 dev_dbg(dev->dev, "==== TA inputs END ====\n");
51}
52#else
53void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
54#endif
55
56static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
57{
58 u16 max, avg = 0;
59 int i;
60
61 memset(dev->buf, 0, sizeof(dev->m_table));
62 amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
63 memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
64
65 in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
66 in->ev_info.skin_temperature = dev->m_table.skin_temp;
67
68 /* Get the avg and max C0 residency of all the cores */
69 max = dev->m_table.avg_core_c0residency[0];
70 for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) {
71 avg += dev->m_table.avg_core_c0residency[i];
72 if (dev->m_table.avg_core_c0residency[i] > max)
73 max = dev->m_table.avg_core_c0residency[i];
74 }
75
76 avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency));
77 in->ev_info.avg_c0residency = avg;
78 in->ev_info.max_c0residency = max;
79 in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
80}
81
82static const char * const pmf_battery_supply_name[] = {
83 "BATT",
84 "BAT0",
85};
86
87static int amd_pmf_get_battery_prop(enum power_supply_property prop)
88{
89 union power_supply_propval value;
90 struct power_supply *psy;
91 int i, ret;
92
93 for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
94 psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
95 if (!psy)
96 continue;
97
98 ret = power_supply_get_property(psy, prop, &value);
99 if (ret) {
100 power_supply_put(psy);
101 return ret;
102 }
103 }
104
105 return value.intval;
106}
107
108static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
109{
110 int val;
111
112 val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
113 if (val < 0)
114 return val;
115 if (val != 1)
116 return -ENODEV;
117
118 in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
119 /* all values in mWh metrics */
120 in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
121 MILLIWATT_PER_WATT;
122 in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
123 MILLIWATT_PER_WATT;
124 in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
125 MILLIWATT_PER_WATT;
126
127 return 0;
128}
129
130static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
131{
132 int val;
133
134 switch (dev->current_profile) {
135 case PLATFORM_PROFILE_PERFORMANCE:
136 val = TA_BEST_PERFORMANCE;
137 break;
138 case PLATFORM_PROFILE_BALANCED:
139 val = TA_BETTER_PERFORMANCE;
140 break;
141 case PLATFORM_PROFILE_LOW_POWER:
142 val = TA_BEST_BATTERY;
143 break;
144 default:
145 dev_err(dev->dev, "Unknown Platform Profile.\n");
146 return -EOPNOTSUPP;
147 }
148 in->ev_info.power_slider = val;
149
150 return 0;
151}
152
153static int amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
154{
155 struct amd_sfh_info sfh_info;
156 int ret;
157
158 /* Get ALS data */
159 ret = amd_get_sfh_info(&sfh_info, MT_ALS);
160 if (!ret)
161 in->ev_info.ambient_light = sfh_info.ambient_light;
162 else
163 return ret;
164
165 /* get HPD data */
166 ret = amd_get_sfh_info(&sfh_info, MT_HPD);
167 if (ret)
168 return ret;
169
170 switch (sfh_info.user_present) {
171 case SFH_NOT_DETECTED:
172 in->ev_info.user_present = 0xff; /* assume no sensors connected */
173 break;
174 case SFH_USER_PRESENT:
175 in->ev_info.user_present = 1;
176 break;
177 case SFH_USER_AWAY:
178 in->ev_info.user_present = 0;
179 break;
180 }
181
182 return 0;
183}
184
185void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
186{
187 /* TA side lid open is 1 and close is 0, hence the ! here */
188 in->ev_info.lid_state = !acpi_lid_open();
189 in->ev_info.power_source = amd_pmf_get_power_source();
190 amd_pmf_get_smu_info(dev, in);
191 amd_pmf_get_battery_info(dev, in);
192 amd_pmf_get_slider_info(dev, in);
193 amd_pmf_get_sensor_info(dev, in);
194}
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * AMD Platform Management Framework Driver - Smart PC Capabilities
4 *
5 * Copyright (c) 2023, Advanced Micro Devices, Inc.
6 * All Rights Reserved.
7 *
8 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9 * Patil Rajesh Reddy <Patil.Reddy@amd.com>
10 */
11
12#include <acpi/button.h>
13#include <linux/amd-pmf-io.h>
14#include <linux/power_supply.h>
15#include <linux/units.h>
16#include "pmf.h"
17
18#ifdef CONFIG_AMD_PMF_DEBUG
19static const char *ta_slider_as_str(unsigned int state)
20{
21 switch (state) {
22 case TA_BEST_PERFORMANCE:
23 return "PERFORMANCE";
24 case TA_BETTER_PERFORMANCE:
25 return "BALANCED";
26 case TA_BEST_BATTERY:
27 return "POWER_SAVER";
28 default:
29 return "Unknown TA Slider State";
30 }
31}
32
33void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
34{
35 dev_dbg(dev->dev, "==== TA inputs START ====\n");
36 dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
37 dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
38 dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
39 dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
40 dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
41 dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
42 dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
43 dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
44 dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
45 dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
46 dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
47 dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
48 dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
49 dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
50 dev_dbg(dev->dev, "==== TA inputs END ====\n");
51}
52#else
53void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
54#endif
55
56static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
57{
58 u16 max, avg = 0;
59 int i;
60
61 /* Get the avg and max C0 residency of all the cores */
62 max = *core_res;
63 for (i = 0; i < size; i++) {
64 avg += core_res[i];
65 if (core_res[i] > max)
66 max = core_res[i];
67 }
68 avg = DIV_ROUND_CLOSEST(avg, size);
69 in->ev_info.avg_c0residency = avg;
70 in->ev_info.max_c0residency = max;
71}
72
73static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
74{
75 /* Get the updated metrics table data */
76 memset(dev->buf, 0, dev->mtable_size);
77 amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
78
79 switch (dev->cpu_id) {
80 case AMD_CPU_ID_PS:
81 memcpy(&dev->m_table, dev->buf, dev->mtable_size);
82 in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
83 in->ev_info.skin_temperature = dev->m_table.skin_temp;
84 in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
85 amd_pmf_get_c0_residency(dev->m_table.avg_core_c0residency,
86 ARRAY_SIZE(dev->m_table.avg_core_c0residency), in);
87 break;
88 case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
89 case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
90 memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
91 in->ev_info.socket_power = dev->m_table_v2.apu_power + dev->m_table_v2.dgpu_power;
92 in->ev_info.skin_temperature = dev->m_table_v2.skin_temp;
93 in->ev_info.gfx_busy = dev->m_table_v2.gfx_activity;
94 amd_pmf_get_c0_residency(dev->m_table_v2.core_c0residency,
95 ARRAY_SIZE(dev->m_table_v2.core_c0residency), in);
96 break;
97 default:
98 dev_err(dev->dev, "Unsupported CPU id: 0x%x", dev->cpu_id);
99 }
100}
101
102static const char * const pmf_battery_supply_name[] = {
103 "BATT",
104 "BAT0",
105};
106
107static int amd_pmf_get_battery_prop(enum power_supply_property prop)
108{
109 union power_supply_propval value;
110 struct power_supply *psy;
111 int i, ret;
112
113 for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
114 psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
115 if (!psy)
116 continue;
117
118 ret = power_supply_get_property(psy, prop, &value);
119 if (ret) {
120 power_supply_put(psy);
121 return ret;
122 }
123 }
124
125 return value.intval;
126}
127
128static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
129{
130 int val;
131
132 val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
133 if (val < 0)
134 return val;
135 if (val != 1)
136 return -ENODEV;
137
138 in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
139 /* all values in mWh metrics */
140 in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
141 MILLIWATT_PER_WATT;
142 in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
143 MILLIWATT_PER_WATT;
144 in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
145 MILLIWATT_PER_WATT;
146
147 return 0;
148}
149
150static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
151{
152 int val;
153
154 switch (dev->current_profile) {
155 case PLATFORM_PROFILE_PERFORMANCE:
156 val = TA_BEST_PERFORMANCE;
157 break;
158 case PLATFORM_PROFILE_BALANCED:
159 val = TA_BETTER_PERFORMANCE;
160 break;
161 case PLATFORM_PROFILE_LOW_POWER:
162 val = TA_BEST_BATTERY;
163 break;
164 default:
165 dev_err(dev->dev, "Unknown Platform Profile.\n");
166 return -EOPNOTSUPP;
167 }
168 in->ev_info.power_slider = val;
169
170 return 0;
171}
172
173static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
174{
175 struct amd_sfh_info sfh_info;
176
177 /* Get the latest information from SFH */
178 in->ev_info.user_present = false;
179
180 /* Get ALS data */
181 if (!amd_get_sfh_info(&sfh_info, MT_ALS))
182 in->ev_info.ambient_light = sfh_info.ambient_light;
183 else
184 dev_dbg(dev->dev, "ALS is not enabled/detected\n");
185
186 /* get HPD data */
187 if (!amd_get_sfh_info(&sfh_info, MT_HPD)) {
188 if (sfh_info.user_present == SFH_USER_PRESENT)
189 in->ev_info.user_present = true;
190 } else {
191 dev_dbg(dev->dev, "HPD is not enabled/detected\n");
192 }
193}
194
195void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
196{
197 /* TA side lid open is 1 and close is 0, hence the ! here */
198 in->ev_info.lid_state = !acpi_lid_open();
199 in->ev_info.power_source = amd_pmf_get_power_source();
200 amd_pmf_get_smu_info(dev, in);
201 amd_pmf_get_battery_info(dev, in);
202 amd_pmf_get_slider_info(dev, in);
203 amd_pmf_get_sensor_info(dev, in);
204}