xref: /linux/drivers/platform/x86/amd/pmf/spc.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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
19 static 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 
33 void 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
53 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
54 #endif
55 
56 static 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 
73 static 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 
102 static const char * const pmf_battery_supply_name[] = {
103 	"BATT",
104 	"BAT0",
105 };
106 
107 static 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 
128 static 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 
150 static 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 
173 static 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 
195 void 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 }
205