spc.c 6.2 KB

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