xref: /linux/drivers/platform/x86/amd/pmf/spc.c (revision cad151904379b302a62e8967b7db6ba2e883a212)
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/power_supply.h>
14 #include <linux/units.h>
15 #include "pmf.h"
16 
17 #ifdef CONFIG_AMD_PMF_DEBUG
18 static const char *ta_slider_as_str(unsigned int state)
19 {
20 	switch (state) {
21 	case TA_BEST_PERFORMANCE:
22 		return "PERFORMANCE";
23 	case TA_BETTER_PERFORMANCE:
24 		return "BALANCED";
25 	case TA_BEST_BATTERY:
26 		return "POWER_SAVER";
27 	default:
28 		return "Unknown TA Slider State";
29 	}
30 }
31 
32 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
33 {
34 	dev_dbg(dev->dev, "==== TA inputs START ====\n");
35 	dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
36 	dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
37 	dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
38 	dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
39 	dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
40 	dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
41 	dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
42 	dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
43 	dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
44 	dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
45 	dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
46 	dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
47 	dev_dbg(dev->dev, "==== TA inputs END ====\n");
48 }
49 #else
50 void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
51 #endif
52 
53 static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
54 {
55 	u16 max, avg = 0;
56 	int i;
57 
58 	memset(dev->buf, 0, sizeof(dev->m_table));
59 	amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
60 	memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
61 
62 	in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
63 	in->ev_info.skin_temperature = dev->m_table.skin_temp;
64 
65 	/* Get the avg and max C0 residency of all the cores */
66 	max = dev->m_table.avg_core_c0residency[0];
67 	for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) {
68 		avg += dev->m_table.avg_core_c0residency[i];
69 		if (dev->m_table.avg_core_c0residency[i] > max)
70 			max = dev->m_table.avg_core_c0residency[i];
71 	}
72 
73 	avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency));
74 	in->ev_info.avg_c0residency = avg;
75 	in->ev_info.max_c0residency = max;
76 	in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
77 }
78 
79 static const char * const pmf_battery_supply_name[] = {
80 	"BATT",
81 	"BAT0",
82 };
83 
84 static int amd_pmf_get_battery_prop(enum power_supply_property prop)
85 {
86 	union power_supply_propval value;
87 	struct power_supply *psy;
88 	int i, ret;
89 
90 	for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
91 		psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
92 		if (!psy)
93 			continue;
94 
95 		ret = power_supply_get_property(psy, prop, &value);
96 		if (ret) {
97 			power_supply_put(psy);
98 			return ret;
99 		}
100 	}
101 
102 	return value.intval;
103 }
104 
105 static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
106 {
107 	int val;
108 
109 	val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
110 	if (val < 0)
111 		return val;
112 	if (val != 1)
113 		return -ENODEV;
114 
115 	in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
116 	/* all values in mWh metrics */
117 	in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
118 		MILLIWATT_PER_WATT;
119 	in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
120 		MILLIWATT_PER_WATT;
121 	in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
122 		MILLIWATT_PER_WATT;
123 
124 	return 0;
125 }
126 
127 static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
128 {
129 	int val;
130 
131 	switch (dev->current_profile) {
132 	case PLATFORM_PROFILE_PERFORMANCE:
133 		val = TA_BEST_PERFORMANCE;
134 		break;
135 	case PLATFORM_PROFILE_BALANCED:
136 		val = TA_BETTER_PERFORMANCE;
137 		break;
138 	case PLATFORM_PROFILE_LOW_POWER:
139 		val = TA_BEST_BATTERY;
140 		break;
141 	default:
142 		dev_err(dev->dev, "Unknown Platform Profile.\n");
143 		return -EOPNOTSUPP;
144 	}
145 	in->ev_info.power_slider = val;
146 
147 	return 0;
148 }
149 
150 void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
151 {
152 	/* TA side lid open is 1 and close is 0, hence the ! here */
153 	in->ev_info.lid_state = !acpi_lid_open();
154 	in->ev_info.power_source = amd_pmf_get_power_source();
155 	amd_pmf_get_smu_info(dev, in);
156 	amd_pmf_get_battery_info(dev, in);
157 	amd_pmf_get_slider_info(dev, in);
158 }
159