Linux Audio

Check our new training course

Loading...
v6.8
  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}
v6.13.7
  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}