aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/amd/pm/swsmu/smu13
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
committerLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
commit5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch)
treecc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/gpu/drm/amd/pm/swsmu/smu13
downloadlinux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz
linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ...
Diffstat (limited to 'drivers/gpu/drm/amd/pm/swsmu/smu13')
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/Makefile31
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c2151
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.h72
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c2491
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c2061
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.h28
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c1052
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.h28
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c1074
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.h29
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c1731
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.h28
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c1238
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.h29
14 files changed, 12043 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/Makefile b/drivers/gpu/drm/amd/pm/swsmu/smu13/Makefile
new file mode 100644
index 000000000..9043f6ef1
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/Makefile
@@ -0,0 +1,31 @@
+#
+# Copyright 2020 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+#
+# Makefile for the 'smu manager' sub-component of powerplay.
+# It provides the smu management services for the driver.
+
+SMU13_MGR = smu_v13_0.o aldebaran_ppt.o yellow_carp_ppt.o smu_v13_0_0_ppt.o smu_v13_0_4_ppt.o \
+ smu_v13_0_5_ppt.o smu_v13_0_7_ppt.o
+
+AMD_SWSMU_SMU13MGR = $(addprefix $(AMD_SWSMU_PATH)/smu13/,$(SMU13_MGR))
+
+AMD_POWERPLAY_FILES += $(AMD_SWSMU_SMU13MGR)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
new file mode 100644
index 000000000..d30ec3005
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
@@ -0,0 +1,2151 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define SWSMU_CODE_LAYER_L2
+
+#include <linux/firmware.h>
+#include "amdgpu.h"
+#include "amdgpu_dpm.h"
+#include "amdgpu_smu.h"
+#include "atomfirmware.h"
+#include "amdgpu_atomfirmware.h"
+#include "amdgpu_atombios.h"
+#include "smu_v13_0.h"
+#include "smu13_driver_if_aldebaran.h"
+#include "soc15_common.h"
+#include "atom.h"
+#include "aldebaran_ppt.h"
+#include "smu_v13_0_pptable.h"
+#include "aldebaran_ppsmc.h"
+#include "nbio/nbio_7_4_offset.h"
+#include "nbio/nbio_7_4_sh_mask.h"
+#include "thm/thm_11_0_2_offset.h"
+#include "thm/thm_11_0_2_sh_mask.h"
+#include "amdgpu_xgmi.h"
+#include <linux/pci.h>
+#include "amdgpu_ras.h"
+#include "smu_cmn.h"
+#include "mp/mp_13_0_2_offset.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+#define ALDEBARAN_FEA_MAP(smu_feature, aldebaran_feature) \
+ [smu_feature] = {1, (aldebaran_feature)}
+
+#define FEATURE_MASK(feature) (1ULL << feature)
+#define SMC_DPM_FEATURE ( \
+ FEATURE_MASK(FEATURE_DATA_CALCULATIONS) | \
+ FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_FCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_LCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_XGMI_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_VCN_BIT))
+
+/* possible frequency drift (1Mhz) */
+#define EPSILON 1
+
+#define smnPCIE_ESM_CTRL 0x111003D0
+
+/*
+ * SMU support ECCTABLE since version 68.42.0,
+ * use this to check ECCTALE feature whether support
+ */
+#define SUPPORT_ECCTABLE_SMU_VERSION 0x00442a00
+
+/*
+ * SMU support mca_ceumc_addr in ECCTABLE since version 68.55.0,
+ * use this to check mca_ceumc_addr record whether support
+ */
+#define SUPPORT_ECCTABLE_V2_SMU_VERSION 0x00443700
+
+/*
+ * SMU support BAD CHENNEL info MSG since version 68.51.00,
+ * use this to check ECCTALE feature whether support
+ */
+#define SUPPORT_BAD_CHANNEL_INFO_MSG_VERSION 0x00443300
+
+static const struct smu_temperature_range smu13_thermal_policy[] =
+{
+ {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
+ { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
+};
+
+static const struct cmn2asic_msg_mapping aldebaran_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0),
+ MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
+ MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
+ MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
+ MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
+ MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetEnabledSmuFeaturesLow, 1),
+ MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetEnabledSmuFeaturesHigh, 1),
+ MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
+ MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
+ MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
+ MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
+ MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
+ MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
+ MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
+ MSG_MAP(SetSystemVirtualDramAddrHigh, PPSMC_MSG_SetSystemVirtualDramAddrHigh, 0),
+ MSG_MAP(SetSystemVirtualDramAddrLow, PPSMC_MSG_SetSystemVirtualDramAddrLow, 0),
+ MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 0),
+ MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 0),
+ MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 0),
+ MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
+ MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 0),
+ MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 0),
+ MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
+ MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
+ MSG_MAP(GetVoltageByDpm, PPSMC_MSG_GetVoltageByDpm, 0),
+ MSG_MAP(GetVoltageByDpmOverdrive, PPSMC_MSG_GetVoltageByDpmOverdrive, 0),
+ MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
+ MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 1),
+ MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
+ MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDriverReset, 0),
+ MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
+ MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
+ MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
+ MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
+ MSG_MAP(GetDebugData, PPSMC_MSG_GetDebugData, 0),
+ MSG_MAP(WaflTest, PPSMC_MSG_WaflTest, 0),
+ MSG_MAP(SetMemoryChannelEnable, PPSMC_MSG_SetMemoryChannelEnable, 0),
+ MSG_MAP(SetNumBadHbmPagesRetired, PPSMC_MSG_SetNumBadHbmPagesRetired, 0),
+ MSG_MAP(DFCstateControl, PPSMC_MSG_DFCstateControl, 0),
+ MSG_MAP(GetGmiPwrDnHyst, PPSMC_MSG_GetGmiPwrDnHyst, 0),
+ MSG_MAP(SetGmiPwrDnHyst, PPSMC_MSG_SetGmiPwrDnHyst, 0),
+ MSG_MAP(GmiPwrDnControl, PPSMC_MSG_GmiPwrDnControl, 0),
+ MSG_MAP(EnterGfxoff, PPSMC_MSG_EnterGfxoff, 0),
+ MSG_MAP(ExitGfxoff, PPSMC_MSG_ExitGfxoff, 0),
+ MSG_MAP(SetExecuteDMATest, PPSMC_MSG_SetExecuteDMATest, 0),
+ MSG_MAP(EnableDeterminism, PPSMC_MSG_EnableDeterminism, 0),
+ MSG_MAP(DisableDeterminism, PPSMC_MSG_DisableDeterminism, 0),
+ MSG_MAP(SetUclkDpmMode, PPSMC_MSG_SetUclkDpmMode, 0),
+ MSG_MAP(GfxDriverResetRecovery, PPSMC_MSG_GfxDriverResetRecovery, 0),
+ MSG_MAP(BoardPowerCalibration, PPSMC_MSG_BoardPowerCalibration, 0),
+ MSG_MAP(HeavySBR, PPSMC_MSG_HeavySBR, 0),
+ MSG_MAP(SetBadHBMPagesRetiredFlagsPerChannel, PPSMC_MSG_SetBadHBMPagesRetiredFlagsPerChannel, 0),
+};
+
+static const struct cmn2asic_mapping aldebaran_clk_map[SMU_CLK_COUNT] = {
+ CLK_MAP(GFXCLK, PPCLK_GFXCLK),
+ CLK_MAP(SCLK, PPCLK_GFXCLK),
+ CLK_MAP(SOCCLK, PPCLK_SOCCLK),
+ CLK_MAP(FCLK, PPCLK_FCLK),
+ CLK_MAP(UCLK, PPCLK_UCLK),
+ CLK_MAP(MCLK, PPCLK_UCLK),
+ CLK_MAP(DCLK, PPCLK_DCLK),
+ CLK_MAP(VCLK, PPCLK_VCLK),
+ CLK_MAP(LCLK, PPCLK_LCLK),
+};
+
+static const struct cmn2asic_mapping aldebaran_feature_mask_map[SMU_FEATURE_COUNT] = {
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DATA_CALCULATIONS_BIT, FEATURE_DATA_CALCULATIONS),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DPM_GFXCLK_BIT, FEATURE_DPM_GFXCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DPM_UCLK_BIT, FEATURE_DPM_UCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DPM_SOCCLK_BIT, FEATURE_DPM_SOCCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DPM_FCLK_BIT, FEATURE_DPM_FCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DPM_LCLK_BIT, FEATURE_DPM_LCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DPM_XGMI_BIT, FEATURE_DPM_XGMI_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DS_GFXCLK_BIT, FEATURE_DS_GFXCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DS_SOCCLK_BIT, FEATURE_DS_SOCCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DS_LCLK_BIT, FEATURE_DS_LCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DS_FCLK_BIT, FEATURE_DS_FCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DS_UCLK_BIT, FEATURE_DS_UCLK_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_GFX_SS_BIT, FEATURE_GFX_SS_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_VCN_DPM_BIT, FEATURE_DPM_VCN_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_RSMU_SMN_CG_BIT, FEATURE_RSMU_SMN_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_WAFL_CG_BIT, FEATURE_WAFL_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_PPT_BIT, FEATURE_PPT_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_TDC_BIT, FEATURE_TDC_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_APCC_PLUS_BIT, FEATURE_APCC_PLUS_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_APCC_DFLL_BIT, FEATURE_APCC_DFLL_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_FUSE_CG_BIT, FEATURE_FUSE_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_MP1_CG_BIT, FEATURE_MP1_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_SMUIO_CG_BIT, FEATURE_SMUIO_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_THM_CG_BIT, FEATURE_THM_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_CLK_CG_BIT, FEATURE_CLK_CG_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_FW_CTF_BIT, FEATURE_FW_CTF_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_THERMAL_BIT, FEATURE_THERMAL_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_OUT_OF_BAND_MONITOR_BIT, FEATURE_OUT_OF_BAND_MONITOR_BIT),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_XGMI_PER_LINK_PWR_DWN_BIT,FEATURE_XGMI_PER_LINK_PWR_DWN),
+ ALDEBARAN_FEA_MAP(SMU_FEATURE_DF_CSTATE_BIT, FEATURE_DF_CSTATE),
+};
+
+static const struct cmn2asic_mapping aldebaran_table_map[SMU_TABLE_COUNT] = {
+ TAB_MAP(PPTABLE),
+ TAB_MAP(AVFS_PSM_DEBUG),
+ TAB_MAP(AVFS_FUSE_OVERRIDE),
+ TAB_MAP(PMSTATUSLOG),
+ TAB_MAP(SMU_METRICS),
+ TAB_MAP(DRIVER_SMU_CONFIG),
+ TAB_MAP(I2C_COMMANDS),
+ TAB_MAP(ECCINFO),
+};
+
+static const uint8_t aldebaran_throttler_map[] = {
+ [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
+ [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
+ [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
+ [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
+ [THROTTLER_TDC_HBM_BIT] = (SMU_THROTTLER_TDC_MEM_BIT),
+ [THROTTLER_TEMP_GPU_BIT] = (SMU_THROTTLER_TEMP_GPU_BIT),
+ [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
+ [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
+ [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
+ [THROTTLER_TEMP_VR_MEM_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
+ [THROTTLER_APCC_BIT] = (SMU_THROTTLER_APCC_BIT),
+};
+
+static int aldebaran_tables_init(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU13_TOOL_SIZE,
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ smu_table->metrics_table = kzalloc(sizeof(SmuMetrics_t), GFP_KERNEL);
+ if (!smu_table->metrics_table)
+ return -ENOMEM;
+ smu_table->metrics_time = 0;
+
+ smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
+ smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
+ if (!smu_table->gpu_metrics_table) {
+ kfree(smu_table->metrics_table);
+ return -ENOMEM;
+ }
+
+ smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
+ if (!smu_table->ecc_table)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int aldebaran_allocate_dpm_context(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ smu_dpm->dpm_context = kzalloc(sizeof(struct smu_13_0_dpm_context),
+ GFP_KERNEL);
+ if (!smu_dpm->dpm_context)
+ return -ENOMEM;
+ smu_dpm->dpm_context_size = sizeof(struct smu_13_0_dpm_context);
+
+ return 0;
+}
+
+static int aldebaran_init_smc_tables(struct smu_context *smu)
+{
+ int ret = 0;
+
+ ret = aldebaran_tables_init(smu);
+ if (ret)
+ return ret;
+
+ ret = aldebaran_allocate_dpm_context(smu);
+ if (ret)
+ return ret;
+
+ return smu_v13_0_init_smc_tables(smu);
+}
+
+static int aldebaran_get_allowed_feature_mask(struct smu_context *smu,
+ uint32_t *feature_mask, uint32_t num)
+{
+ if (num > 2)
+ return -EINVAL;
+
+ /* pptable will handle the features to enable */
+ memset(feature_mask, 0xFF, sizeof(uint32_t) * num);
+
+ return 0;
+}
+
+static int aldebaran_set_default_dpm_table(struct smu_context *smu)
+{
+ struct smu_13_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *dpm_table = NULL;
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ int ret = 0;
+
+ /* socclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.soc_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_SOCCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* gfxclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.gfx_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
+ /* in the case of gfxclk, only fine-grained dpm is honored */
+ dpm_table->count = 2;
+ dpm_table->dpm_levels[0].value = pptable->GfxclkFmin;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->dpm_levels[1].value = pptable->GfxclkFmax;
+ dpm_table->dpm_levels[1].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[1].value;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* memclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.uclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_UCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* fclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.fclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_FCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ return 0;
+}
+
+static int aldebaran_check_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+
+ table_context->thermal_controller_type =
+ powerplay_table->thermal_controller_type;
+
+ return 0;
+}
+
+static int aldebaran_store_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
+ sizeof(PPTable_t));
+
+ return 0;
+}
+
+static int aldebaran_append_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ struct atom_smc_dpm_info_v4_10 *smc_dpm_table;
+ int index, ret;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ smc_dpm_info);
+
+ ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
+ (uint8_t **)&smc_dpm_table);
+ if (ret)
+ return ret;
+
+ dev_info(smu->adev->dev, "smc_dpm_info table revision(format.content): %d.%d\n",
+ smc_dpm_table->table_header.format_revision,
+ smc_dpm_table->table_header.content_revision);
+
+ if ((smc_dpm_table->table_header.format_revision == 4) &&
+ (smc_dpm_table->table_header.content_revision == 10))
+ smu_memcpy_trailing(smc_pptable, GfxMaxCurrent, reserved,
+ smc_dpm_table, GfxMaxCurrent);
+ return 0;
+}
+
+static int aldebaran_setup_pptable(struct smu_context *smu)
+{
+ int ret = 0;
+
+ /* VBIOS pptable is the first choice */
+ smu->smu_table.boot_values.pp_table_id = 0;
+
+ ret = smu_v13_0_setup_pptable(smu);
+ if (ret)
+ return ret;
+
+ ret = aldebaran_store_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ ret = aldebaran_append_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ ret = aldebaran_check_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static bool aldebaran_is_primary(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->smuio.funcs && adev->smuio.funcs->get_die_id)
+ return adev->smuio.funcs->get_die_id(adev) == 0;
+
+ return true;
+}
+
+static int aldebaran_run_board_btc(struct smu_context *smu)
+{
+ u32 smu_version;
+ int ret;
+
+ if (!aldebaran_is_primary(smu))
+ return 0;
+
+ ret = smu_cmn_get_smc_version(smu, NULL, &smu_version);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to get smu version!\n");
+ return ret;
+ }
+ if (smu_version <= 0x00441d00)
+ return 0;
+
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_BoardPowerCalibration, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "Board power calibration failed!\n");
+
+ return ret;
+}
+
+static int aldebaran_run_btc(struct smu_context *smu)
+{
+ int ret;
+
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_RunDcBtc, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "RunDcBtc failed!\n");
+ else
+ ret = aldebaran_run_board_btc(smu);
+
+ return ret;
+}
+
+static int aldebaran_populate_umd_state_clk(struct smu_context *smu)
+{
+ struct smu_13_0_dpm_context *dpm_context =
+ smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *gfx_table =
+ &dpm_context->dpm_tables.gfx_table;
+ struct smu_13_0_dpm_table *mem_table =
+ &dpm_context->dpm_tables.uclk_table;
+ struct smu_13_0_dpm_table *soc_table =
+ &dpm_context->dpm_tables.soc_table;
+ struct smu_umd_pstate_table *pstate_table =
+ &smu->pstate_table;
+
+ pstate_table->gfxclk_pstate.min = gfx_table->min;
+ pstate_table->gfxclk_pstate.peak = gfx_table->max;
+ pstate_table->gfxclk_pstate.curr.min = gfx_table->min;
+ pstate_table->gfxclk_pstate.curr.max = gfx_table->max;
+
+ pstate_table->uclk_pstate.min = mem_table->min;
+ pstate_table->uclk_pstate.peak = mem_table->max;
+ pstate_table->uclk_pstate.curr.min = mem_table->min;
+ pstate_table->uclk_pstate.curr.max = mem_table->max;
+
+ pstate_table->socclk_pstate.min = soc_table->min;
+ pstate_table->socclk_pstate.peak = soc_table->max;
+ pstate_table->socclk_pstate.curr.min = soc_table->min;
+ pstate_table->socclk_pstate.curr.max = soc_table->max;
+
+ if (gfx_table->count > ALDEBARAN_UMD_PSTATE_GFXCLK_LEVEL &&
+ mem_table->count > ALDEBARAN_UMD_PSTATE_MCLK_LEVEL &&
+ soc_table->count > ALDEBARAN_UMD_PSTATE_SOCCLK_LEVEL) {
+ pstate_table->gfxclk_pstate.standard =
+ gfx_table->dpm_levels[ALDEBARAN_UMD_PSTATE_GFXCLK_LEVEL].value;
+ pstate_table->uclk_pstate.standard =
+ mem_table->dpm_levels[ALDEBARAN_UMD_PSTATE_MCLK_LEVEL].value;
+ pstate_table->socclk_pstate.standard =
+ soc_table->dpm_levels[ALDEBARAN_UMD_PSTATE_SOCCLK_LEVEL].value;
+ } else {
+ pstate_table->gfxclk_pstate.standard =
+ pstate_table->gfxclk_pstate.min;
+ pstate_table->uclk_pstate.standard =
+ pstate_table->uclk_pstate.min;
+ pstate_table->socclk_pstate.standard =
+ pstate_table->socclk_pstate.min;
+ }
+
+ return 0;
+}
+
+static int aldebaran_get_clk_table(struct smu_context *smu,
+ struct pp_clock_levels_with_latency *clocks,
+ struct smu_13_0_dpm_table *dpm_table)
+{
+ uint32_t i;
+
+ clocks->num_levels = min_t(uint32_t,
+ dpm_table->count,
+ (uint32_t)PP_MAX_CLOCK_LEVELS);
+
+ for (i = 0; i < clocks->num_levels; i++) {
+ clocks->data[i].clocks_in_khz =
+ dpm_table->dpm_levels[i].value * 1000;
+ clocks->data[i].latency_in_us = 0;
+ }
+
+ return 0;
+}
+
+static int aldebaran_freqs_in_same_level(int32_t frequency1,
+ int32_t frequency2)
+{
+ return (abs(frequency1 - frequency2) <= EPSILON);
+}
+
+static int aldebaran_get_smu_metrics_data(struct smu_context *smu,
+ MetricsMember_t member,
+ uint32_t *value)
+{
+ struct smu_table_context *smu_table= &smu->smu_table;
+ SmuMetrics_t *metrics = (SmuMetrics_t *)smu_table->metrics_table;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu,
+ NULL,
+ false);
+ if (ret)
+ return ret;
+
+ switch (member) {
+ case METRICS_CURR_GFXCLK:
+ *value = metrics->CurrClock[PPCLK_GFXCLK];
+ break;
+ case METRICS_CURR_SOCCLK:
+ *value = metrics->CurrClock[PPCLK_SOCCLK];
+ break;
+ case METRICS_CURR_UCLK:
+ *value = metrics->CurrClock[PPCLK_UCLK];
+ break;
+ case METRICS_CURR_VCLK:
+ *value = metrics->CurrClock[PPCLK_VCLK];
+ break;
+ case METRICS_CURR_DCLK:
+ *value = metrics->CurrClock[PPCLK_DCLK];
+ break;
+ case METRICS_CURR_FCLK:
+ *value = metrics->CurrClock[PPCLK_FCLK];
+ break;
+ case METRICS_AVERAGE_GFXCLK:
+ *value = metrics->AverageGfxclkFrequency;
+ break;
+ case METRICS_AVERAGE_SOCCLK:
+ *value = metrics->AverageSocclkFrequency;
+ break;
+ case METRICS_AVERAGE_UCLK:
+ *value = metrics->AverageUclkFrequency;
+ break;
+ case METRICS_AVERAGE_GFXACTIVITY:
+ *value = metrics->AverageGfxActivity;
+ break;
+ case METRICS_AVERAGE_MEMACTIVITY:
+ *value = metrics->AverageUclkActivity;
+ break;
+ case METRICS_AVERAGE_SOCKETPOWER:
+ /* Valid power data is available only from primary die */
+ *value = aldebaran_is_primary(smu) ?
+ metrics->AverageSocketPower << 8 :
+ 0;
+ break;
+ case METRICS_TEMPERATURE_EDGE:
+ *value = metrics->TemperatureEdge *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_HOTSPOT:
+ *value = metrics->TemperatureHotspot *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_MEM:
+ *value = metrics->TemperatureHBM *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRGFX:
+ *value = metrics->TemperatureVrGfx *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRSOC:
+ *value = metrics->TemperatureVrSoc *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRMEM:
+ *value = metrics->TemperatureVrMem *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_THROTTLER_STATUS:
+ *value = metrics->ThrottlerStatus;
+ break;
+ case METRICS_UNIQUE_ID_UPPER32:
+ *value = metrics->PublicSerialNumUpper32;
+ break;
+ case METRICS_UNIQUE_ID_LOWER32:
+ *value = metrics->PublicSerialNumLower32;
+ break;
+ default:
+ *value = UINT_MAX;
+ break;
+ }
+
+ return ret;
+}
+
+static int aldebaran_get_current_clk_freq_by_table(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ MetricsMember_t member_type;
+ int clk_id = 0;
+
+ if (!value)
+ return -EINVAL;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return -EINVAL;
+
+ switch (clk_id) {
+ case PPCLK_GFXCLK:
+ /*
+ * CurrClock[clk_id] can provide accurate
+ * output only when the dpm feature is enabled.
+ * We can use Average_* for dpm disabled case.
+ * But this is available for gfxclk/uclk/socclk/vclk/dclk.
+ */
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT))
+ member_type = METRICS_CURR_GFXCLK;
+ else
+ member_type = METRICS_AVERAGE_GFXCLK;
+ break;
+ case PPCLK_UCLK:
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT))
+ member_type = METRICS_CURR_UCLK;
+ else
+ member_type = METRICS_AVERAGE_UCLK;
+ break;
+ case PPCLK_SOCCLK:
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT))
+ member_type = METRICS_CURR_SOCCLK;
+ else
+ member_type = METRICS_AVERAGE_SOCCLK;
+ break;
+ case PPCLK_VCLK:
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_VCN_PG_BIT))
+ member_type = METRICS_CURR_VCLK;
+ else
+ member_type = METRICS_AVERAGE_VCLK;
+ break;
+ case PPCLK_DCLK:
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_VCN_PG_BIT))
+ member_type = METRICS_CURR_DCLK;
+ else
+ member_type = METRICS_AVERAGE_DCLK;
+ break;
+ case PPCLK_FCLK:
+ member_type = METRICS_CURR_FCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return aldebaran_get_smu_metrics_data(smu,
+ member_type,
+ value);
+}
+
+static int aldebaran_print_clk_levels(struct smu_context *smu,
+ enum smu_clk_type type, char *buf)
+{
+ int i, now, size = 0;
+ int ret = 0;
+ struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
+ struct pp_clock_levels_with_latency clocks;
+ struct smu_13_0_dpm_table *single_dpm_table;
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct smu_13_0_dpm_context *dpm_context = NULL;
+ int display_levels;
+ uint32_t freq_values[3] = {0};
+ uint32_t min_clk, max_clk;
+
+ smu_cmn_get_sysfs_buf(&buf, &size);
+
+ if (amdgpu_ras_intr_triggered()) {
+ size += sysfs_emit_at(buf, size, "unavailable\n");
+ return size;
+ }
+
+ dpm_context = smu_dpm->dpm_context;
+
+ switch (type) {
+
+ case SMU_OD_SCLK:
+ size += sysfs_emit_at(buf, size, "%s:\n", "GFXCLK");
+ fallthrough;
+ case SMU_SCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_GFXCLK, &now);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get current gfx clk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
+ ret = aldebaran_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get gfx clk levels Failed!");
+ return ret;
+ }
+
+ display_levels = (clocks.num_levels == 1) ? 1 : 2;
+
+ min_clk = pstate_table->gfxclk_pstate.curr.min;
+ max_clk = pstate_table->gfxclk_pstate.curr.max;
+
+ freq_values[0] = min_clk;
+ freq_values[1] = max_clk;
+
+ /* fine-grained dpm has only 2 levels */
+ if (now > min_clk && now < max_clk) {
+ display_levels++;
+ freq_values[2] = max_clk;
+ freq_values[1] = now;
+ }
+
+ for (i = 0; i < display_levels; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i,
+ freq_values[i],
+ (display_levels == 1) ?
+ "*" :
+ (aldebaran_freqs_in_same_level(
+ freq_values[i], now) ?
+ "*" :
+ ""));
+
+ break;
+
+ case SMU_OD_MCLK:
+ size += sysfs_emit_at(buf, size, "%s:\n", "MCLK");
+ fallthrough;
+ case SMU_MCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_UCLK, &now);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get current mclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
+ ret = aldebaran_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get memory clk levels Failed!");
+ return ret;
+ }
+
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.num_levels == 1) ? "*" :
+ (aldebaran_freqs_in_same_level(
+ clocks.data[i].clocks_in_khz / 1000,
+ now) ? "*" : ""));
+ break;
+
+ case SMU_SOCCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_SOCCLK, &now);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get current socclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_context->dpm_tables.soc_table);
+ ret = aldebaran_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get socclk levels Failed!");
+ return ret;
+ }
+
+ for (i = 0; i < clocks.num_levels; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, clocks.data[i].clocks_in_khz / 1000,
+ (clocks.num_levels == 1) ? "*" :
+ (aldebaran_freqs_in_same_level(
+ clocks.data[i].clocks_in_khz / 1000,
+ now) ? "*" : ""));
+ break;
+
+ case SMU_FCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_FCLK, &now);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get current fclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
+ ret = aldebaran_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get fclk levels Failed!");
+ return ret;
+ }
+
+ for (i = 0; i < single_dpm_table->count; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, single_dpm_table->dpm_levels[i].value,
+ (clocks.num_levels == 1) ? "*" :
+ (aldebaran_freqs_in_same_level(
+ clocks.data[i].clocks_in_khz / 1000,
+ now) ? "*" : ""));
+ break;
+
+ case SMU_VCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_VCLK, &now);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get current vclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
+ ret = aldebaran_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get vclk levels Failed!");
+ return ret;
+ }
+
+ for (i = 0; i < single_dpm_table->count; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, single_dpm_table->dpm_levels[i].value,
+ (clocks.num_levels == 1) ? "*" :
+ (aldebaran_freqs_in_same_level(
+ clocks.data[i].clocks_in_khz / 1000,
+ now) ? "*" : ""));
+ break;
+
+ case SMU_DCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_DCLK, &now);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get current dclk Failed!");
+ return ret;
+ }
+
+ single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
+ ret = aldebaran_get_clk_table(smu, &clocks, single_dpm_table);
+ if (ret) {
+ dev_err(smu->adev->dev, "Attempt to get dclk levels Failed!");
+ return ret;
+ }
+
+ for (i = 0; i < single_dpm_table->count; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, single_dpm_table->dpm_levels[i].value,
+ (clocks.num_levels == 1) ? "*" :
+ (aldebaran_freqs_in_same_level(
+ clocks.data[i].clocks_in_khz / 1000,
+ now) ? "*" : ""));
+ break;
+
+ default:
+ break;
+ }
+
+ return size;
+}
+
+static int aldebaran_upload_dpm_level(struct smu_context *smu,
+ bool max,
+ uint32_t feature_mask,
+ uint32_t level)
+{
+ struct smu_13_0_dpm_context *dpm_context =
+ smu->smu_dpm.dpm_context;
+ uint32_t freq;
+ int ret = 0;
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT) &&
+ (feature_mask & FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT))) {
+ freq = dpm_context->dpm_tables.gfx_table.dpm_levels[level].value;
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_GFXCLK << 16) | (freq & 0xffff),
+ NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to set soft %s gfxclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT) &&
+ (feature_mask & FEATURE_MASK(FEATURE_DPM_UCLK_BIT))) {
+ freq = dpm_context->dpm_tables.uclk_table.dpm_levels[level].value;
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_UCLK << 16) | (freq & 0xffff),
+ NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to set soft %s memclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT) &&
+ (feature_mask & FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT))) {
+ freq = dpm_context->dpm_tables.soc_table.dpm_levels[level].value;
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ (max ? SMU_MSG_SetSoftMaxByFreq : SMU_MSG_SetSoftMinByFreq),
+ (PPCLK_SOCCLK << 16) | (freq & 0xffff),
+ NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to set soft %s socclk !\n",
+ max ? "max" : "min");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aldebaran_force_clk_levels(struct smu_context *smu,
+ enum smu_clk_type type, uint32_t mask)
+{
+ struct smu_13_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *single_dpm_table = NULL;
+ uint32_t soft_min_level, soft_max_level;
+ int ret = 0;
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ switch (type) {
+ case SMU_SCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
+ if (soft_max_level >= single_dpm_table->count) {
+ dev_err(smu->adev->dev, "Clock level specified %d is over max allowed %d\n",
+ soft_max_level, single_dpm_table->count - 1);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = aldebaran_upload_dpm_level(smu,
+ false,
+ FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT),
+ soft_min_level);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to upload boot level to lowest!\n");
+ break;
+ }
+
+ ret = aldebaran_upload_dpm_level(smu,
+ true,
+ FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT),
+ soft_max_level);
+ if (ret)
+ dev_err(smu->adev->dev, "Failed to upload dpm max level to highest!\n");
+
+ break;
+
+ case SMU_MCLK:
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ /*
+ * Should not arrive here since aldebaran does not
+ * support mclk/socclk/fclk softmin/softmax settings
+ */
+ ret = -EINVAL;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int aldebaran_get_thermal_temperature_range(struct smu_context *smu,
+ struct smu_temperature_range *range)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+
+ if (!range)
+ return -EINVAL;
+
+ memcpy(range, &smu13_thermal_policy[0], sizeof(struct smu_temperature_range));
+
+ range->hotspot_crit_max = pptable->ThotspotLimit *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->hotspot_emergency_max = (pptable->ThotspotLimit + CTF_OFFSET_HOTSPOT) *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->mem_crit_max = pptable->TmemLimit *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->mem_emergency_max = (pptable->TmemLimit + CTF_OFFSET_MEM)*
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
+
+ return 0;
+}
+
+static int aldebaran_get_current_activity_percent(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ uint32_t *value)
+{
+ int ret = 0;
+
+ if (!value)
+ return -EINVAL;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = aldebaran_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXACTIVITY,
+ value);
+ break;
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ ret = aldebaran_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_MEMACTIVITY,
+ value);
+ break;
+ default:
+ dev_err(smu->adev->dev, "Invalid sensor for retrieving clock activity\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aldebaran_get_gpu_power(struct smu_context *smu, uint32_t *value)
+{
+ if (!value)
+ return -EINVAL;
+
+ return aldebaran_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_SOCKETPOWER,
+ value);
+}
+
+static int aldebaran_thermal_get_temperature(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ uint32_t *value)
+{
+ int ret = 0;
+
+ if (!value)
+ return -EINVAL;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = aldebaran_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_HOTSPOT,
+ value);
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = aldebaran_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_EDGE,
+ value);
+ break;
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ ret = aldebaran_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_MEM,
+ value);
+ break;
+ default:
+ dev_err(smu->adev->dev, "Invalid sensor for retrieving temp\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aldebaran_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data, uint32_t *size)
+{
+ int ret = 0;
+
+ if (amdgpu_ras_intr_triggered())
+ return 0;
+
+ if (!data || !size)
+ return -EINVAL;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = aldebaran_get_current_activity_percent(smu,
+ sensor,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = aldebaran_get_gpu_power(smu, (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ ret = aldebaran_thermal_get_temperature(smu, sensor,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_UCLK, (uint32_t *)data);
+ /* the output clock frequency in 10K unit */
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = aldebaran_get_current_clk_freq_by_table(smu, SMU_GFXCLK, (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = smu_v13_0_get_gfx_vdd(smu, (uint32_t *)data);
+ *size = 4;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int aldebaran_get_power_limit(struct smu_context *smu,
+ uint32_t *current_power_limit,
+ uint32_t *default_power_limit,
+ uint32_t *max_power_limit)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ uint32_t power_limit = 0;
+ int ret;
+
+ if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) {
+ if (current_power_limit)
+ *current_power_limit = 0;
+ if (default_power_limit)
+ *default_power_limit = 0;
+ if (max_power_limit)
+ *max_power_limit = 0;
+
+ dev_warn(smu->adev->dev,
+ "PPT feature is not enabled, power values can't be fetched.");
+
+ return 0;
+ }
+
+ /* Valid power data is available only from primary die.
+ * For secondary die show the value as 0.
+ */
+ if (aldebaran_is_primary(smu)) {
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetPptLimit,
+ &power_limit);
+
+ if (ret) {
+ /* the last hope to figure out the ppt limit */
+ if (!pptable) {
+ dev_err(smu->adev->dev,
+ "Cannot get PPT limit due to pptable missing!");
+ return -EINVAL;
+ }
+ power_limit = pptable->PptLimit;
+ }
+ }
+
+ if (current_power_limit)
+ *current_power_limit = power_limit;
+ if (default_power_limit)
+ *default_power_limit = power_limit;
+
+ if (max_power_limit) {
+ if (pptable)
+ *max_power_limit = pptable->PptLimit;
+ }
+
+ return 0;
+}
+
+static int aldebaran_set_power_limit(struct smu_context *smu,
+ enum smu_ppt_limit_type limit_type,
+ uint32_t limit)
+{
+ /* Power limit can be set only through primary die */
+ if (aldebaran_is_primary(smu))
+ return smu_v13_0_set_power_limit(smu, limit_type, limit);
+
+ return -EINVAL;
+}
+
+static int aldebaran_system_features_control(struct smu_context *smu, bool enable)
+{
+ int ret;
+
+ ret = smu_v13_0_system_features_control(smu, enable);
+ if (!ret && enable)
+ ret = aldebaran_run_btc(smu);
+
+ return ret;
+}
+
+static int aldebaran_set_performance_level(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_13_0_dpm_table *gfx_table =
+ &dpm_context->dpm_tables.gfx_table;
+ struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
+
+ /* Disable determinism if switching to another mode */
+ if ((smu_dpm->dpm_level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM) &&
+ (level != AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM)) {
+ smu_cmn_send_smc_msg(smu, SMU_MSG_DisableDeterminism, NULL);
+ pstate_table->gfxclk_pstate.curr.max = gfx_table->max;
+ }
+
+ switch (level) {
+
+ case AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM:
+ return 0;
+
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ default:
+ break;
+ }
+
+ return smu_v13_0_set_performance_level(smu, level);
+}
+
+static int aldebaran_set_soft_freq_limited_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t min,
+ uint32_t max)
+{
+ struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t min_clk;
+ uint32_t max_clk;
+ int ret = 0;
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK)
+ return -EINVAL;
+
+ if ((smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+ && (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM))
+ return -EINVAL;
+
+ if (smu_dpm->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL) {
+ if (min >= max) {
+ dev_err(smu->adev->dev,
+ "Minimum GFX clk should be less than the maximum allowed clock\n");
+ return -EINVAL;
+ }
+
+ if ((min == pstate_table->gfxclk_pstate.curr.min) &&
+ (max == pstate_table->gfxclk_pstate.curr.max))
+ return 0;
+
+ ret = smu_v13_0_set_soft_freq_limited_range(smu, SMU_GFXCLK,
+ min, max);
+ if (!ret) {
+ pstate_table->gfxclk_pstate.curr.min = min;
+ pstate_table->gfxclk_pstate.curr.max = max;
+ }
+
+ return ret;
+ }
+
+ if (smu_dpm->dpm_level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM) {
+ if (!max || (max < dpm_context->dpm_tables.gfx_table.min) ||
+ (max > dpm_context->dpm_tables.gfx_table.max)) {
+ dev_warn(adev->dev,
+ "Invalid max frequency %d MHz specified for determinism\n", max);
+ return -EINVAL;
+ }
+
+ /* Restore default min/max clocks and enable determinism */
+ min_clk = dpm_context->dpm_tables.gfx_table.min;
+ max_clk = dpm_context->dpm_tables.gfx_table.max;
+ ret = smu_v13_0_set_soft_freq_limited_range(smu, SMU_GFXCLK, min_clk, max_clk);
+ if (!ret) {
+ usleep_range(500, 1000);
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_EnableDeterminism,
+ max, NULL);
+ if (ret) {
+ dev_err(adev->dev,
+ "Failed to enable determinism at GFX clock %d MHz\n", max);
+ } else {
+ pstate_table->gfxclk_pstate.curr.min = min_clk;
+ pstate_table->gfxclk_pstate.curr.max = max;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aldebaran_usr_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type,
+ long input[], uint32_t size)
+{
+ struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
+ uint32_t min_clk;
+ uint32_t max_clk;
+ int ret = 0;
+
+ /* Only allowed in manual or determinism mode */
+ if ((smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+ && (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM))
+ return -EINVAL;
+
+ switch (type) {
+ case PP_OD_EDIT_SCLK_VDDC_TABLE:
+ if (size != 2) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+
+ if (input[0] == 0) {
+ if (input[1] < dpm_context->dpm_tables.gfx_table.min) {
+ dev_warn(smu->adev->dev, "Minimum GFX clk (%ld) MHz specified is less than the minimum allowed (%d) MHz\n",
+ input[1], dpm_context->dpm_tables.gfx_table.min);
+ pstate_table->gfxclk_pstate.custom.min =
+ pstate_table->gfxclk_pstate.curr.min;
+ return -EINVAL;
+ }
+
+ pstate_table->gfxclk_pstate.custom.min = input[1];
+ } else if (input[0] == 1) {
+ if (input[1] > dpm_context->dpm_tables.gfx_table.max) {
+ dev_warn(smu->adev->dev, "Maximum GFX clk (%ld) MHz specified is greater than the maximum allowed (%d) MHz\n",
+ input[1], dpm_context->dpm_tables.gfx_table.max);
+ pstate_table->gfxclk_pstate.custom.max =
+ pstate_table->gfxclk_pstate.curr.max;
+ return -EINVAL;
+ }
+
+ pstate_table->gfxclk_pstate.custom.max = input[1];
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case PP_OD_RESTORE_DEFAULT_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ } else {
+ /* Use the default frequencies for manual and determinism mode */
+ min_clk = dpm_context->dpm_tables.gfx_table.min;
+ max_clk = dpm_context->dpm_tables.gfx_table.max;
+
+ return aldebaran_set_soft_freq_limited_range(smu, SMU_GFXCLK, min_clk, max_clk);
+ }
+ break;
+ case PP_OD_COMMIT_DPM_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ } else {
+ if (!pstate_table->gfxclk_pstate.custom.min)
+ pstate_table->gfxclk_pstate.custom.min =
+ pstate_table->gfxclk_pstate.curr.min;
+
+ if (!pstate_table->gfxclk_pstate.custom.max)
+ pstate_table->gfxclk_pstate.custom.max =
+ pstate_table->gfxclk_pstate.curr.max;
+
+ min_clk = pstate_table->gfxclk_pstate.custom.min;
+ max_clk = pstate_table->gfxclk_pstate.custom.max;
+
+ return aldebaran_set_soft_freq_limited_range(smu, SMU_GFXCLK, min_clk, max_clk);
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return ret;
+}
+
+static bool aldebaran_is_dpm_running(struct smu_context *smu)
+{
+ int ret;
+ uint64_t feature_enabled;
+
+ ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
+ if (ret)
+ return false;
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static int aldebaran_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msg, int num_msgs)
+{
+ struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
+ struct amdgpu_device *adev = smu_i2c->adev;
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *table = &smu_table->driver_table;
+ SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
+ int i, j, r, c;
+ u16 dir;
+
+ if (!adev->pm.dpm_enabled)
+ return -EBUSY;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->I2CcontrollerPort = smu_i2c->port;
+ req->I2CSpeed = I2C_SPEED_FAST_400K;
+ req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
+ dir = msg[0].flags & I2C_M_RD;
+
+ for (c = i = 0; i < num_msgs; i++) {
+ for (j = 0; j < msg[i].len; j++, c++) {
+ SwI2cCmd_t *cmd = &req->SwI2cCmds[c];
+
+ if (!(msg[i].flags & I2C_M_RD)) {
+ /* write */
+ cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
+ cmd->ReadWriteData = msg[i].buf[j];
+ }
+
+ if ((dir ^ msg[i].flags) & I2C_M_RD) {
+ /* The direction changes.
+ */
+ dir = msg[i].flags & I2C_M_RD;
+ cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
+ }
+
+ req->NumCmds++;
+
+ /*
+ * Insert STOP if we are at the last byte of either last
+ * message for the transaction or the client explicitly
+ * requires a STOP at this particular message.
+ */
+ if ((j == msg[i].len - 1) &&
+ ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
+ cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
+ cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
+ }
+ }
+ }
+ mutex_lock(&adev->pm.mutex);
+ r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
+ mutex_unlock(&adev->pm.mutex);
+ if (r)
+ goto fail;
+
+ for (c = i = 0; i < num_msgs; i++) {
+ if (!(msg[i].flags & I2C_M_RD)) {
+ c += msg[i].len;
+ continue;
+ }
+ for (j = 0; j < msg[i].len; j++, c++) {
+ SwI2cCmd_t *cmd = &res->SwI2cCmds[c];
+
+ msg[i].buf[j] = cmd->ReadWriteData;
+ }
+ }
+ r = num_msgs;
+fail:
+ kfree(req);
+ return r;
+}
+
+static u32 aldebaran_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+
+static const struct i2c_algorithm aldebaran_i2c_algo = {
+ .master_xfer = aldebaran_i2c_xfer,
+ .functionality = aldebaran_i2c_func,
+};
+
+static const struct i2c_adapter_quirks aldebaran_i2c_control_quirks = {
+ .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR | I2C_AQ_NO_ZERO_LEN,
+ .max_read_len = MAX_SW_I2C_COMMANDS,
+ .max_write_len = MAX_SW_I2C_COMMANDS,
+ .max_comb_1st_msg_len = 2,
+ .max_comb_2nd_msg_len = MAX_SW_I2C_COMMANDS - 2,
+};
+
+static int aldebaran_i2c_control_init(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[0];
+ struct i2c_adapter *control = &smu_i2c->adapter;
+ int res;
+
+ smu_i2c->adev = adev;
+ smu_i2c->port = 0;
+ mutex_init(&smu_i2c->mutex);
+ control->owner = THIS_MODULE;
+ control->class = I2C_CLASS_SPD;
+ control->dev.parent = &adev->pdev->dev;
+ control->algo = &aldebaran_i2c_algo;
+ snprintf(control->name, sizeof(control->name), "AMDGPU SMU 0");
+ control->quirks = &aldebaran_i2c_control_quirks;
+ i2c_set_adapdata(control, smu_i2c);
+
+ res = i2c_add_adapter(control);
+ if (res) {
+ DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
+ goto Out_err;
+ }
+
+ adev->pm.ras_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
+ adev->pm.fru_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
+
+ return 0;
+Out_err:
+ i2c_del_adapter(control);
+
+ return res;
+}
+
+static void aldebaran_i2c_control_fini(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int i;
+
+ for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
+ struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
+ struct i2c_adapter *control = &smu_i2c->adapter;
+
+ i2c_del_adapter(control);
+ }
+ adev->pm.ras_eeprom_i2c_bus = NULL;
+ adev->pm.fru_eeprom_i2c_bus = NULL;
+}
+
+static void aldebaran_get_unique_id(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t upper32 = 0, lower32 = 0;
+
+ if (aldebaran_get_smu_metrics_data(smu, METRICS_UNIQUE_ID_UPPER32, &upper32))
+ goto out;
+ if (aldebaran_get_smu_metrics_data(smu, METRICS_UNIQUE_ID_LOWER32, &lower32))
+ goto out;
+
+out:
+ adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
+ if (adev->serial[0] == '\0')
+ sprintf(adev->serial, "%016llx", adev->unique_id);
+}
+
+static bool aldebaran_is_baco_supported(struct smu_context *smu)
+{
+ /* aldebaran is not support baco */
+
+ return false;
+}
+
+static int aldebaran_set_df_cstate(struct smu_context *smu,
+ enum pp_df_cstate state)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ /*
+ * Aldebaran does not need the cstate disablement
+ * prerequisite for gpu reset.
+ */
+ if (amdgpu_in_reset(adev) || adev->in_suspend)
+ return 0;
+
+ return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_DFCstateControl, state, NULL);
+}
+
+static int aldebaran_allow_xgmi_power_down(struct smu_context *smu, bool en)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ /* The message only works on master die and NACK will be sent
+ back for other dies, only send it on master die */
+ if (!adev->smuio.funcs->get_socket_id(adev) &&
+ !adev->smuio.funcs->get_die_id(adev))
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GmiPwrDnControl,
+ en ? 0 : 1,
+ NULL);
+ else
+ return 0;
+}
+
+static const struct throttling_logging_label {
+ uint32_t feature_mask;
+ const char *label;
+} logging_label[] = {
+ {(1U << THROTTLER_TEMP_GPU_BIT), "GPU"},
+ {(1U << THROTTLER_TEMP_MEM_BIT), "HBM"},
+ {(1U << THROTTLER_TEMP_VR_GFX_BIT), "VR of GFX rail"},
+ {(1U << THROTTLER_TEMP_VR_MEM_BIT), "VR of HBM rail"},
+ {(1U << THROTTLER_TEMP_VR_SOC_BIT), "VR of SOC rail"},
+};
+static void aldebaran_log_thermal_throttling_event(struct smu_context *smu)
+{
+ int ret;
+ int throttler_idx, throtting_events = 0, buf_idx = 0;
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t throttler_status;
+ char log_buf[256];
+
+ ret = aldebaran_get_smu_metrics_data(smu,
+ METRICS_THROTTLER_STATUS,
+ &throttler_status);
+ if (ret)
+ return;
+
+ memset(log_buf, 0, sizeof(log_buf));
+ for (throttler_idx = 0; throttler_idx < ARRAY_SIZE(logging_label);
+ throttler_idx++) {
+ if (throttler_status & logging_label[throttler_idx].feature_mask) {
+ throtting_events++;
+ buf_idx += snprintf(log_buf + buf_idx,
+ sizeof(log_buf) - buf_idx,
+ "%s%s",
+ throtting_events > 1 ? " and " : "",
+ logging_label[throttler_idx].label);
+ if (buf_idx >= sizeof(log_buf)) {
+ dev_err(adev->dev, "buffer overflow!\n");
+ log_buf[sizeof(log_buf) - 1] = '\0';
+ break;
+ }
+ }
+ }
+
+ dev_warn(adev->dev, "WARN: GPU thermal throttling temperature reached, expect performance decrease. %s.\n",
+ log_buf);
+ kgd2kfd_smi_event_throttle(smu->adev->kfd.dev,
+ smu_cmn_get_indep_throttler_status(throttler_status,
+ aldebaran_throttler_map));
+}
+
+static int aldebaran_get_current_pcie_link_speed(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t esm_ctrl;
+
+ /* TODO: confirm this on real target */
+ esm_ctrl = RREG32_PCIE(smnPCIE_ESM_CTRL);
+ if ((esm_ctrl >> 15) & 0x1FFFF)
+ return (((esm_ctrl >> 8) & 0x3F) + 128);
+
+ return smu_v13_0_get_current_pcie_link_speed(smu);
+}
+
+static ssize_t aldebaran_get_gpu_metrics(struct smu_context *smu,
+ void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct gpu_metrics_v1_3 *gpu_metrics =
+ (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
+ SmuMetrics_t metrics;
+ int i, ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu,
+ &metrics,
+ true);
+ if (ret)
+ return ret;
+
+ smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
+
+ gpu_metrics->temperature_edge = metrics.TemperatureEdge;
+ gpu_metrics->temperature_hotspot = metrics.TemperatureHotspot;
+ gpu_metrics->temperature_mem = metrics.TemperatureHBM;
+ gpu_metrics->temperature_vrgfx = metrics.TemperatureVrGfx;
+ gpu_metrics->temperature_vrsoc = metrics.TemperatureVrSoc;
+ gpu_metrics->temperature_vrmem = metrics.TemperatureVrMem;
+
+ gpu_metrics->average_gfx_activity = metrics.AverageGfxActivity;
+ gpu_metrics->average_umc_activity = metrics.AverageUclkActivity;
+ gpu_metrics->average_mm_activity = 0;
+
+ /* Valid power data is available only from primary die */
+ if (aldebaran_is_primary(smu)) {
+ gpu_metrics->average_socket_power = metrics.AverageSocketPower;
+ gpu_metrics->energy_accumulator =
+ (uint64_t)metrics.EnergyAcc64bitHigh << 32 |
+ metrics.EnergyAcc64bitLow;
+ } else {
+ gpu_metrics->average_socket_power = 0;
+ gpu_metrics->energy_accumulator = 0;
+ }
+
+ gpu_metrics->average_gfxclk_frequency = metrics.AverageGfxclkFrequency;
+ gpu_metrics->average_socclk_frequency = metrics.AverageSocclkFrequency;
+ gpu_metrics->average_uclk_frequency = metrics.AverageUclkFrequency;
+ gpu_metrics->average_vclk0_frequency = 0;
+ gpu_metrics->average_dclk0_frequency = 0;
+
+ gpu_metrics->current_gfxclk = metrics.CurrClock[PPCLK_GFXCLK];
+ gpu_metrics->current_socclk = metrics.CurrClock[PPCLK_SOCCLK];
+ gpu_metrics->current_uclk = metrics.CurrClock[PPCLK_UCLK];
+ gpu_metrics->current_vclk0 = metrics.CurrClock[PPCLK_VCLK];
+ gpu_metrics->current_dclk0 = metrics.CurrClock[PPCLK_DCLK];
+
+ gpu_metrics->throttle_status = metrics.ThrottlerStatus;
+ gpu_metrics->indep_throttle_status =
+ smu_cmn_get_indep_throttler_status(metrics.ThrottlerStatus,
+ aldebaran_throttler_map);
+
+ gpu_metrics->current_fan_speed = 0;
+
+ gpu_metrics->pcie_link_width =
+ smu_v13_0_get_current_pcie_link_width(smu);
+ gpu_metrics->pcie_link_speed =
+ aldebaran_get_current_pcie_link_speed(smu);
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+
+ gpu_metrics->gfx_activity_acc = metrics.GfxBusyAcc;
+ gpu_metrics->mem_activity_acc = metrics.DramBusyAcc;
+
+ for (i = 0; i < NUM_HBM_INSTANCES; i++)
+ gpu_metrics->temperature_hbm[i] = metrics.TemperatureAllHBM[i];
+
+ gpu_metrics->firmware_timestamp = ((uint64_t)metrics.TimeStampHigh << 32) |
+ metrics.TimeStampLow;
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v1_3);
+}
+
+static int aldebaran_check_ecc_table_support(struct smu_context *smu,
+ int *ecctable_version)
+{
+ uint32_t if_version = 0xff, smu_version = 0xff;
+ int ret = 0;
+
+ ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version);
+ if (ret) {
+ /* return not support if failed get smu_version */
+ ret = -EOPNOTSUPP;
+ }
+
+ if (smu_version < SUPPORT_ECCTABLE_SMU_VERSION)
+ ret = -EOPNOTSUPP;
+ else if (smu_version >= SUPPORT_ECCTABLE_SMU_VERSION &&
+ smu_version < SUPPORT_ECCTABLE_V2_SMU_VERSION)
+ *ecctable_version = 1;
+ else
+ *ecctable_version = 2;
+
+ return ret;
+}
+
+static ssize_t aldebaran_get_ecc_info(struct smu_context *smu,
+ void *table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ EccInfoTable_t *ecc_table = NULL;
+ struct ecc_info_per_ch *ecc_info_per_channel = NULL;
+ int i, ret = 0;
+ int table_version = 0;
+ struct umc_ecc_info *eccinfo = (struct umc_ecc_info *)table;
+
+ ret = aldebaran_check_ecc_table_support(smu, &table_version);
+ if (ret)
+ return ret;
+
+ ret = smu_cmn_update_table(smu,
+ SMU_TABLE_ECCINFO,
+ 0,
+ smu_table->ecc_table,
+ false);
+ if (ret) {
+ dev_info(smu->adev->dev, "Failed to export SMU ecc table!\n");
+ return ret;
+ }
+
+ ecc_table = (EccInfoTable_t *)smu_table->ecc_table;
+
+ if (table_version == 1) {
+ for (i = 0; i < ALDEBARAN_UMC_CHANNEL_NUM; i++) {
+ ecc_info_per_channel = &(eccinfo->ecc[i]);
+ ecc_info_per_channel->ce_count_lo_chip =
+ ecc_table->EccInfo[i].ce_count_lo_chip;
+ ecc_info_per_channel->ce_count_hi_chip =
+ ecc_table->EccInfo[i].ce_count_hi_chip;
+ ecc_info_per_channel->mca_umc_status =
+ ecc_table->EccInfo[i].mca_umc_status;
+ ecc_info_per_channel->mca_umc_addr =
+ ecc_table->EccInfo[i].mca_umc_addr;
+ }
+ } else if (table_version == 2) {
+ for (i = 0; i < ALDEBARAN_UMC_CHANNEL_NUM; i++) {
+ ecc_info_per_channel = &(eccinfo->ecc[i]);
+ ecc_info_per_channel->ce_count_lo_chip =
+ ecc_table->EccInfo_V2[i].ce_count_lo_chip;
+ ecc_info_per_channel->ce_count_hi_chip =
+ ecc_table->EccInfo_V2[i].ce_count_hi_chip;
+ ecc_info_per_channel->mca_umc_status =
+ ecc_table->EccInfo_V2[i].mca_umc_status;
+ ecc_info_per_channel->mca_umc_addr =
+ ecc_table->EccInfo_V2[i].mca_umc_addr;
+ ecc_info_per_channel->mca_ceumc_addr =
+ ecc_table->EccInfo_V2[i].mca_ceumc_addr;
+ }
+ eccinfo->record_ce_addr_supported = 1;
+ }
+
+ return ret;
+}
+
+static int aldebaran_mode1_reset(struct smu_context *smu)
+{
+ u32 smu_version, fatal_err, param;
+ int ret = 0;
+ struct amdgpu_device *adev = smu->adev;
+ struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
+
+ fatal_err = 0;
+ param = SMU_RESET_MODE_1;
+
+ /*
+ * PM FW support SMU_MSG_GfxDeviceDriverReset from 68.07
+ */
+ smu_cmn_get_smc_version(smu, NULL, &smu_version);
+ if (smu_version < 0x00440700) {
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_Mode1Reset, NULL);
+ }
+ else {
+ /* fatal error triggered by ras, PMFW supports the flag
+ from 68.44.0 */
+ if ((smu_version >= 0x00442c00) && ras &&
+ atomic_read(&ras->in_recovery))
+ fatal_err = 1;
+
+ param |= (fatal_err << 16);
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GfxDeviceDriverReset, param, NULL);
+ }
+
+ if (!ret)
+ msleep(SMU13_MODE1_RESET_WAIT_TIME_IN_MS);
+
+ return ret;
+}
+
+static int aldebaran_mode2_reset(struct smu_context *smu)
+{
+ u32 smu_version;
+ int ret = 0, index;
+ struct amdgpu_device *adev = smu->adev;
+ int timeout = 10;
+
+ smu_cmn_get_smc_version(smu, NULL, &smu_version);
+
+ index = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_MSG,
+ SMU_MSG_GfxDeviceDriverReset);
+
+ mutex_lock(&smu->message_lock);
+ if (smu_version >= 0x00441400) {
+ ret = smu_cmn_send_msg_without_waiting(smu, (uint16_t)index, SMU_RESET_MODE_2);
+ /* This is similar to FLR, wait till max FLR timeout */
+ msleep(100);
+ dev_dbg(smu->adev->dev, "restore config space...\n");
+ /* Restore the config space saved during init */
+ amdgpu_device_load_pci_state(adev->pdev);
+
+ dev_dbg(smu->adev->dev, "wait for reset ack\n");
+ while (ret == -ETIME && timeout) {
+ ret = smu_cmn_wait_for_response(smu);
+ /* Wait a bit more time for getting ACK */
+ if (ret == -ETIME) {
+ --timeout;
+ usleep_range(500, 1000);
+ continue;
+ }
+
+ if (ret != 1) {
+ dev_err(adev->dev, "failed to send mode2 message \tparam: 0x%08x response %#x\n",
+ SMU_RESET_MODE_2, ret);
+ goto out;
+ }
+ }
+
+ } else {
+ dev_err(adev->dev, "smu fw 0x%x does not support MSG_GfxDeviceDriverReset MSG\n",
+ smu_version);
+ }
+
+ if (ret == 1)
+ ret = 0;
+out:
+ mutex_unlock(&smu->message_lock);
+
+ return ret;
+}
+
+static int aldebaran_smu_handle_passthrough_sbr(struct smu_context *smu, bool enable)
+{
+ int ret = 0;
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_HeavySBR, enable ? 1 : 0, NULL);
+
+ return ret;
+}
+
+static bool aldebaran_is_mode1_reset_supported(struct smu_context *smu)
+{
+#if 0
+ struct amdgpu_device *adev = smu->adev;
+ u32 smu_version;
+ uint32_t val;
+ /**
+ * PM FW version support mode1 reset from 68.07
+ */
+ smu_cmn_get_smc_version(smu, NULL, &smu_version);
+ if ((smu_version < 0x00440700))
+ return false;
+ /**
+ * mode1 reset relies on PSP, so we should check if
+ * PSP is alive.
+ */
+ val = RREG32_SOC15(MP0, 0, regMP0_SMN_C2PMSG_81);
+
+ return val != 0x0;
+#endif
+ return true;
+}
+
+static bool aldebaran_is_mode2_reset_supported(struct smu_context *smu)
+{
+ return true;
+}
+
+static int aldebaran_set_mp1_state(struct smu_context *smu,
+ enum pp_mp1_state mp1_state)
+{
+ switch (mp1_state) {
+ case PP_MP1_STATE_UNLOAD:
+ return smu_cmn_set_mp1_state(smu, mp1_state);
+ default:
+ return 0;
+ }
+}
+
+static int aldebaran_smu_send_hbm_bad_page_num(struct smu_context *smu,
+ uint32_t size)
+{
+ int ret = 0;
+
+ /* message SMU to update the bad page number on SMUBUS */
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetNumBadHbmPagesRetired, size, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "[%s] failed to message SMU to update HBM bad pages number\n",
+ __func__);
+
+ return ret;
+}
+
+static int aldebaran_check_bad_channel_info_support(struct smu_context *smu)
+{
+ uint32_t if_version = 0xff, smu_version = 0xff;
+ int ret = 0;
+
+ ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version);
+ if (ret) {
+ /* return not support if failed get smu_version */
+ ret = -EOPNOTSUPP;
+ }
+
+ if (smu_version < SUPPORT_BAD_CHANNEL_INFO_MSG_VERSION)
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+static int aldebaran_send_hbm_bad_channel_flag(struct smu_context *smu,
+ uint32_t size)
+{
+ int ret = 0;
+
+ ret = aldebaran_check_bad_channel_info_support(smu);
+ if (ret)
+ return ret;
+
+ /* message SMU to update the bad channel info on SMUBUS */
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetBadHBMPagesRetiredFlagsPerChannel, size, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "[%s] failed to message SMU to update HBM bad channel info\n",
+ __func__);
+
+ return ret;
+}
+
+static const struct pptable_funcs aldebaran_ppt_funcs = {
+ /* init dpm */
+ .get_allowed_feature_mask = aldebaran_get_allowed_feature_mask,
+ /* dpm/clk tables */
+ .set_default_dpm_table = aldebaran_set_default_dpm_table,
+ .populate_umd_state_clk = aldebaran_populate_umd_state_clk,
+ .get_thermal_temperature_range = aldebaran_get_thermal_temperature_range,
+ .print_clk_levels = aldebaran_print_clk_levels,
+ .force_clk_levels = aldebaran_force_clk_levels,
+ .read_sensor = aldebaran_read_sensor,
+ .set_performance_level = aldebaran_set_performance_level,
+ .get_power_limit = aldebaran_get_power_limit,
+ .is_dpm_running = aldebaran_is_dpm_running,
+ .get_unique_id = aldebaran_get_unique_id,
+ .init_microcode = smu_v13_0_init_microcode,
+ .load_microcode = smu_v13_0_load_microcode,
+ .fini_microcode = smu_v13_0_fini_microcode,
+ .init_smc_tables = aldebaran_init_smc_tables,
+ .fini_smc_tables = smu_v13_0_fini_smc_tables,
+ .init_power = smu_v13_0_init_power,
+ .fini_power = smu_v13_0_fini_power,
+ .check_fw_status = smu_v13_0_check_fw_status,
+ /* pptable related */
+ .setup_pptable = aldebaran_setup_pptable,
+ .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values,
+ .check_fw_version = smu_v13_0_check_fw_version,
+ .write_pptable = smu_cmn_write_pptable,
+ .set_driver_table_location = smu_v13_0_set_driver_table_location,
+ .set_tool_table_location = smu_v13_0_set_tool_table_location,
+ .notify_memory_pool_location = smu_v13_0_notify_memory_pool_location,
+ .system_features_control = aldebaran_system_features_control,
+ .send_smc_msg_with_param = smu_cmn_send_smc_msg_with_param,
+ .send_smc_msg = smu_cmn_send_smc_msg,
+ .get_enabled_mask = smu_cmn_get_enabled_mask,
+ .feature_is_enabled = smu_cmn_feature_is_enabled,
+ .disable_all_features_with_exception = smu_cmn_disable_all_features_with_exception,
+ .set_power_limit = aldebaran_set_power_limit,
+ .init_max_sustainable_clocks = smu_v13_0_init_max_sustainable_clocks,
+ .enable_thermal_alert = smu_v13_0_enable_thermal_alert,
+ .disable_thermal_alert = smu_v13_0_disable_thermal_alert,
+ .set_xgmi_pstate = smu_v13_0_set_xgmi_pstate,
+ .register_irq_handler = smu_v13_0_register_irq_handler,
+ .set_azalia_d3_pme = smu_v13_0_set_azalia_d3_pme,
+ .get_max_sustainable_clocks_by_dc = smu_v13_0_get_max_sustainable_clocks_by_dc,
+ .baco_is_support= aldebaran_is_baco_supported,
+ .get_dpm_ultimate_freq = smu_v13_0_get_dpm_ultimate_freq,
+ .set_soft_freq_limited_range = aldebaran_set_soft_freq_limited_range,
+ .od_edit_dpm_table = aldebaran_usr_edit_dpm_table,
+ .set_df_cstate = aldebaran_set_df_cstate,
+ .allow_xgmi_power_down = aldebaran_allow_xgmi_power_down,
+ .log_thermal_throttling_event = aldebaran_log_thermal_throttling_event,
+ .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
+ .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
+ .get_gpu_metrics = aldebaran_get_gpu_metrics,
+ .mode1_reset_is_support = aldebaran_is_mode1_reset_supported,
+ .mode2_reset_is_support = aldebaran_is_mode2_reset_supported,
+ .smu_handle_passthrough_sbr = aldebaran_smu_handle_passthrough_sbr,
+ .mode1_reset = aldebaran_mode1_reset,
+ .set_mp1_state = aldebaran_set_mp1_state,
+ .mode2_reset = aldebaran_mode2_reset,
+ .wait_for_event = smu_v13_0_wait_for_event,
+ .i2c_init = aldebaran_i2c_control_init,
+ .i2c_fini = aldebaran_i2c_control_fini,
+ .send_hbm_bad_pages_num = aldebaran_smu_send_hbm_bad_page_num,
+ .get_ecc_info = aldebaran_get_ecc_info,
+ .send_hbm_bad_channel_flag = aldebaran_send_hbm_bad_channel_flag,
+};
+
+void aldebaran_set_ppt_funcs(struct smu_context *smu)
+{
+ smu->ppt_funcs = &aldebaran_ppt_funcs;
+ smu->message_map = aldebaran_message_map;
+ smu->clock_map = aldebaran_clk_map;
+ smu->feature_map = aldebaran_feature_mask_map;
+ smu->table_map = aldebaran_table_map;
+ smu_v13_0_set_smu_mailbox_registers(smu);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.h
new file mode 100644
index 000000000..31e991bde
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __ALDEBARAN_PPT_H__
+#define __ALDEBARAN_PPT_H__
+
+#define ALDEBARAN_UMD_PSTATE_GFXCLK_LEVEL 0x3
+#define ALDEBARAN_UMD_PSTATE_SOCCLK_LEVEL 0x3
+#define ALDEBARAN_UMD_PSTATE_MCLK_LEVEL 0x2
+
+#define MAX_DPM_NUMBER 16
+#define ALDEBARAN_MAX_PCIE_CONF 2
+
+struct aldebaran_dpm_level {
+ bool enabled;
+ uint32_t value;
+ uint32_t param1;
+};
+
+struct aldebaran_dpm_state {
+ uint32_t soft_min_level;
+ uint32_t soft_max_level;
+ uint32_t hard_min_level;
+ uint32_t hard_max_level;
+};
+
+struct aldebaran_single_dpm_table {
+ uint32_t count;
+ struct aldebaran_dpm_state dpm_state;
+ struct aldebaran_dpm_level dpm_levels[MAX_DPM_NUMBER];
+};
+
+struct aldebaran_pcie_table {
+ uint16_t count;
+ uint8_t pcie_gen[ALDEBARAN_MAX_PCIE_CONF];
+ uint8_t pcie_lane[ALDEBARAN_MAX_PCIE_CONF];
+ uint32_t lclk[ALDEBARAN_MAX_PCIE_CONF];
+};
+
+struct aldebaran_dpm_table {
+ struct aldebaran_single_dpm_table soc_table;
+ struct aldebaran_single_dpm_table gfx_table;
+ struct aldebaran_single_dpm_table mem_table;
+ struct aldebaran_single_dpm_table eclk_table;
+ struct aldebaran_single_dpm_table vclk_table;
+ struct aldebaran_single_dpm_table dclk_table;
+ struct aldebaran_single_dpm_table fclk_table;
+ struct aldebaran_pcie_table pcie_table;
+};
+
+extern void aldebaran_set_ppt_funcs(struct smu_context *smu);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
new file mode 100644
index 000000000..b4373b656
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
@@ -0,0 +1,2491 @@
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/reboot.h>
+
+#define SMU_13_0_PARTIAL_PPTABLE
+#define SWSMU_CODE_LAYER_L3
+
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "atomfirmware.h"
+#include "amdgpu_atomfirmware.h"
+#include "amdgpu_atombios.h"
+#include "smu_v13_0.h"
+#include "soc15_common.h"
+#include "atom.h"
+#include "amdgpu_ras.h"
+#include "smu_cmn.h"
+
+#include "asic_reg/thm/thm_13_0_2_offset.h"
+#include "asic_reg/thm/thm_13_0_2_sh_mask.h"
+#include "asic_reg/mp/mp_13_0_2_offset.h"
+#include "asic_reg/mp/mp_13_0_2_sh_mask.h"
+#include "asic_reg/smuio/smuio_13_0_2_offset.h"
+#include "asic_reg/smuio/smuio_13_0_2_sh_mask.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+MODULE_FIRMWARE("amdgpu/aldebaran_smc.bin");
+MODULE_FIRMWARE("amdgpu/smu_13_0_0.bin");
+MODULE_FIRMWARE("amdgpu/smu_13_0_7.bin");
+MODULE_FIRMWARE("amdgpu/smu_13_0_10.bin");
+
+#define mmMP1_SMN_C2PMSG_66 0x0282
+#define mmMP1_SMN_C2PMSG_66_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_82 0x0292
+#define mmMP1_SMN_C2PMSG_82_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_90 0x029a
+#define mmMP1_SMN_C2PMSG_90_BASE_IDX 0
+
+#define SMU13_VOLTAGE_SCALE 4
+
+#define LINK_WIDTH_MAX 6
+#define LINK_SPEED_MAX 3
+
+#define smnPCIE_LC_LINK_WIDTH_CNTL 0x11140288
+#define PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK 0x00000070L
+#define PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT 0x4
+#define smnPCIE_LC_SPEED_CNTL 0x11140290
+#define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK 0xC000
+#define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT 0xE
+
+static const int link_width[] = {0, 1, 2, 4, 8, 12, 16};
+static const int link_speed[] = {25, 50, 80, 160};
+
+int smu_v13_0_init_microcode(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ const char *chip_name;
+ char fw_name[30];
+ char ucode_prefix[30];
+ int err = 0;
+ const struct smc_firmware_header_v1_0 *hdr;
+ const struct common_firmware_header *header;
+ struct amdgpu_firmware_info *ucode = NULL;
+
+ /* doesn't need to load smu firmware in IOV mode */
+ if (amdgpu_sriov_vf(adev))
+ return 0;
+
+ switch (adev->ip_versions[MP1_HWIP][0]) {
+ case IP_VERSION(13, 0, 2):
+ chip_name = "aldebaran_smc";
+ break;
+ default:
+ amdgpu_ucode_ip_version_decode(adev, MP1_HWIP, ucode_prefix, sizeof(ucode_prefix));
+ chip_name = ucode_prefix;
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "amdgpu/%s.bin", chip_name);
+
+ err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
+ if (err)
+ goto out;
+ err = amdgpu_ucode_validate(adev->pm.fw);
+ if (err)
+ goto out;
+
+ hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
+ amdgpu_ucode_print_smc_hdr(&hdr->header);
+ adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
+
+ if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+ ucode = &adev->firmware.ucode[AMDGPU_UCODE_ID_SMC];
+ ucode->ucode_id = AMDGPU_UCODE_ID_SMC;
+ ucode->fw = adev->pm.fw;
+ header = (const struct common_firmware_header *)ucode->fw->data;
+ adev->firmware.fw_size +=
+ ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE);
+ }
+
+out:
+ if (err) {
+ DRM_ERROR("smu_v13_0: Failed to load firmware \"%s\"\n",
+ fw_name);
+ release_firmware(adev->pm.fw);
+ adev->pm.fw = NULL;
+ }
+ return err;
+}
+
+void smu_v13_0_fini_microcode(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ release_firmware(adev->pm.fw);
+ adev->pm.fw = NULL;
+ adev->pm.fw_version = 0;
+}
+
+int smu_v13_0_load_microcode(struct smu_context *smu)
+{
+#if 0
+ struct amdgpu_device *adev = smu->adev;
+ const uint32_t *src;
+ const struct smc_firmware_header_v1_0 *hdr;
+ uint32_t addr_start = MP1_SRAM;
+ uint32_t i;
+ uint32_t smc_fw_size;
+ uint32_t mp1_fw_flags;
+
+ hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
+ src = (const uint32_t *)(adev->pm.fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ smc_fw_size = hdr->header.ucode_size_bytes;
+
+ for (i = 1; i < smc_fw_size/4 - 1; i++) {
+ WREG32_PCIE(addr_start, src[i]);
+ addr_start += 4;
+ }
+
+ WREG32_PCIE(MP1_Public | (smnMP1_PUB_CTRL & 0xffffffff),
+ 1 & MP1_SMN_PUB_CTRL__RESET_MASK);
+ WREG32_PCIE(MP1_Public | (smnMP1_PUB_CTRL & 0xffffffff),
+ 1 & ~MP1_SMN_PUB_CTRL__RESET_MASK);
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
+ if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
+ MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
+ break;
+ udelay(1);
+ }
+
+ if (i == adev->usec_timeout)
+ return -ETIME;
+#endif
+
+ return 0;
+}
+
+int smu_v13_0_init_pptable_microcode(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct amdgpu_firmware_info *ucode = NULL;
+ uint32_t size = 0, pptable_id = 0;
+ int ret = 0;
+ void *table;
+
+ /* doesn't need to load smu firmware in IOV mode */
+ if (amdgpu_sriov_vf(adev))
+ return 0;
+
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
+ return 0;
+
+ if (!adev->scpm_enabled)
+ return 0;
+
+ if ((adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 7)) ||
+ (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 0)) ||
+ (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 10)))
+ return 0;
+
+ /* override pptable_id from driver parameter */
+ if (amdgpu_smu_pptable_id >= 0) {
+ pptable_id = amdgpu_smu_pptable_id;
+ dev_info(adev->dev, "override pptable id %d\n", pptable_id);
+ } else {
+ pptable_id = smu->smu_table.boot_values.pp_table_id;
+ }
+
+ /* "pptable_id == 0" means vbios carries the pptable. */
+ if (!pptable_id)
+ return 0;
+
+ ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ if (ret)
+ return ret;
+
+ smu->pptable_firmware.data = table;
+ smu->pptable_firmware.size = size;
+
+ ucode = &adev->firmware.ucode[AMDGPU_UCODE_ID_PPTABLE];
+ ucode->ucode_id = AMDGPU_UCODE_ID_PPTABLE;
+ ucode->fw = &smu->pptable_firmware;
+ adev->firmware.fw_size +=
+ ALIGN(smu->pptable_firmware.size, PAGE_SIZE);
+
+ return 0;
+}
+
+int smu_v13_0_check_fw_status(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t mp1_fw_flags;
+
+ switch (adev->ip_versions[MP1_HWIP][0]) {
+ case IP_VERSION(13, 0, 4):
+ case IP_VERSION(13, 0, 11):
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_V13_0_4_FIRMWARE_FLAGS & 0xffffffff));
+ break;
+ default:
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS & 0xffffffff));
+ break;
+ }
+
+ if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
+ MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
+ return 0;
+
+ return -EIO;
+}
+
+int smu_v13_0_check_fw_version(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t if_version = 0xff, smu_version = 0xff;
+ uint8_t smu_program, smu_major, smu_minor, smu_debug;
+ int ret = 0;
+
+ ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version);
+ if (ret)
+ return ret;
+
+ smu_program = (smu_version >> 24) & 0xff;
+ smu_major = (smu_version >> 16) & 0xff;
+ smu_minor = (smu_version >> 8) & 0xff;
+ smu_debug = (smu_version >> 0) & 0xff;
+ if (smu->is_apu)
+ adev->pm.fw_version = smu_version;
+
+ switch (adev->ip_versions[MP1_HWIP][0]) {
+ case IP_VERSION(13, 0, 2):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_ALDE;
+ break;
+ case IP_VERSION(13, 0, 0):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_0;
+ break;
+ case IP_VERSION(13, 0, 10):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_10;
+ break;
+ case IP_VERSION(13, 0, 7):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_7;
+ break;
+ case IP_VERSION(13, 0, 1):
+ case IP_VERSION(13, 0, 3):
+ case IP_VERSION(13, 0, 8):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_YELLOW_CARP;
+ break;
+ case IP_VERSION(13, 0, 4):
+ case IP_VERSION(13, 0, 11):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_4;
+ break;
+ case IP_VERSION(13, 0, 5):
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_5;
+ break;
+ default:
+ dev_err(adev->dev, "smu unsupported IP version: 0x%x.\n",
+ adev->ip_versions[MP1_HWIP][0]);
+ smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_INV;
+ break;
+ }
+
+ /* only for dGPU w/ SMU13*/
+ if (adev->pm.fw)
+ dev_dbg(smu->adev->dev, "smu fw reported program %d, version = 0x%08x (%d.%d.%d)\n",
+ smu_program, smu_version, smu_major, smu_minor, smu_debug);
+
+ /*
+ * 1. if_version mismatch is not critical as our fw is designed
+ * to be backward compatible.
+ * 2. New fw usually brings some optimizations. But that's visible
+ * only on the paired driver.
+ * Considering above, we just leave user a warning message instead
+ * of halt driver loading.
+ */
+ if (if_version != smu->smc_driver_if_version) {
+ dev_info(adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, "
+ "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n",
+ smu->smc_driver_if_version, if_version,
+ smu_program, smu_version, smu_major, smu_minor, smu_debug);
+ dev_warn(adev->dev, "SMU driver if version not matched\n");
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t ppt_offset_bytes;
+ const struct smc_firmware_header_v2_0 *v2;
+
+ v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data;
+
+ ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes);
+ *size = le32_to_cpu(v2->ppt_size_bytes);
+ *table = (uint8_t *)v2 + ppt_offset_bytes;
+
+ return 0;
+}
+
+static int smu_v13_0_set_pptable_v2_1(struct smu_context *smu, void **table,
+ uint32_t *size, uint32_t pptable_id)
+{
+ struct amdgpu_device *adev = smu->adev;
+ const struct smc_firmware_header_v2_1 *v2_1;
+ struct smc_soft_pptable_entry *entries;
+ uint32_t pptable_count = 0;
+ int i = 0;
+
+ v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data;
+ entries = (struct smc_soft_pptable_entry *)
+ ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset));
+ pptable_count = le32_to_cpu(v2_1->pptable_count);
+ for (i = 0; i < pptable_count; i++) {
+ if (le32_to_cpu(entries[i].id) == pptable_id) {
+ *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes));
+ *size = le32_to_cpu(entries[i].ppt_size_bytes);
+ break;
+ }
+ }
+
+ if (i == pptable_count)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint16_t atom_table_size;
+ uint8_t frev, crev;
+ int ret, index;
+
+ dev_info(adev->dev, "use vbios provided pptable\n");
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ powerplayinfo);
+
+ ret = amdgpu_atombios_get_data_table(adev, index, &atom_table_size, &frev, &crev,
+ (uint8_t **)table);
+ if (ret)
+ return ret;
+
+ if (size)
+ *size = atom_table_size;
+
+ return 0;
+}
+
+int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu,
+ void **table,
+ uint32_t *size,
+ uint32_t pptable_id)
+{
+ const struct smc_firmware_header_v1_0 *hdr;
+ struct amdgpu_device *adev = smu->adev;
+ uint16_t version_major, version_minor;
+ int ret;
+
+ hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
+ if (!hdr)
+ return -EINVAL;
+
+ dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id);
+
+ version_major = le16_to_cpu(hdr->header.header_version_major);
+ version_minor = le16_to_cpu(hdr->header.header_version_minor);
+ if (version_major != 2) {
+ dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n",
+ version_major, version_minor);
+ return -EINVAL;
+ }
+
+ switch (version_minor) {
+ case 0:
+ ret = smu_v13_0_set_pptable_v2_0(smu, table, size);
+ break;
+ case 1:
+ ret = smu_v13_0_set_pptable_v2_1(smu, table, size, pptable_id);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_setup_pptable(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t size = 0, pptable_id = 0;
+ void *table;
+ int ret = 0;
+
+ /* override pptable_id from driver parameter */
+ if (amdgpu_smu_pptable_id >= 0) {
+ pptable_id = amdgpu_smu_pptable_id;
+ dev_info(adev->dev, "override pptable id %d\n", pptable_id);
+ } else {
+ pptable_id = smu->smu_table.boot_values.pp_table_id;
+ }
+
+ /* force using vbios pptable in sriov mode */
+ if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1))
+ ret = smu_v13_0_get_pptable_from_vbios(smu, &table, &size);
+ else
+ ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+
+ if (ret)
+ return ret;
+
+ if (!smu->smu_table.power_play_table)
+ smu->smu_table.power_play_table = table;
+ if (!smu->smu_table.power_play_table_size)
+ smu->smu_table.power_play_table_size = size;
+
+ return 0;
+}
+
+int smu_v13_0_init_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+ int ret = 0;
+
+ smu_table->driver_pptable =
+ kzalloc(tables[SMU_TABLE_PPTABLE].size, GFP_KERNEL);
+ if (!smu_table->driver_pptable) {
+ ret = -ENOMEM;
+ goto err0_out;
+ }
+
+ smu_table->max_sustainable_clocks =
+ kzalloc(sizeof(struct smu_13_0_max_sustainable_clocks), GFP_KERNEL);
+ if (!smu_table->max_sustainable_clocks) {
+ ret = -ENOMEM;
+ goto err1_out;
+ }
+
+ /* Aldebaran does not support OVERDRIVE */
+ if (tables[SMU_TABLE_OVERDRIVE].size) {
+ smu_table->overdrive_table =
+ kzalloc(tables[SMU_TABLE_OVERDRIVE].size, GFP_KERNEL);
+ if (!smu_table->overdrive_table) {
+ ret = -ENOMEM;
+ goto err2_out;
+ }
+
+ smu_table->boot_overdrive_table =
+ kzalloc(tables[SMU_TABLE_OVERDRIVE].size, GFP_KERNEL);
+ if (!smu_table->boot_overdrive_table) {
+ ret = -ENOMEM;
+ goto err3_out;
+ }
+ }
+
+ smu_table->combo_pptable =
+ kzalloc(tables[SMU_TABLE_COMBO_PPTABLE].size, GFP_KERNEL);
+ if (!smu_table->combo_pptable) {
+ ret = -ENOMEM;
+ goto err4_out;
+ }
+
+ return 0;
+
+err4_out:
+ kfree(smu_table->boot_overdrive_table);
+err3_out:
+ kfree(smu_table->overdrive_table);
+err2_out:
+ kfree(smu_table->max_sustainable_clocks);
+err1_out:
+ kfree(smu_table->driver_pptable);
+err0_out:
+ return ret;
+}
+
+int smu_v13_0_fini_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ kfree(smu_table->gpu_metrics_table);
+ kfree(smu_table->combo_pptable);
+ kfree(smu_table->boot_overdrive_table);
+ kfree(smu_table->overdrive_table);
+ kfree(smu_table->max_sustainable_clocks);
+ kfree(smu_table->driver_pptable);
+ smu_table->gpu_metrics_table = NULL;
+ smu_table->combo_pptable = NULL;
+ smu_table->boot_overdrive_table = NULL;
+ smu_table->overdrive_table = NULL;
+ smu_table->max_sustainable_clocks = NULL;
+ smu_table->driver_pptable = NULL;
+ kfree(smu_table->hardcode_pptable);
+ smu_table->hardcode_pptable = NULL;
+
+ kfree(smu_table->ecc_table);
+ kfree(smu_table->metrics_table);
+ kfree(smu_table->watermarks_table);
+ smu_table->ecc_table = NULL;
+ smu_table->metrics_table = NULL;
+ smu_table->watermarks_table = NULL;
+ smu_table->metrics_time = 0;
+
+ kfree(smu_dpm->dpm_context);
+ kfree(smu_dpm->golden_dpm_context);
+ kfree(smu_dpm->dpm_current_power_state);
+ kfree(smu_dpm->dpm_request_power_state);
+ smu_dpm->dpm_context = NULL;
+ smu_dpm->golden_dpm_context = NULL;
+ smu_dpm->dpm_context_size = 0;
+ smu_dpm->dpm_current_power_state = NULL;
+ smu_dpm->dpm_request_power_state = NULL;
+
+ return 0;
+}
+
+int smu_v13_0_init_power(struct smu_context *smu)
+{
+ struct smu_power_context *smu_power = &smu->smu_power;
+
+ if (smu_power->power_context || smu_power->power_context_size != 0)
+ return -EINVAL;
+
+ smu_power->power_context = kzalloc(sizeof(struct smu_13_0_dpm_context),
+ GFP_KERNEL);
+ if (!smu_power->power_context)
+ return -ENOMEM;
+ smu_power->power_context_size = sizeof(struct smu_13_0_dpm_context);
+
+ return 0;
+}
+
+int smu_v13_0_fini_power(struct smu_context *smu)
+{
+ struct smu_power_context *smu_power = &smu->smu_power;
+
+ if (!smu_power->power_context || smu_power->power_context_size == 0)
+ return -EINVAL;
+
+ kfree(smu_power->power_context);
+ smu_power->power_context = NULL;
+ smu_power->power_context_size = 0;
+
+ return 0;
+}
+
+int smu_v13_0_get_vbios_bootup_values(struct smu_context *smu)
+{
+ int ret, index;
+ uint16_t size;
+ uint8_t frev, crev;
+ struct atom_common_table_header *header;
+ struct atom_firmware_info_v3_4 *v_3_4;
+ struct atom_firmware_info_v3_3 *v_3_3;
+ struct atom_firmware_info_v3_1 *v_3_1;
+ struct atom_smu_info_v3_6 *smu_info_v3_6;
+ struct atom_smu_info_v4_0 *smu_info_v4_0;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ firmwareinfo);
+
+ ret = amdgpu_atombios_get_data_table(smu->adev, index, &size, &frev, &crev,
+ (uint8_t **)&header);
+ if (ret)
+ return ret;
+
+ if (header->format_revision != 3) {
+ dev_err(smu->adev->dev, "unknown atom_firmware_info version! for smu13\n");
+ return -EINVAL;
+ }
+
+ switch (header->content_revision) {
+ case 0:
+ case 1:
+ case 2:
+ v_3_1 = (struct atom_firmware_info_v3_1 *)header;
+ smu->smu_table.boot_values.revision = v_3_1->firmware_revision;
+ smu->smu_table.boot_values.gfxclk = v_3_1->bootup_sclk_in10khz;
+ smu->smu_table.boot_values.uclk = v_3_1->bootup_mclk_in10khz;
+ smu->smu_table.boot_values.socclk = 0;
+ smu->smu_table.boot_values.dcefclk = 0;
+ smu->smu_table.boot_values.vddc = v_3_1->bootup_vddc_mv;
+ smu->smu_table.boot_values.vddci = v_3_1->bootup_vddci_mv;
+ smu->smu_table.boot_values.mvddc = v_3_1->bootup_mvddc_mv;
+ smu->smu_table.boot_values.vdd_gfx = v_3_1->bootup_vddgfx_mv;
+ smu->smu_table.boot_values.cooling_id = v_3_1->coolingsolution_id;
+ smu->smu_table.boot_values.pp_table_id = 0;
+ break;
+ case 3:
+ v_3_3 = (struct atom_firmware_info_v3_3 *)header;
+ smu->smu_table.boot_values.revision = v_3_3->firmware_revision;
+ smu->smu_table.boot_values.gfxclk = v_3_3->bootup_sclk_in10khz;
+ smu->smu_table.boot_values.uclk = v_3_3->bootup_mclk_in10khz;
+ smu->smu_table.boot_values.socclk = 0;
+ smu->smu_table.boot_values.dcefclk = 0;
+ smu->smu_table.boot_values.vddc = v_3_3->bootup_vddc_mv;
+ smu->smu_table.boot_values.vddci = v_3_3->bootup_vddci_mv;
+ smu->smu_table.boot_values.mvddc = v_3_3->bootup_mvddc_mv;
+ smu->smu_table.boot_values.vdd_gfx = v_3_3->bootup_vddgfx_mv;
+ smu->smu_table.boot_values.cooling_id = v_3_3->coolingsolution_id;
+ smu->smu_table.boot_values.pp_table_id = v_3_3->pplib_pptable_id;
+ break;
+ case 4:
+ default:
+ v_3_4 = (struct atom_firmware_info_v3_4 *)header;
+ smu->smu_table.boot_values.revision = v_3_4->firmware_revision;
+ smu->smu_table.boot_values.gfxclk = v_3_4->bootup_sclk_in10khz;
+ smu->smu_table.boot_values.uclk = v_3_4->bootup_mclk_in10khz;
+ smu->smu_table.boot_values.socclk = 0;
+ smu->smu_table.boot_values.dcefclk = 0;
+ smu->smu_table.boot_values.vddc = v_3_4->bootup_vddc_mv;
+ smu->smu_table.boot_values.vddci = v_3_4->bootup_vddci_mv;
+ smu->smu_table.boot_values.mvddc = v_3_4->bootup_mvddc_mv;
+ smu->smu_table.boot_values.vdd_gfx = v_3_4->bootup_vddgfx_mv;
+ smu->smu_table.boot_values.cooling_id = v_3_4->coolingsolution_id;
+ smu->smu_table.boot_values.pp_table_id = v_3_4->pplib_pptable_id;
+ break;
+ }
+
+ smu->smu_table.boot_values.format_revision = header->format_revision;
+ smu->smu_table.boot_values.content_revision = header->content_revision;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ smu_info);
+ if (!amdgpu_atombios_get_data_table(smu->adev, index, &size, &frev, &crev,
+ (uint8_t **)&header)) {
+
+ if ((frev == 3) && (crev == 6)) {
+ smu_info_v3_6 = (struct atom_smu_info_v3_6 *)header;
+
+ smu->smu_table.boot_values.socclk = smu_info_v3_6->bootup_socclk_10khz;
+ smu->smu_table.boot_values.vclk = smu_info_v3_6->bootup_vclk_10khz;
+ smu->smu_table.boot_values.dclk = smu_info_v3_6->bootup_dclk_10khz;
+ smu->smu_table.boot_values.fclk = smu_info_v3_6->bootup_fclk_10khz;
+ } else if ((frev == 3) && (crev == 1)) {
+ return 0;
+ } else if ((frev == 4) && (crev == 0)) {
+ smu_info_v4_0 = (struct atom_smu_info_v4_0 *)header;
+
+ smu->smu_table.boot_values.socclk = smu_info_v4_0->bootup_socclk_10khz;
+ smu->smu_table.boot_values.dcefclk = smu_info_v4_0->bootup_dcefclk_10khz;
+ smu->smu_table.boot_values.vclk = smu_info_v4_0->bootup_vclk0_10khz;
+ smu->smu_table.boot_values.dclk = smu_info_v4_0->bootup_dclk0_10khz;
+ smu->smu_table.boot_values.fclk = smu_info_v4_0->bootup_fclk_10khz;
+ } else {
+ dev_warn(smu->adev->dev, "Unexpected and unhandled version: %d.%d\n",
+ (uint32_t)frev, (uint32_t)crev);
+ }
+ }
+
+ return 0;
+}
+
+
+int smu_v13_0_notify_memory_pool_location(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *memory_pool = &smu_table->memory_pool;
+ int ret = 0;
+ uint64_t address;
+ uint32_t address_low, address_high;
+
+ if (memory_pool->size == 0 || memory_pool->cpu_addr == NULL)
+ return ret;
+
+ address = memory_pool->mc_address;
+ address_high = (uint32_t)upper_32_bits(address);
+ address_low = (uint32_t)lower_32_bits(address);
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_DramLogSetDramAddrHigh,
+ address_high, NULL);
+ if (ret)
+ return ret;
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_DramLogSetDramAddrLow,
+ address_low, NULL);
+ if (ret)
+ return ret;
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_DramLogSetDramSize,
+ (uint32_t)memory_pool->size, NULL);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+int smu_v13_0_set_min_deep_sleep_dcefclk(struct smu_context *smu, uint32_t clk)
+{
+ int ret;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetMinDeepSleepDcefclk, clk, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "SMU13 attempt to set divider for DCEFCLK Failed!");
+
+ return ret;
+}
+
+int smu_v13_0_set_driver_table_location(struct smu_context *smu)
+{
+ struct smu_table *driver_table = &smu->smu_table.driver_table;
+ int ret = 0;
+
+ if (driver_table->mc_address) {
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetDriverDramAddrHigh,
+ upper_32_bits(driver_table->mc_address),
+ NULL);
+ if (!ret)
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetDriverDramAddrLow,
+ lower_32_bits(driver_table->mc_address),
+ NULL);
+ }
+
+ return ret;
+}
+
+int smu_v13_0_set_tool_table_location(struct smu_context *smu)
+{
+ int ret = 0;
+ struct smu_table *tool_table = &smu->smu_table.tables[SMU_TABLE_PMSTATUSLOG];
+
+ if (tool_table->mc_address) {
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetToolsDramAddrHigh,
+ upper_32_bits(tool_table->mc_address),
+ NULL);
+ if (!ret)
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetToolsDramAddrLow,
+ lower_32_bits(tool_table->mc_address),
+ NULL);
+ }
+
+ return ret;
+}
+
+int smu_v13_0_init_display_count(struct smu_context *smu, uint32_t count)
+{
+ int ret = 0;
+
+ if (!smu->pm_enabled)
+ return ret;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_NumOfDisplays, count, NULL);
+
+ return ret;
+}
+
+int smu_v13_0_set_allowed_mask(struct smu_context *smu)
+{
+ struct smu_feature *feature = &smu->smu_feature;
+ int ret = 0;
+ uint32_t feature_mask[2];
+
+ if (bitmap_empty(feature->allowed, SMU_FEATURE_MAX) ||
+ feature->feature_num < 64)
+ return -EINVAL;
+
+ bitmap_to_arr32(feature_mask, feature->allowed, 64);
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetAllowedFeaturesMaskHigh,
+ feature_mask[1], NULL);
+ if (ret)
+ return ret;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetAllowedFeaturesMaskLow,
+ feature_mask[0],
+ NULL);
+}
+
+int smu_v13_0_gfx_off_control(struct smu_context *smu, bool enable)
+{
+ int ret = 0;
+ struct amdgpu_device *adev = smu->adev;
+
+ switch (adev->ip_versions[MP1_HWIP][0]) {
+ case IP_VERSION(13, 0, 0):
+ case IP_VERSION(13, 0, 1):
+ case IP_VERSION(13, 0, 3):
+ case IP_VERSION(13, 0, 4):
+ case IP_VERSION(13, 0, 5):
+ case IP_VERSION(13, 0, 7):
+ case IP_VERSION(13, 0, 8):
+ case IP_VERSION(13, 0, 10):
+ case IP_VERSION(13, 0, 11):
+ if (!(adev->pm.pp_feature & PP_GFXOFF_MASK))
+ return 0;
+ if (enable)
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_AllowGfxOff, NULL);
+ else
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_DisallowGfxOff, NULL);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_system_features_control(struct smu_context *smu,
+ bool en)
+{
+ return smu_cmn_send_smc_msg(smu, (en ? SMU_MSG_EnableAllSmuFeatures :
+ SMU_MSG_DisableAllSmuFeatures), NULL);
+}
+
+int smu_v13_0_notify_display_change(struct smu_context *smu)
+{
+ int ret = 0;
+
+ if (!smu->pm_enabled)
+ return ret;
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT) &&
+ smu->adev->gmc.vram_type == AMDGPU_VRAM_TYPE_HBM)
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetUclkFastSwitch, 1, NULL);
+
+ return ret;
+}
+
+ static int
+smu_v13_0_get_max_sustainable_clock(struct smu_context *smu, uint32_t *clock,
+ enum smu_clk_type clock_select)
+{
+ int ret = 0;
+ int clk_id;
+
+ if ((smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_MSG, SMU_MSG_GetDcModeMaxDpmFreq) < 0) ||
+ (smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_MSG, SMU_MSG_GetMaxDpmFreq) < 0))
+ return 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clock_select);
+ if (clk_id < 0)
+ return -EINVAL;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetDcModeMaxDpmFreq,
+ clk_id << 16, clock);
+ if (ret) {
+ dev_err(smu->adev->dev, "[GetMaxSustainableClock] Failed to get max DC clock from SMC!");
+ return ret;
+ }
+
+ if (*clock != 0)
+ return 0;
+
+ /* if DC limit is zero, return AC limit */
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetMaxDpmFreq,
+ clk_id << 16, clock);
+ if (ret) {
+ dev_err(smu->adev->dev, "[GetMaxSustainableClock] failed to get max AC clock from SMC!");
+ return ret;
+ }
+
+ return 0;
+}
+
+int smu_v13_0_init_max_sustainable_clocks(struct smu_context *smu)
+{
+ struct smu_13_0_max_sustainable_clocks *max_sustainable_clocks =
+ smu->smu_table.max_sustainable_clocks;
+ int ret = 0;
+
+ max_sustainable_clocks->uclock = smu->smu_table.boot_values.uclk / 100;
+ max_sustainable_clocks->soc_clock = smu->smu_table.boot_values.socclk / 100;
+ max_sustainable_clocks->dcef_clock = smu->smu_table.boot_values.dcefclk / 100;
+ max_sustainable_clocks->display_clock = 0xFFFFFFFF;
+ max_sustainable_clocks->phy_clock = 0xFFFFFFFF;
+ max_sustainable_clocks->pixel_clock = 0xFFFFFFFF;
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
+ ret = smu_v13_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->uclock),
+ SMU_UCLK);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get max UCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
+ ret = smu_v13_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->soc_clock),
+ SMU_SOCCLK);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get max SOCCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT)) {
+ ret = smu_v13_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->dcef_clock),
+ SMU_DCEFCLK);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get max DCEFCLK from SMC!",
+ __func__);
+ return ret;
+ }
+
+ ret = smu_v13_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->display_clock),
+ SMU_DISPCLK);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get max DISPCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ ret = smu_v13_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->phy_clock),
+ SMU_PHYCLK);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get max PHYCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ ret = smu_v13_0_get_max_sustainable_clock(smu,
+ &(max_sustainable_clocks->pixel_clock),
+ SMU_PIXCLK);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get max PIXCLK from SMC!",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (max_sustainable_clocks->soc_clock < max_sustainable_clocks->uclock)
+ max_sustainable_clocks->uclock = max_sustainable_clocks->soc_clock;
+
+ return 0;
+}
+
+int smu_v13_0_get_current_power_limit(struct smu_context *smu,
+ uint32_t *power_limit)
+{
+ int power_src;
+ int ret = 0;
+
+ if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT))
+ return -EINVAL;
+
+ power_src = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_PWR,
+ smu->adev->pm.ac_power ?
+ SMU_POWER_SOURCE_AC :
+ SMU_POWER_SOURCE_DC);
+ if (power_src < 0)
+ return -EINVAL;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetPptLimit,
+ power_src << 16,
+ power_limit);
+ if (ret)
+ dev_err(smu->adev->dev, "[%s] get PPT limit failed!", __func__);
+
+ return ret;
+}
+
+int smu_v13_0_set_power_limit(struct smu_context *smu,
+ enum smu_ppt_limit_type limit_type,
+ uint32_t limit)
+{
+ int ret = 0;
+
+ if (limit_type != SMU_DEFAULT_PPT_LIMIT)
+ return -EINVAL;
+
+ if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) {
+ dev_err(smu->adev->dev, "Setting new power limit is not supported!\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetPptLimit, limit, NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] Set power limit Failed!\n", __func__);
+ return ret;
+ }
+
+ smu->current_power_limit = limit;
+
+ return 0;
+}
+
+static int smu_v13_0_allow_ih_interrupt(struct smu_context *smu)
+{
+ return smu_cmn_send_smc_msg(smu,
+ SMU_MSG_AllowIHHostInterrupt,
+ NULL);
+}
+
+static int smu_v13_0_process_pending_interrupt(struct smu_context *smu)
+{
+ int ret = 0;
+
+ if (smu->dc_controlled_by_gpio &&
+ smu_cmn_feature_is_enabled(smu, SMU_FEATURE_ACDC_BIT))
+ ret = smu_v13_0_allow_ih_interrupt(smu);
+
+ return ret;
+}
+
+int smu_v13_0_enable_thermal_alert(struct smu_context *smu)
+{
+ int ret = 0;
+
+ if (!smu->irq_source.num_types)
+ return 0;
+
+ ret = amdgpu_irq_get(smu->adev, &smu->irq_source, 0);
+ if (ret)
+ return ret;
+
+ return smu_v13_0_process_pending_interrupt(smu);
+}
+
+int smu_v13_0_disable_thermal_alert(struct smu_context *smu)
+{
+ if (!smu->irq_source.num_types)
+ return 0;
+
+ return amdgpu_irq_put(smu->adev, &smu->irq_source, 0);
+}
+
+static uint16_t convert_to_vddc(uint8_t vid)
+{
+ return (uint16_t) ((6200 - (vid * 25)) / SMU13_VOLTAGE_SCALE);
+}
+
+int smu_v13_0_get_gfx_vdd(struct smu_context *smu, uint32_t *value)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t vdd = 0, val_vid = 0;
+
+ if (!value)
+ return -EINVAL;
+ val_vid = (RREG32_SOC15(SMUIO, 0, regSMUSVI0_TEL_PLANE0) &
+ SMUSVI0_TEL_PLANE0__SVI0_PLANE0_VDDCOR_MASK) >>
+ SMUSVI0_TEL_PLANE0__SVI0_PLANE0_VDDCOR__SHIFT;
+
+ vdd = (uint32_t)convert_to_vddc((uint8_t)val_vid);
+
+ *value = vdd;
+
+ return 0;
+
+}
+
+int
+smu_v13_0_display_clock_voltage_request(struct smu_context *smu,
+ struct pp_display_clock_request
+ *clock_req)
+{
+ enum amd_pp_clock_type clk_type = clock_req->clock_type;
+ int ret = 0;
+ enum smu_clk_type clk_select = 0;
+ uint32_t clk_freq = clock_req->clock_freq_in_khz / 1000;
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCEFCLK_BIT) ||
+ smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
+ switch (clk_type) {
+ case amd_pp_dcef_clock:
+ clk_select = SMU_DCEFCLK;
+ break;
+ case amd_pp_disp_clock:
+ clk_select = SMU_DISPCLK;
+ break;
+ case amd_pp_pixel_clock:
+ clk_select = SMU_PIXCLK;
+ break;
+ case amd_pp_phy_clock:
+ clk_select = SMU_PHYCLK;
+ break;
+ case amd_pp_mem_clock:
+ clk_select = SMU_UCLK;
+ break;
+ default:
+ dev_info(smu->adev->dev, "[%s] Invalid Clock Type!", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ goto failed;
+
+ if (clk_select == SMU_UCLK && smu->disable_uclk_switch)
+ return 0;
+
+ ret = smu_v13_0_set_hard_freq_limited_range(smu, clk_select, clk_freq, 0);
+
+ if(clk_select == SMU_UCLK)
+ smu->hard_min_uclk_req_from_dal = clk_freq;
+ }
+
+failed:
+ return ret;
+}
+
+uint32_t smu_v13_0_get_fan_control_mode(struct smu_context *smu)
+{
+ if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_FAN_CONTROL_BIT))
+ return AMD_FAN_CTRL_MANUAL;
+ else
+ return AMD_FAN_CTRL_AUTO;
+}
+
+ static int
+smu_v13_0_auto_fan_control(struct smu_context *smu, bool auto_fan_control)
+{
+ int ret = 0;
+
+ if (!smu_cmn_feature_is_supported(smu, SMU_FEATURE_FAN_CONTROL_BIT))
+ return 0;
+
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_FAN_CONTROL_BIT, auto_fan_control);
+ if (ret)
+ dev_err(smu->adev->dev, "[%s]%s smc FAN CONTROL feature failed!",
+ __func__, (auto_fan_control ? "Start" : "Stop"));
+
+ return ret;
+}
+
+ static int
+smu_v13_0_set_fan_static_mode(struct smu_context *smu, uint32_t mode)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ WREG32_SOC15(THM, 0, regCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, regCG_FDO_CTRL2),
+ CG_FDO_CTRL2, TMIN, 0));
+ WREG32_SOC15(THM, 0, regCG_FDO_CTRL2,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, regCG_FDO_CTRL2),
+ CG_FDO_CTRL2, FDO_PWM_MODE, mode));
+
+ return 0;
+}
+
+int smu_v13_0_set_fan_speed_pwm(struct smu_context *smu,
+ uint32_t speed)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t duty100, duty;
+ uint64_t tmp64;
+
+ speed = MIN(speed, 255);
+
+ if (smu_v13_0_auto_fan_control(smu, 0))
+ return -EINVAL;
+
+ duty100 = REG_GET_FIELD(RREG32_SOC15(THM, 0, regCG_FDO_CTRL1),
+ CG_FDO_CTRL1, FMAX_DUTY100);
+ if (!duty100)
+ return -EINVAL;
+
+ tmp64 = (uint64_t)speed * duty100;
+ do_div(tmp64, 255);
+ duty = (uint32_t)tmp64;
+
+ WREG32_SOC15(THM, 0, regCG_FDO_CTRL0,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, regCG_FDO_CTRL0),
+ CG_FDO_CTRL0, FDO_STATIC_DUTY, duty));
+
+ return smu_v13_0_set_fan_static_mode(smu, FDO_PWM_MODE_STATIC);
+}
+
+ int
+smu_v13_0_set_fan_control_mode(struct smu_context *smu,
+ uint32_t mode)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case AMD_FAN_CTRL_NONE:
+ ret = smu_v13_0_set_fan_speed_pwm(smu, 255);
+ break;
+ case AMD_FAN_CTRL_MANUAL:
+ ret = smu_v13_0_auto_fan_control(smu, 0);
+ break;
+ case AMD_FAN_CTRL_AUTO:
+ ret = smu_v13_0_auto_fan_control(smu, 1);
+ break;
+ default:
+ break;
+ }
+
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s]Set fan control mode failed!", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_set_fan_speed_rpm(struct smu_context *smu,
+ uint32_t speed)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t crystal_clock_freq = 2500;
+ uint32_t tach_period;
+ int ret;
+
+ if (!speed)
+ return -EINVAL;
+
+ ret = smu_v13_0_auto_fan_control(smu, 0);
+ if (ret)
+ return ret;
+
+ tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
+ WREG32_SOC15(THM, 0, regCG_TACH_CTRL,
+ REG_SET_FIELD(RREG32_SOC15(THM, 0, regCG_TACH_CTRL),
+ CG_TACH_CTRL, TARGET_PERIOD,
+ tach_period));
+
+ return smu_v13_0_set_fan_static_mode(smu, FDO_PWM_MODE_STATIC_RPM);
+}
+
+int smu_v13_0_set_xgmi_pstate(struct smu_context *smu,
+ uint32_t pstate)
+{
+ int ret = 0;
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetXgmiMode,
+ pstate ? XGMI_MODE_PSTATE_D0 : XGMI_MODE_PSTATE_D3,
+ NULL);
+ return ret;
+}
+
+static int smu_v13_0_set_irq_state(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ unsigned tyep,
+ enum amdgpu_interrupt_state state)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ uint32_t low, high;
+ uint32_t val = 0;
+
+ switch (state) {
+ case AMDGPU_IRQ_STATE_DISABLE:
+ /* For THM irqs */
+ val = RREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_INTH_MASK, 1);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_INTL_MASK, 1);
+ WREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL, val);
+
+ WREG32_SOC15(THM, 0, regTHM_THERMAL_INT_ENA, 0);
+
+ /* For MP1 SW irqs */
+ val = RREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT_CTRL);
+ val = REG_SET_FIELD(val, MP1_SMN_IH_SW_INT_CTRL, INT_MASK, 1);
+ WREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT_CTRL, val);
+
+ break;
+ case AMDGPU_IRQ_STATE_ENABLE:
+ /* For THM irqs */
+ low = max(SMU_THERMAL_MINIMUM_ALERT_TEMP,
+ smu->thermal_range.min / SMU_TEMPERATURE_UNITS_PER_CENTIGRADES);
+ high = min(SMU_THERMAL_MAXIMUM_ALERT_TEMP,
+ smu->thermal_range.software_shutdown_temp);
+
+ val = RREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, MAX_IH_CREDIT, 5);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_INTH_MASK, 0);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_INTL_MASK, 0);
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, (high & 0xff));
+ val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, (low & 0xff));
+ val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK);
+ WREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL, val);
+
+ val = (1 << THM_THERMAL_INT_ENA__THERM_INTH_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_INTL_CLR__SHIFT);
+ val |= (1 << THM_THERMAL_INT_ENA__THERM_TRIGGER_CLR__SHIFT);
+ WREG32_SOC15(THM, 0, regTHM_THERMAL_INT_ENA, val);
+
+ /* For MP1 SW irqs */
+ val = RREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT);
+ val = REG_SET_FIELD(val, MP1_SMN_IH_SW_INT, ID, 0xFE);
+ val = REG_SET_FIELD(val, MP1_SMN_IH_SW_INT, VALID, 0);
+ WREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT, val);
+
+ val = RREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT_CTRL);
+ val = REG_SET_FIELD(val, MP1_SMN_IH_SW_INT_CTRL, INT_MASK, 0);
+ WREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT_CTRL, val);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int smu_v13_0_ack_ac_dc_interrupt(struct smu_context *smu)
+{
+ return smu_cmn_send_smc_msg(smu,
+ SMU_MSG_ReenableAcDcInterrupt,
+ NULL);
+}
+
+#define THM_11_0__SRCID__THM_DIG_THERM_L2H 0 /* ASIC_TEMP > CG_THERMAL_INT.DIG_THERM_INTH */
+#define THM_11_0__SRCID__THM_DIG_THERM_H2L 1 /* ASIC_TEMP < CG_THERMAL_INT.DIG_THERM_INTL */
+#define SMUIO_11_0__SRCID__SMUIO_GPIO19 83
+
+static int smu_v13_0_irq_process(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ uint32_t client_id = entry->client_id;
+ uint32_t src_id = entry->src_id;
+ /*
+ * ctxid is used to distinguish different
+ * events for SMCToHost interrupt.
+ */
+ uint32_t ctxid = entry->src_data[0];
+ uint32_t data;
+ uint32_t high;
+
+ if (client_id == SOC15_IH_CLIENTID_THM) {
+ switch (src_id) {
+ case THM_11_0__SRCID__THM_DIG_THERM_L2H:
+ dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n");
+ /*
+ * SW CTF just occurred.
+ * Try to do a graceful shutdown to prevent further damage.
+ */
+ dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n");
+ orderly_poweroff(true);
+ break;
+ case THM_11_0__SRCID__THM_DIG_THERM_H2L:
+ dev_emerg(adev->dev, "ERROR: GPU under temperature range detected\n");
+ break;
+ default:
+ dev_emerg(adev->dev, "ERROR: GPU under temperature range unknown src id (%d)\n",
+ src_id);
+ break;
+ }
+ } else if (client_id == SOC15_IH_CLIENTID_ROM_SMUIO) {
+ dev_emerg(adev->dev, "ERROR: GPU HW Critical Temperature Fault(aka CTF) detected!\n");
+ /*
+ * HW CTF just occurred. Shutdown to prevent further damage.
+ */
+ dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU HW CTF!\n");
+ orderly_poweroff(true);
+ } else if (client_id == SOC15_IH_CLIENTID_MP1) {
+ if (src_id == 0xfe) {
+ /* ACK SMUToHost interrupt */
+ data = RREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT_CTRL);
+ data = REG_SET_FIELD(data, MP1_SMN_IH_SW_INT_CTRL, INT_ACK, 1);
+ WREG32_SOC15(MP1, 0, regMP1_SMN_IH_SW_INT_CTRL, data);
+
+ switch (ctxid) {
+ case 0x3:
+ dev_dbg(adev->dev, "Switched to AC mode!\n");
+ smu_v13_0_ack_ac_dc_interrupt(smu);
+ break;
+ case 0x4:
+ dev_dbg(adev->dev, "Switched to DC mode!\n");
+ smu_v13_0_ack_ac_dc_interrupt(smu);
+ break;
+ case 0x7:
+ /*
+ * Increment the throttle interrupt counter
+ */
+ atomic64_inc(&smu->throttle_int_counter);
+
+ if (!atomic_read(&adev->throttling_logging_enabled))
+ return 0;
+
+ if (__ratelimit(&adev->throttling_logging_rs))
+ schedule_work(&smu->throttling_logging_work);
+
+ break;
+ case 0x8:
+ high = smu->thermal_range.software_shutdown_temp +
+ smu->thermal_range.software_shutdown_temp_offset;
+ high = min_t(typeof(high),
+ SMU_THERMAL_MAXIMUM_ALERT_TEMP,
+ high);
+ dev_emerg(adev->dev, "Reduce soft CTF limit to %d (by an offset %d)\n",
+ high,
+ smu->thermal_range.software_shutdown_temp_offset);
+
+ data = RREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL);
+ data = REG_SET_FIELD(data, THM_THERMAL_INT_CTRL,
+ DIG_THERM_INTH,
+ (high & 0xff));
+ data = data & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK);
+ WREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL, data);
+ break;
+ case 0x9:
+ high = min_t(typeof(high),
+ SMU_THERMAL_MAXIMUM_ALERT_TEMP,
+ smu->thermal_range.software_shutdown_temp);
+ dev_emerg(adev->dev, "Recover soft CTF limit to %d\n", high);
+
+ data = RREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL);
+ data = REG_SET_FIELD(data, THM_THERMAL_INT_CTRL,
+ DIG_THERM_INTH,
+ (high & 0xff));
+ data = data & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK);
+ WREG32_SOC15(THM, 0, regTHM_THERMAL_INT_CTRL, data);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct amdgpu_irq_src_funcs smu_v13_0_irq_funcs =
+{
+ .set = smu_v13_0_set_irq_state,
+ .process = smu_v13_0_irq_process,
+};
+
+int smu_v13_0_register_irq_handler(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct amdgpu_irq_src *irq_src = &smu->irq_source;
+ int ret = 0;
+
+ if (amdgpu_sriov_vf(adev))
+ return 0;
+
+ irq_src->num_types = 1;
+ irq_src->funcs = &smu_v13_0_irq_funcs;
+
+ ret = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_THM,
+ THM_11_0__SRCID__THM_DIG_THERM_L2H,
+ irq_src);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_THM,
+ THM_11_0__SRCID__THM_DIG_THERM_H2L,
+ irq_src);
+ if (ret)
+ return ret;
+
+ /* Register CTF(GPIO_19) interrupt */
+ ret = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_ROM_SMUIO,
+ SMUIO_11_0__SRCID__SMUIO_GPIO19,
+ irq_src);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_MP1,
+ 0xfe,
+ irq_src);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu,
+ struct pp_smu_nv_clock_table *max_clocks)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_max_sustainable_clocks *sustainable_clocks = NULL;
+
+ if (!max_clocks || !table_context->max_sustainable_clocks)
+ return -EINVAL;
+
+ sustainable_clocks = table_context->max_sustainable_clocks;
+
+ max_clocks->dcfClockInKhz =
+ (unsigned int) sustainable_clocks->dcef_clock * 1000;
+ max_clocks->displayClockInKhz =
+ (unsigned int) sustainable_clocks->display_clock * 1000;
+ max_clocks->phyClockInKhz =
+ (unsigned int) sustainable_clocks->phy_clock * 1000;
+ max_clocks->pixelClockInKhz =
+ (unsigned int) sustainable_clocks->pixel_clock * 1000;
+ max_clocks->uClockInKhz =
+ (unsigned int) sustainable_clocks->uclock * 1000;
+ max_clocks->socClockInKhz =
+ (unsigned int) sustainable_clocks->soc_clock * 1000;
+ max_clocks->dscClockInKhz = 0;
+ max_clocks->dppClockInKhz = 0;
+ max_clocks->fabricClockInKhz = 0;
+
+ return 0;
+}
+
+int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu)
+{
+ int ret = 0;
+
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL);
+
+ return ret;
+}
+
+static int smu_v13_0_wait_for_reset_complete(struct smu_context *smu,
+ uint64_t event_arg)
+{
+ int ret = 0;
+
+ dev_dbg(smu->adev->dev, "waiting for smu reset complete\n");
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GfxDriverResetRecovery, NULL);
+
+ return ret;
+}
+
+int smu_v13_0_wait_for_event(struct smu_context *smu, enum smu_event_type event,
+ uint64_t event_arg)
+{
+ int ret = -EINVAL;
+
+ switch (event) {
+ case SMU_EVENT_RESET_COMPLETE:
+ ret = smu_v13_0_wait_for_reset_complete(smu, event_arg);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type,
+ uint32_t *min, uint32_t *max)
+{
+ int ret = 0, clk_id = 0;
+ uint32_t param = 0;
+ uint32_t clock_limit;
+
+ if (!smu_cmn_clk_dpm_is_enabled(smu, clk_type)) {
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ clock_limit = smu->smu_table.boot_values.uclk;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ clock_limit = smu->smu_table.boot_values.gfxclk;
+ break;
+ case SMU_SOCCLK:
+ clock_limit = smu->smu_table.boot_values.socclk;
+ break;
+ default:
+ clock_limit = 0;
+ break;
+ }
+
+ /* clock in Mhz unit */
+ if (min)
+ *min = clock_limit / 100;
+ if (max)
+ *max = clock_limit / 100;
+
+ return 0;
+ }
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0) {
+ ret = -EINVAL;
+ goto failed;
+ }
+ param = (clk_id & 0xffff) << 16;
+
+ if (max) {
+ if (smu->adev->pm.ac_power)
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetMaxDpmFreq,
+ param,
+ max);
+ else
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetDcModeMaxDpmFreq,
+ param,
+ max);
+ if (ret)
+ goto failed;
+ }
+
+ if (min) {
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetMinDpmFreq, param, min);
+ if (ret)
+ goto failed;
+ }
+
+failed:
+ return ret;
+}
+
+int smu_v13_0_set_soft_freq_limited_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t min,
+ uint32_t max)
+{
+ int ret = 0, clk_id = 0;
+ uint32_t param;
+
+ if (!smu_cmn_clk_dpm_is_enabled(smu, clk_type))
+ return 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return clk_id;
+
+ if (max > 0) {
+ param = (uint32_t)((clk_id << 16) | (max & 0xffff));
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxByFreq,
+ param, NULL);
+ if (ret)
+ goto out;
+ }
+
+ if (min > 0) {
+ param = (uint32_t)((clk_id << 16) | (min & 0xffff));
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMinByFreq,
+ param, NULL);
+ if (ret)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int smu_v13_0_set_hard_freq_limited_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t min,
+ uint32_t max)
+{
+ int ret = 0, clk_id = 0;
+ uint32_t param;
+
+ if (min <= 0 && max <= 0)
+ return -EINVAL;
+
+ if (!smu_cmn_clk_dpm_is_enabled(smu, clk_type))
+ return 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return clk_id;
+
+ if (max > 0) {
+ param = (uint32_t)((clk_id << 16) | (max & 0xffff));
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetHardMaxByFreq,
+ param, NULL);
+ if (ret)
+ return ret;
+ }
+
+ if (min > 0) {
+ param = (uint32_t)((clk_id << 16) | (min & 0xffff));
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinByFreq,
+ param, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_set_performance_level(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct smu_13_0_dpm_context *dpm_context =
+ smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *gfx_table =
+ &dpm_context->dpm_tables.gfx_table;
+ struct smu_13_0_dpm_table *mem_table =
+ &dpm_context->dpm_tables.uclk_table;
+ struct smu_13_0_dpm_table *soc_table =
+ &dpm_context->dpm_tables.soc_table;
+ struct smu_13_0_dpm_table *vclk_table =
+ &dpm_context->dpm_tables.vclk_table;
+ struct smu_13_0_dpm_table *dclk_table =
+ &dpm_context->dpm_tables.dclk_table;
+ struct smu_13_0_dpm_table *fclk_table =
+ &dpm_context->dpm_tables.fclk_table;
+ struct smu_umd_pstate_table *pstate_table =
+ &smu->pstate_table;
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t sclk_min = 0, sclk_max = 0;
+ uint32_t mclk_min = 0, mclk_max = 0;
+ uint32_t socclk_min = 0, socclk_max = 0;
+ uint32_t vclk_min = 0, vclk_max = 0;
+ uint32_t dclk_min = 0, dclk_max = 0;
+ uint32_t fclk_min = 0, fclk_max = 0;
+ int ret = 0, i;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ sclk_min = sclk_max = gfx_table->max;
+ mclk_min = mclk_max = mem_table->max;
+ socclk_min = socclk_max = soc_table->max;
+ vclk_min = vclk_max = vclk_table->max;
+ dclk_min = dclk_max = dclk_table->max;
+ fclk_min = fclk_max = fclk_table->max;
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ sclk_min = sclk_max = gfx_table->min;
+ mclk_min = mclk_max = mem_table->min;
+ socclk_min = socclk_max = soc_table->min;
+ vclk_min = vclk_max = vclk_table->min;
+ dclk_min = dclk_max = dclk_table->min;
+ fclk_min = fclk_max = fclk_table->min;
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ sclk_min = gfx_table->min;
+ sclk_max = gfx_table->max;
+ mclk_min = mem_table->min;
+ mclk_max = mem_table->max;
+ socclk_min = soc_table->min;
+ socclk_max = soc_table->max;
+ vclk_min = vclk_table->min;
+ vclk_max = vclk_table->max;
+ dclk_min = dclk_table->min;
+ dclk_max = dclk_table->max;
+ fclk_min = fclk_table->min;
+ fclk_max = fclk_table->max;
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ sclk_min = sclk_max = pstate_table->gfxclk_pstate.standard;
+ mclk_min = mclk_max = pstate_table->uclk_pstate.standard;
+ socclk_min = socclk_max = pstate_table->socclk_pstate.standard;
+ vclk_min = vclk_max = pstate_table->vclk_pstate.standard;
+ dclk_min = dclk_max = pstate_table->dclk_pstate.standard;
+ fclk_min = fclk_max = pstate_table->fclk_pstate.standard;
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ sclk_min = sclk_max = pstate_table->gfxclk_pstate.min;
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ mclk_min = mclk_max = pstate_table->uclk_pstate.min;
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ sclk_min = sclk_max = pstate_table->gfxclk_pstate.peak;
+ mclk_min = mclk_max = pstate_table->uclk_pstate.peak;
+ socclk_min = socclk_max = pstate_table->socclk_pstate.peak;
+ vclk_min = vclk_max = pstate_table->vclk_pstate.peak;
+ dclk_min = dclk_max = pstate_table->dclk_pstate.peak;
+ fclk_min = fclk_max = pstate_table->fclk_pstate.peak;
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ return 0;
+ default:
+ dev_err(adev->dev, "Invalid performance level %d\n", level);
+ return -EINVAL;
+ }
+
+ /*
+ * Unset those settings for SMU 13.0.2. As soft limits settings
+ * for those clock domains are not supported.
+ */
+ if (smu->adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 2)) {
+ mclk_min = mclk_max = 0;
+ socclk_min = socclk_max = 0;
+ vclk_min = vclk_max = 0;
+ dclk_min = dclk_max = 0;
+ fclk_min = fclk_max = 0;
+ }
+
+ if (sclk_min && sclk_max) {
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ SMU_GFXCLK,
+ sclk_min,
+ sclk_max);
+ if (ret)
+ return ret;
+
+ pstate_table->gfxclk_pstate.curr.min = sclk_min;
+ pstate_table->gfxclk_pstate.curr.max = sclk_max;
+ }
+
+ if (mclk_min && mclk_max) {
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ SMU_MCLK,
+ mclk_min,
+ mclk_max);
+ if (ret)
+ return ret;
+
+ pstate_table->uclk_pstate.curr.min = mclk_min;
+ pstate_table->uclk_pstate.curr.max = mclk_max;
+ }
+
+ if (socclk_min && socclk_max) {
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ SMU_SOCCLK,
+ socclk_min,
+ socclk_max);
+ if (ret)
+ return ret;
+
+ pstate_table->socclk_pstate.curr.min = socclk_min;
+ pstate_table->socclk_pstate.curr.max = socclk_max;
+ }
+
+ if (vclk_min && vclk_max) {
+ for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
+ if (adev->vcn.harvest_config & (1 << i))
+ continue;
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ i ? SMU_VCLK1 : SMU_VCLK,
+ vclk_min,
+ vclk_max);
+ if (ret)
+ return ret;
+ }
+ pstate_table->vclk_pstate.curr.min = vclk_min;
+ pstate_table->vclk_pstate.curr.max = vclk_max;
+ }
+
+ if (dclk_min && dclk_max) {
+ for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
+ if (adev->vcn.harvest_config & (1 << i))
+ continue;
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ i ? SMU_DCLK1 : SMU_DCLK,
+ dclk_min,
+ dclk_max);
+ if (ret)
+ return ret;
+ }
+ pstate_table->dclk_pstate.curr.min = dclk_min;
+ pstate_table->dclk_pstate.curr.max = dclk_max;
+ }
+
+ if (fclk_min && fclk_max) {
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ SMU_FCLK,
+ fclk_min,
+ fclk_max);
+ if (ret)
+ return ret;
+
+ pstate_table->fclk_pstate.curr.min = fclk_min;
+ pstate_table->fclk_pstate.curr.max = fclk_max;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_set_power_source(struct smu_context *smu,
+ enum smu_power_src_type power_src)
+{
+ int pwr_source;
+
+ pwr_source = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_PWR,
+ (uint32_t)power_src);
+ if (pwr_source < 0)
+ return -EINVAL;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_NotifyPowerSource,
+ pwr_source,
+ NULL);
+}
+
+static int smu_v13_0_get_dpm_freq_by_index(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint16_t level,
+ uint32_t *value)
+{
+ int ret = 0, clk_id = 0;
+ uint32_t param;
+
+ if (!value)
+ return -EINVAL;
+
+ if (!smu_cmn_clk_dpm_is_enabled(smu, clk_type))
+ return 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return clk_id;
+
+ param = (uint32_t)(((clk_id & 0xffff) << 16) | (level & 0xffff));
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetDpmFreqByIndex,
+ param,
+ value);
+ if (ret)
+ return ret;
+
+ *value = *value & 0x7fffffff;
+
+ return ret;
+}
+
+static int smu_v13_0_get_dpm_level_count(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ int ret;
+
+ ret = smu_v13_0_get_dpm_freq_by_index(smu, clk_type, 0xff, value);
+ /* SMU v13.0.2 FW returns 0 based max level, increment by one for it */
+ if((smu->adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 2)) && (!ret && value))
+ ++(*value);
+
+ return ret;
+}
+
+static int smu_v13_0_get_fine_grained_status(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ bool *is_fine_grained_dpm)
+{
+ int ret = 0, clk_id = 0;
+ uint32_t param;
+ uint32_t value;
+
+ if (!is_fine_grained_dpm)
+ return -EINVAL;
+
+ if (!smu_cmn_clk_dpm_is_enabled(smu, clk_type))
+ return 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return clk_id;
+
+ param = (uint32_t)(((clk_id & 0xffff) << 16) | 0xff);
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetDpmFreqByIndex,
+ param,
+ &value);
+ if (ret)
+ return ret;
+
+ /*
+ * BIT31: 1 - Fine grained DPM, 0 - Dicrete DPM
+ * now, we un-support it
+ */
+ *is_fine_grained_dpm = value & 0x80000000;
+
+ return 0;
+}
+
+int smu_v13_0_set_single_dpm_table(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ struct smu_13_0_dpm_table *single_dpm_table)
+{
+ int ret = 0;
+ uint32_t clk;
+ int i;
+
+ ret = smu_v13_0_get_dpm_level_count(smu,
+ clk_type,
+ &single_dpm_table->count);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get dpm levels!\n", __func__);
+ return ret;
+ }
+
+ if (smu->adev->ip_versions[MP1_HWIP][0] != IP_VERSION(13, 0, 2)) {
+ ret = smu_v13_0_get_fine_grained_status(smu,
+ clk_type,
+ &single_dpm_table->is_fine_grained);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get fine grained status!\n", __func__);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < single_dpm_table->count; i++) {
+ ret = smu_v13_0_get_dpm_freq_by_index(smu,
+ clk_type,
+ i,
+ &clk);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] failed to get dpm freq by index!\n", __func__);
+ return ret;
+ }
+
+ single_dpm_table->dpm_levels[i].value = clk;
+ single_dpm_table->dpm_levels[i].enabled = true;
+
+ if (i == 0)
+ single_dpm_table->min = clk;
+ else if (i == single_dpm_table->count - 1)
+ single_dpm_table->max = clk;
+ }
+
+ return 0;
+}
+
+int smu_v13_0_get_dpm_level_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *min_value,
+ uint32_t *max_value)
+{
+ uint32_t level_count = 0;
+ int ret = 0;
+
+ if (!min_value && !max_value)
+ return -EINVAL;
+
+ if (min_value) {
+ /* by default, level 0 clock value as min value */
+ ret = smu_v13_0_get_dpm_freq_by_index(smu,
+ clk_type,
+ 0,
+ min_value);
+ if (ret)
+ return ret;
+ }
+
+ if (max_value) {
+ ret = smu_v13_0_get_dpm_level_count(smu,
+ clk_type,
+ &level_count);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_get_dpm_freq_by_index(smu,
+ clk_type,
+ level_count - 1,
+ max_value);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_get_current_pcie_link_width_level(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ return (RREG32_PCIE(smnPCIE_LC_LINK_WIDTH_CNTL) &
+ PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD_MASK)
+ >> PCIE_LC_LINK_WIDTH_CNTL__LC_LINK_WIDTH_RD__SHIFT;
+}
+
+int smu_v13_0_get_current_pcie_link_width(struct smu_context *smu)
+{
+ uint32_t width_level;
+
+ width_level = smu_v13_0_get_current_pcie_link_width_level(smu);
+ if (width_level > LINK_WIDTH_MAX)
+ width_level = 0;
+
+ return link_width[width_level];
+}
+
+int smu_v13_0_get_current_pcie_link_speed_level(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ return (RREG32_PCIE(smnPCIE_LC_SPEED_CNTL) &
+ PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK)
+ >> PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT;
+}
+
+int smu_v13_0_get_current_pcie_link_speed(struct smu_context *smu)
+{
+ uint32_t speed_level;
+
+ speed_level = smu_v13_0_get_current_pcie_link_speed_level(smu);
+ if (speed_level > LINK_SPEED_MAX)
+ speed_level = 0;
+
+ return link_speed[speed_level];
+}
+
+int smu_v13_0_set_vcn_enable(struct smu_context *smu,
+ bool enable)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int i, ret = 0;
+
+ for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
+ if (adev->vcn.harvest_config & (1 << i))
+ continue;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, enable ?
+ SMU_MSG_PowerUpVcn : SMU_MSG_PowerDownVcn,
+ i << 16U, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_set_jpeg_enable(struct smu_context *smu,
+ bool enable)
+{
+ return smu_cmn_send_smc_msg_with_param(smu, enable ?
+ SMU_MSG_PowerUpJpeg : SMU_MSG_PowerDownJpeg,
+ 0, NULL);
+}
+
+int smu_v13_0_run_btc(struct smu_context *smu)
+{
+ int res;
+
+ res = smu_cmn_send_smc_msg(smu, SMU_MSG_RunDcBtc, NULL);
+ if (res)
+ dev_err(smu->adev->dev, "RunDcBtc failed!\n");
+
+ return res;
+}
+
+int smu_v13_0_gpo_control(struct smu_context *smu,
+ bool enablement)
+{
+ int res;
+
+ res = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_AllowGpo,
+ enablement ? 1 : 0,
+ NULL);
+ if (res)
+ dev_err(smu->adev->dev, "SetGpoAllow %d failed!\n", enablement);
+
+ return res;
+}
+
+int smu_v13_0_deep_sleep_control(struct smu_context *smu,
+ bool enablement)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_GFXCLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_GFXCLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s GFXCLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_UCLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_UCLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s UCLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_FCLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_FCLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s FCLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_SOCCLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_SOCCLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s SOCCLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_LCLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_LCLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s LCLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_VCN_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_VCN_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s VCN DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_MP0CLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_MP0CLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s MP0/MPIOCLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_DS_MP1CLK_BIT)) {
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_DS_MP1CLK_BIT, enablement);
+ if (ret) {
+ dev_err(adev->dev, "Failed to %s MP1CLK DS!\n", enablement ? "enable" : "disable");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+int smu_v13_0_gfx_ulv_control(struct smu_context *smu,
+ bool enablement)
+{
+ int ret = 0;
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_GFX_ULV_BIT))
+ ret = smu_cmn_feature_set_enabled(smu, SMU_FEATURE_GFX_ULV_BIT, enablement);
+
+ return ret;
+}
+
+int smu_v13_0_baco_set_armd3_sequence(struct smu_context *smu,
+ enum smu_baco_seq baco_seq)
+{
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_ArmD3,
+ baco_seq,
+ NULL);
+}
+
+bool smu_v13_0_baco_is_support(struct smu_context *smu)
+{
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+
+ if (amdgpu_sriov_vf(smu->adev) ||
+ !smu_baco->platform_support)
+ return false;
+
+ /* return true if ASIC is in BACO state already */
+ if (smu_v13_0_baco_get_state(smu) == SMU_BACO_STATE_ENTER)
+ return true;
+
+ if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_BACO_BIT) &&
+ !smu_cmn_feature_is_enabled(smu, SMU_FEATURE_BACO_BIT))
+ return false;
+
+ return true;
+}
+
+enum smu_baco_state smu_v13_0_baco_get_state(struct smu_context *smu)
+{
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+
+ return smu_baco->state;
+}
+
+int smu_v13_0_baco_set_state(struct smu_context *smu,
+ enum smu_baco_state state)
+{
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ if (smu_v13_0_baco_get_state(smu) == state)
+ return 0;
+
+ if (state == SMU_BACO_STATE_ENTER) {
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_EnterBaco,
+ smu_baco->maco_support ?
+ BACO_SEQ_BAMACO : BACO_SEQ_BACO,
+ NULL);
+ } else {
+ ret = smu_cmn_send_smc_msg(smu,
+ SMU_MSG_ExitBaco,
+ NULL);
+ if (ret)
+ return ret;
+
+ /* clear vbios scratch 6 and 7 for coming asic reinit */
+ WREG32(adev->bios_scratch_reg_offset + 6, 0);
+ WREG32(adev->bios_scratch_reg_offset + 7, 0);
+ }
+
+ if (!ret)
+ smu_baco->state = state;
+
+ return ret;
+}
+
+int smu_v13_0_baco_enter(struct smu_context *smu)
+{
+ int ret = 0;
+
+ ret = smu_v13_0_baco_set_state(smu,
+ SMU_BACO_STATE_ENTER);
+ if (ret)
+ return ret;
+
+ msleep(10);
+
+ return ret;
+}
+
+int smu_v13_0_baco_exit(struct smu_context *smu)
+{
+ return smu_v13_0_baco_set_state(smu,
+ SMU_BACO_STATE_EXIT);
+}
+
+int smu_v13_0_set_gfx_power_up_by_imu(struct smu_context *smu)
+{
+ uint16_t index;
+
+ index = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_MSG,
+ SMU_MSG_EnableGfxImu);
+ /* Param 1 to tell PMFW to enable GFXOFF feature */
+ return smu_cmn_send_msg_without_waiting(smu, index, 1);
+}
+
+int smu_v13_0_od_edit_dpm_table(struct smu_context *smu,
+ enum PP_OD_DPM_TABLE_COMMAND type,
+ long input[], uint32_t size)
+{
+ struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
+ int ret = 0;
+
+ /* Only allowed in manual mode */
+ if (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+ return -EINVAL;
+
+ switch (type) {
+ case PP_OD_EDIT_SCLK_VDDC_TABLE:
+ if (size != 2) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+
+ if (input[0] == 0) {
+ if (input[1] < smu->gfx_default_hard_min_freq) {
+ dev_warn(smu->adev->dev,
+ "Fine grain setting minimum sclk (%ld) MHz is less than the minimum allowed (%d) MHz\n",
+ input[1], smu->gfx_default_hard_min_freq);
+ return -EINVAL;
+ }
+ smu->gfx_actual_hard_min_freq = input[1];
+ } else if (input[0] == 1) {
+ if (input[1] > smu->gfx_default_soft_max_freq) {
+ dev_warn(smu->adev->dev,
+ "Fine grain setting maximum sclk (%ld) MHz is greater than the maximum allowed (%d) MHz\n",
+ input[1], smu->gfx_default_soft_max_freq);
+ return -EINVAL;
+ }
+ smu->gfx_actual_soft_max_freq = input[1];
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case PP_OD_RESTORE_DEFAULT_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+ smu->gfx_actual_hard_min_freq = smu->gfx_default_hard_min_freq;
+ smu->gfx_actual_soft_max_freq = smu->gfx_default_soft_max_freq;
+ break;
+ case PP_OD_COMMIT_DPM_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+ if (smu->gfx_actual_hard_min_freq > smu->gfx_actual_soft_max_freq) {
+ dev_err(smu->adev->dev,
+ "The setting minimum sclk (%d) MHz is greater than the setting maximum sclk (%d) MHz\n",
+ smu->gfx_actual_hard_min_freq,
+ smu->gfx_actual_soft_max_freq);
+ return -EINVAL;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinGfxClk,
+ smu->gfx_actual_hard_min_freq,
+ NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Set hard min sclk failed!");
+ return ret;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxGfxClk,
+ smu->gfx_actual_soft_max_freq,
+ NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Set soft max sclk failed!");
+ return ret;
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return ret;
+}
+
+int smu_v13_0_set_default_dpm_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ return smu_cmn_update_table(smu, SMU_TABLE_DPMCLOCKS, 0,
+ smu_table->clocks_table, false);
+}
+
+void smu_v13_0_set_smu_mailbox_registers(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ smu->param_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_82);
+ smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_66);
+ smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_90);
+}
+
+int smu_v13_0_mode1_reset(struct smu_context *smu)
+{
+ int ret = 0;
+
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_Mode1Reset, NULL);
+ if (!ret)
+ msleep(SMU13_MODE1_RESET_WAIT_TIME_IN_MS);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
new file mode 100644
index 000000000..508e39254
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -0,0 +1,2061 @@
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define SWSMU_CODE_LAYER_L2
+
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "atomfirmware.h"
+#include "amdgpu_atomfirmware.h"
+#include "amdgpu_atombios.h"
+#include "smu_v13_0.h"
+#include "smu13_driver_if_v13_0_0.h"
+#include "soc15_common.h"
+#include "atom.h"
+#include "smu_v13_0_0_ppt.h"
+#include "smu_v13_0_0_pptable.h"
+#include "smu_v13_0_0_ppsmc.h"
+#include "nbio/nbio_4_3_0_offset.h"
+#include "nbio/nbio_4_3_0_sh_mask.h"
+#include "mp/mp_13_0_0_offset.h"
+#include "mp/mp_13_0_0_sh_mask.h"
+
+#include "asic_reg/mp/mp_13_0_0_sh_mask.h"
+#include "smu_cmn.h"
+#include "amdgpu_ras.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
+
+#define FEATURE_MASK(feature) (1ULL << feature)
+#define SMC_DPM_FEATURE ( \
+ FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_LINK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_FCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_MP0CLK_BIT))
+
+#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
+
+#define mmMP1_SMN_C2PMSG_66 0x0282
+#define mmMP1_SMN_C2PMSG_66_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_82 0x0292
+#define mmMP1_SMN_C2PMSG_82_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_90 0x029a
+#define mmMP1_SMN_C2PMSG_90_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_75 0x028b
+#define mmMP1_SMN_C2PMSG_75_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_53 0x0275
+#define mmMP1_SMN_C2PMSG_53_BASE_IDX 0
+
+#define mmMP1_SMN_C2PMSG_54 0x0276
+#define mmMP1_SMN_C2PMSG_54_BASE_IDX 0
+
+#define DEBUGSMC_MSG_Mode1Reset 2
+
+static struct cmn2asic_msg_mapping smu_v13_0_0_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
+ MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
+ MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
+ MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow, 0),
+ MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh, 0),
+ MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
+ MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
+ MSG_MAP(EnableSmuFeaturesLow, PPSMC_MSG_EnableSmuFeaturesLow, 1),
+ MSG_MAP(EnableSmuFeaturesHigh, PPSMC_MSG_EnableSmuFeaturesHigh, 1),
+ MSG_MAP(DisableSmuFeaturesLow, PPSMC_MSG_DisableSmuFeaturesLow, 1),
+ MSG_MAP(DisableSmuFeaturesHigh, PPSMC_MSG_DisableSmuFeaturesHigh, 1),
+ MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetRunningSmuFeaturesLow, 1),
+ MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh, 1),
+ MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
+ MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
+ MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
+ MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
+ MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
+ MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
+ MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
+ MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
+ MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
+ MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
+ MSG_MAP(EnterBaco, PPSMC_MSG_EnterBaco, 0),
+ MSG_MAP(ExitBaco, PPSMC_MSG_ExitBaco, 0),
+ MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 1),
+ MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 1),
+ MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 1),
+ MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
+ MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 1),
+ MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 1),
+ MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
+ MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 0),
+ MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 0),
+ MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 0),
+ MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 0),
+ MSG_MAP(GetDcModeMaxDpmFreq, PPSMC_MSG_GetDcModeMaxDpmFreq, 1),
+ MSG_MAP(OverridePcieParameters, PPSMC_MSG_OverridePcieParameters, 0),
+ MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
+ MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
+ MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
+ MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 0),
+ MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 0),
+ MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0),
+ MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
+ MSG_MAP(NotifyPowerSource, PPSMC_MSG_NotifyPowerSource, 0),
+ MSG_MAP(Mode1Reset, PPSMC_MSG_Mode1Reset, 0),
+ MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
+ MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
+ MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
+ MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired, 0),
+ MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
+ PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0),
+ MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0),
+ MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
+};
+
+static struct cmn2asic_mapping smu_v13_0_0_clk_map[SMU_CLK_COUNT] = {
+ CLK_MAP(GFXCLK, PPCLK_GFXCLK),
+ CLK_MAP(SCLK, PPCLK_GFXCLK),
+ CLK_MAP(SOCCLK, PPCLK_SOCCLK),
+ CLK_MAP(FCLK, PPCLK_FCLK),
+ CLK_MAP(UCLK, PPCLK_UCLK),
+ CLK_MAP(MCLK, PPCLK_UCLK),
+ CLK_MAP(VCLK, PPCLK_VCLK_0),
+ CLK_MAP(VCLK1, PPCLK_VCLK_1),
+ CLK_MAP(DCLK, PPCLK_DCLK_0),
+ CLK_MAP(DCLK1, PPCLK_DCLK_1),
+};
+
+static struct cmn2asic_mapping smu_v13_0_0_feature_mask_map[SMU_FEATURE_COUNT] = {
+ FEA_MAP(FW_DATA_READ),
+ FEA_MAP(DPM_GFXCLK),
+ FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
+ FEA_MAP(DPM_UCLK),
+ FEA_MAP(DPM_FCLK),
+ FEA_MAP(DPM_SOCCLK),
+ FEA_MAP(DPM_MP0CLK),
+ FEA_MAP(DPM_LINK),
+ FEA_MAP(DPM_DCN),
+ FEA_MAP(VMEMP_SCALING),
+ FEA_MAP(VDDIO_MEM_SCALING),
+ FEA_MAP(DS_GFXCLK),
+ FEA_MAP(DS_SOCCLK),
+ FEA_MAP(DS_FCLK),
+ FEA_MAP(DS_LCLK),
+ FEA_MAP(DS_DCFCLK),
+ FEA_MAP(DS_UCLK),
+ FEA_MAP(GFX_ULV),
+ FEA_MAP(FW_DSTATE),
+ FEA_MAP(GFXOFF),
+ FEA_MAP(BACO),
+ FEA_MAP(MM_DPM),
+ FEA_MAP(SOC_MPCLK_DS),
+ FEA_MAP(BACO_MPCLK_DS),
+ FEA_MAP(THROTTLERS),
+ FEA_MAP(SMARTSHIFT),
+ FEA_MAP(GTHR),
+ FEA_MAP(ACDC),
+ FEA_MAP(VR0HOT),
+ FEA_MAP(FW_CTF),
+ FEA_MAP(FAN_CONTROL),
+ FEA_MAP(GFX_DCS),
+ FEA_MAP(GFX_READ_MARGIN),
+ FEA_MAP(LED_DISPLAY),
+ FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
+ FEA_MAP(OUT_OF_BAND_MONITOR),
+ FEA_MAP(OPTIMIZED_VMIN),
+ FEA_MAP(GFX_IMU),
+ FEA_MAP(BOOT_TIME_CAL),
+ FEA_MAP(GFX_PCC_DFLL),
+ FEA_MAP(SOC_CG),
+ FEA_MAP(DF_CSTATE),
+ FEA_MAP(GFX_EDC),
+ FEA_MAP(BOOT_POWER_OPT),
+ FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
+ FEA_MAP(DS_VCN),
+ FEA_MAP(BACO_CG),
+ FEA_MAP(MEM_TEMP_READ),
+ FEA_MAP(ATHUB_MMHUB_PG),
+ FEA_MAP(SOC_PCC),
+ [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
+ [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
+ [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
+};
+
+static struct cmn2asic_mapping smu_v13_0_0_table_map[SMU_TABLE_COUNT] = {
+ TAB_MAP(PPTABLE),
+ TAB_MAP(WATERMARKS),
+ TAB_MAP(AVFS_PSM_DEBUG),
+ TAB_MAP(PMSTATUSLOG),
+ TAB_MAP(SMU_METRICS),
+ TAB_MAP(DRIVER_SMU_CONFIG),
+ TAB_MAP(ACTIVITY_MONITOR_COEFF),
+ [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
+ TAB_MAP(I2C_COMMANDS),
+};
+
+static struct cmn2asic_mapping smu_v13_0_0_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
+ PWR_MAP(AC),
+ PWR_MAP(DC),
+};
+
+static struct cmn2asic_mapping smu_v13_0_0_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D, WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING, WORKLOAD_PPLIB_POWER_SAVING_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO, WORKLOAD_PPLIB_VIDEO_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR, WORKLOAD_PPLIB_VR_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE, WORKLOAD_PPLIB_COMPUTE_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM, WORKLOAD_PPLIB_CUSTOM_BIT),
+};
+
+static const uint8_t smu_v13_0_0_throttler_map[] = {
+ [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
+ [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
+ [THROTTLER_PPT2_BIT] = (SMU_THROTTLER_PPT2_BIT),
+ [THROTTLER_PPT3_BIT] = (SMU_THROTTLER_PPT3_BIT),
+ [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
+ [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
+ [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
+ [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
+ [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
+ [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
+ [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
+ [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
+ [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
+ [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
+ [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
+ [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
+ [THROTTLER_FIT_BIT] = (SMU_THROTTLER_FIT_BIT),
+};
+
+static int
+smu_v13_0_0_get_allowed_feature_mask(struct smu_context *smu,
+ uint32_t *feature_mask, uint32_t num)
+{
+ struct amdgpu_device *adev = smu->adev;
+ u32 smu_version;
+
+ if (num > 2)
+ return -EINVAL;
+
+ memset(feature_mask, 0xff, sizeof(uint32_t) * num);
+
+ if (!(adev->pm.pp_feature & PP_SCLK_DPM_MASK)) {
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_IMU_BIT);
+ }
+
+ if (!(adev->pg_flags & AMD_PG_SUPPORT_ATHUB) ||
+ !(adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);
+
+ if (!(adev->pm.pp_feature & PP_SOCCLK_DPM_MASK))
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);
+
+ /* PMFW 78.58 contains a critical fix for gfxoff feature */
+ smu_cmn_get_smc_version(smu, NULL, &smu_version);
+ if ((smu_version < 0x004e3a00) ||
+ !(adev->pm.pp_feature & PP_GFXOFF_MASK))
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFXOFF_BIT);
+
+ if (!(adev->pm.pp_feature & PP_MCLK_DPM_MASK)) {
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
+ }
+
+ if (!(adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK))
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);
+
+ if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_LINK_BIT);
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_LCLK_BIT);
+ }
+
+ if (!(adev->pm.pp_feature & PP_ULV_MASK))
+ *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_ULV_BIT);
+
+ return 0;
+}
+
+static int smu_v13_0_0_check_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_0_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+
+ if (powerplay_table->platform_caps & SMU_13_0_0_PP_PLATFORM_CAP_HARDWAREDC)
+ smu->dc_controlled_by_gpio = true;
+
+ if (powerplay_table->platform_caps & SMU_13_0_0_PP_PLATFORM_CAP_BACO ||
+ powerplay_table->platform_caps & SMU_13_0_0_PP_PLATFORM_CAP_MACO)
+ smu_baco->platform_support = true;
+
+ if (powerplay_table->platform_caps & SMU_13_0_0_PP_PLATFORM_CAP_MACO)
+ smu_baco->maco_support = true;
+
+ table_context->thermal_controller_type =
+ powerplay_table->thermal_controller_type;
+
+ /*
+ * Instead of having its own buffer space and get overdrive_table copied,
+ * smu->od_settings just points to the actual overdrive_table
+ */
+ smu->od_settings = &powerplay_table->overdrive_table;
+
+ return 0;
+}
+
+static int smu_v13_0_0_store_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_0_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+
+ memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
+ sizeof(PPTable_t));
+
+ return 0;
+}
+
+#ifndef atom_smc_dpm_info_table_13_0_0
+struct atom_smc_dpm_info_table_13_0_0 {
+ struct atom_common_table_header table_header;
+ BoardTable_t BoardTable;
+};
+#endif
+
+static int smu_v13_0_0_append_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ struct atom_smc_dpm_info_table_13_0_0 *smc_dpm_table;
+ BoardTable_t *BoardTable = &smc_pptable->BoardTable;
+ int index, ret;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ smc_dpm_info);
+
+ ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
+ (uint8_t **)&smc_dpm_table);
+ if (ret)
+ return ret;
+
+ memcpy(BoardTable, &smc_dpm_table->BoardTable, sizeof(BoardTable_t));
+
+ return 0;
+}
+
+static int smu_v13_0_0_get_pptable_from_pmfw(struct smu_context *smu,
+ void **table,
+ uint32_t *size)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ void *combo_pptable = smu_table->combo_pptable;
+ int ret = 0;
+
+ ret = smu_cmn_get_combo_pptable(smu);
+ if (ret)
+ return ret;
+
+ *table = combo_pptable;
+ *size = sizeof(struct smu_13_0_0_powerplay_table);
+
+ return 0;
+}
+
+static int smu_v13_0_0_setup_pptable(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ if (amdgpu_sriov_vf(smu->adev))
+ return 0;
+
+ ret = smu_v13_0_0_get_pptable_from_pmfw(smu,
+ &smu_table->power_play_table,
+ &smu_table->power_play_table_size);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_0_store_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * With SCPM enabled, the operation below will be handled
+ * by PSP. Driver involvment is unnecessary and useless.
+ */
+ if (!adev->scpm_enabled) {
+ ret = smu_v13_0_0_append_powerplay_table(smu);
+ if (ret)
+ return ret;
+ }
+
+ ret = smu_v13_0_0_check_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int smu_v13_0_0_tables_init(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU13_TOOL_SIZE,
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+ sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
+ if (!smu_table->metrics_table)
+ goto err0_out;
+ smu_table->metrics_time = 0;
+
+ smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
+ smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
+ if (!smu_table->gpu_metrics_table)
+ goto err1_out;
+
+ smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
+ if (!smu_table->watermarks_table)
+ goto err2_out;
+
+ return 0;
+
+err2_out:
+ kfree(smu_table->gpu_metrics_table);
+err1_out:
+ kfree(smu_table->metrics_table);
+err0_out:
+ return -ENOMEM;
+}
+
+static int smu_v13_0_0_allocate_dpm_context(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ smu_dpm->dpm_context = kzalloc(sizeof(struct smu_13_0_dpm_context),
+ GFP_KERNEL);
+ if (!smu_dpm->dpm_context)
+ return -ENOMEM;
+
+ smu_dpm->dpm_context_size = sizeof(struct smu_13_0_dpm_context);
+
+ return 0;
+}
+
+static int smu_v13_0_0_init_smc_tables(struct smu_context *smu)
+{
+ int ret = 0;
+
+ ret = smu_v13_0_0_tables_init(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_0_allocate_dpm_context(smu);
+ if (ret)
+ return ret;
+
+ return smu_v13_0_init_smc_tables(smu);
+}
+
+static int smu_v13_0_0_set_default_dpm_table(struct smu_context *smu)
+{
+ struct smu_13_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+ struct smu_13_0_dpm_table *dpm_table;
+ struct smu_13_0_pcie_table *pcie_table;
+ uint32_t link_level;
+ int ret = 0;
+
+ /* socclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.soc_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_SOCCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* gfxclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.gfx_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_GFXCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+
+ /*
+ * Update the reported maximum shader clock to the value
+ * which can be guarded to be achieved on all cards. This
+ * is aligned with Window setting. And considering that value
+ * might be not the peak frequency the card can achieve, it
+ * is normal some real-time clock frequency can overtake this
+ * labelled maximum clock frequency(for example in pp_dpm_sclk
+ * sysfs output).
+ */
+ if (skutable->DriverReportedClocks.GameClockAc &&
+ (dpm_table->dpm_levels[dpm_table->count - 1].value >
+ skutable->DriverReportedClocks.GameClockAc)) {
+ dpm_table->dpm_levels[dpm_table->count - 1].value =
+ skutable->DriverReportedClocks.GameClockAc;
+ dpm_table->max = skutable->DriverReportedClocks.GameClockAc;
+ }
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* uclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.uclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_UCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* fclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.fclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_FCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* vclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.vclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_VCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* dclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.dclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_DCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* lclk dpm table setup */
+ pcie_table = &dpm_context->dpm_tables.pcie_table;
+ pcie_table->num_of_link_levels = 0;
+ for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
+ if (!skutable->PcieGenSpeed[link_level] &&
+ !skutable->PcieLaneCount[link_level] &&
+ !skutable->LclkFreq[link_level])
+ continue;
+
+ pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
+ skutable->PcieGenSpeed[link_level];
+ pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
+ skutable->PcieLaneCount[link_level];
+ pcie_table->clk_freq[pcie_table->num_of_link_levels] =
+ skutable->LclkFreq[link_level];
+ pcie_table->num_of_link_levels++;
+ }
+
+ return 0;
+}
+
+static bool smu_v13_0_0_is_dpm_running(struct smu_context *smu)
+{
+ int ret = 0;
+ uint64_t feature_enabled;
+
+ ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
+ if (ret)
+ return false;
+
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static void smu_v13_0_0_dump_pptable(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+
+ dev_info(smu->adev->dev, "Dumped PPTable:\n");
+
+ dev_info(smu->adev->dev, "Version = 0x%08x\n", skutable->Version);
+ dev_info(smu->adev->dev, "FeaturesToRun[0] = 0x%08x\n", skutable->FeaturesToRun[0]);
+ dev_info(smu->adev->dev, "FeaturesToRun[1] = 0x%08x\n", skutable->FeaturesToRun[1]);
+}
+
+static int smu_v13_0_0_system_features_control(struct smu_context *smu,
+ bool en)
+{
+ return smu_v13_0_system_features_control(smu, en);
+}
+
+static uint32_t smu_v13_0_get_throttler_status(SmuMetrics_t *metrics)
+{
+ uint32_t throttler_status = 0;
+ int i;
+
+ for (i = 0; i < THROTTLER_COUNT; i++)
+ throttler_status |=
+ (metrics->ThrottlingPercentage[i] ? 1U << i : 0);
+
+ return throttler_status;
+}
+
+#define SMU_13_0_0_BUSY_THRESHOLD 15
+static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu,
+ MetricsMember_t member,
+ uint32_t *value)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ SmuMetrics_t *metrics =
+ &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu,
+ NULL,
+ false);
+ if (ret)
+ return ret;
+
+ switch (member) {
+ case METRICS_CURR_GFXCLK:
+ *value = metrics->CurrClock[PPCLK_GFXCLK];
+ break;
+ case METRICS_CURR_SOCCLK:
+ *value = metrics->CurrClock[PPCLK_SOCCLK];
+ break;
+ case METRICS_CURR_UCLK:
+ *value = metrics->CurrClock[PPCLK_UCLK];
+ break;
+ case METRICS_CURR_VCLK:
+ *value = metrics->CurrClock[PPCLK_VCLK_0];
+ break;
+ case METRICS_CURR_VCLK1:
+ *value = metrics->CurrClock[PPCLK_VCLK_1];
+ break;
+ case METRICS_CURR_DCLK:
+ *value = metrics->CurrClock[PPCLK_DCLK_0];
+ break;
+ case METRICS_CURR_DCLK1:
+ *value = metrics->CurrClock[PPCLK_DCLK_1];
+ break;
+ case METRICS_CURR_FCLK:
+ *value = metrics->CurrClock[PPCLK_FCLK];
+ break;
+ case METRICS_AVERAGE_GFXCLK:
+ if (metrics->AverageGfxActivity <= SMU_13_0_0_BUSY_THRESHOLD)
+ *value = metrics->AverageGfxclkFrequencyPostDs;
+ else
+ *value = metrics->AverageGfxclkFrequencyPreDs;
+ break;
+ case METRICS_AVERAGE_FCLK:
+ if (metrics->AverageUclkActivity <= SMU_13_0_0_BUSY_THRESHOLD)
+ *value = metrics->AverageFclkFrequencyPostDs;
+ else
+ *value = metrics->AverageFclkFrequencyPreDs;
+ break;
+ case METRICS_AVERAGE_UCLK:
+ if (metrics->AverageUclkActivity <= SMU_13_0_0_BUSY_THRESHOLD)
+ *value = metrics->AverageMemclkFrequencyPostDs;
+ else
+ *value = metrics->AverageMemclkFrequencyPreDs;
+ break;
+ case METRICS_AVERAGE_VCLK:
+ *value = metrics->AverageVclk0Frequency;
+ break;
+ case METRICS_AVERAGE_DCLK:
+ *value = metrics->AverageDclk0Frequency;
+ break;
+ case METRICS_AVERAGE_VCLK1:
+ *value = metrics->AverageVclk1Frequency;
+ break;
+ case METRICS_AVERAGE_DCLK1:
+ *value = metrics->AverageDclk1Frequency;
+ break;
+ case METRICS_AVERAGE_GFXACTIVITY:
+ *value = metrics->AverageGfxActivity;
+ break;
+ case METRICS_AVERAGE_MEMACTIVITY:
+ *value = metrics->AverageUclkActivity;
+ break;
+ case METRICS_AVERAGE_SOCKETPOWER:
+ *value = metrics->AverageSocketPower << 8;
+ break;
+ case METRICS_TEMPERATURE_EDGE:
+ *value = metrics->AvgTemperature[TEMP_EDGE] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_HOTSPOT:
+ *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_MEM:
+ *value = metrics->AvgTemperature[TEMP_MEM] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRGFX:
+ *value = metrics->AvgTemperature[TEMP_VR_GFX] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRSOC:
+ *value = metrics->AvgTemperature[TEMP_VR_SOC] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_THROTTLER_STATUS:
+ *value = smu_v13_0_get_throttler_status(metrics);
+ break;
+ case METRICS_CURR_FANSPEED:
+ *value = metrics->AvgFanRpm;
+ break;
+ case METRICS_CURR_FANPWM:
+ *value = metrics->AvgFanPwm;
+ break;
+ case METRICS_VOLTAGE_VDDGFX:
+ *value = metrics->AvgVoltage[SVI_PLANE_GFX];
+ break;
+ case METRICS_PCIE_RATE:
+ *value = metrics->PcieRate;
+ break;
+ case METRICS_PCIE_WIDTH:
+ *value = metrics->PcieWidth;
+ break;
+ default:
+ *value = UINT_MAX;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_0_get_dpm_ultimate_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *min,
+ uint32_t *max)
+{
+ struct smu_13_0_dpm_context *dpm_context =
+ smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *dpm_table;
+
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ /* uclk dpm table */
+ dpm_table = &dpm_context->dpm_tables.uclk_table;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ /* gfxclk dpm table */
+ dpm_table = &dpm_context->dpm_tables.gfx_table;
+ break;
+ case SMU_SOCCLK:
+ /* socclk dpm table */
+ dpm_table = &dpm_context->dpm_tables.soc_table;
+ break;
+ case SMU_FCLK:
+ /* fclk dpm table */
+ dpm_table = &dpm_context->dpm_tables.fclk_table;
+ break;
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ /* vclk dpm table */
+ dpm_table = &dpm_context->dpm_tables.vclk_table;
+ break;
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ /* dclk dpm table */
+ dpm_table = &dpm_context->dpm_tables.dclk_table;
+ break;
+ default:
+ dev_err(smu->adev->dev, "Unsupported clock type!\n");
+ return -EINVAL;
+ }
+
+ if (min)
+ *min = dpm_table->min;
+ if (max)
+ *max = dpm_table->max;
+
+ return 0;
+}
+
+static int smu_v13_0_0_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data,
+ uint32_t *size)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ int ret = 0;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
+ *(uint16_t *)data = smc_pptable->SkuTable.FanMaximumRpm;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_MEMACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_SOCKETPOWER,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_HOTSPOT,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_EDGE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_MEM,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_CURR_UCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDGFX,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_0_get_current_clk_freq_by_table(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ MetricsMember_t member_type;
+ int clk_id = 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return -EINVAL;
+
+ switch (clk_id) {
+ case PPCLK_GFXCLK:
+ member_type = METRICS_AVERAGE_GFXCLK;
+ break;
+ case PPCLK_UCLK:
+ member_type = METRICS_CURR_UCLK;
+ break;
+ case PPCLK_FCLK:
+ member_type = METRICS_CURR_FCLK;
+ break;
+ case PPCLK_SOCCLK:
+ member_type = METRICS_CURR_SOCCLK;
+ break;
+ case PPCLK_VCLK_0:
+ member_type = METRICS_AVERAGE_VCLK;
+ break;
+ case PPCLK_DCLK_0:
+ member_type = METRICS_AVERAGE_DCLK;
+ break;
+ case PPCLK_VCLK_1:
+ member_type = METRICS_AVERAGE_VCLK1;
+ break;
+ case PPCLK_DCLK_1:
+ member_type = METRICS_AVERAGE_DCLK1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return smu_v13_0_0_get_smu_metrics_data(smu,
+ member_type,
+ value);
+}
+
+static int smu_v13_0_0_print_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ char *buf)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_13_0_dpm_table *single_dpm_table;
+ struct smu_13_0_pcie_table *pcie_table;
+ const int link_width[] = {0, 1, 2, 4, 8, 12, 16};
+ uint32_t gen_speed, lane_width;
+ int i, curr_freq, size = 0;
+ int ret = 0;
+
+ smu_cmn_get_sysfs_buf(&buf, &size);
+
+ if (amdgpu_ras_intr_triggered()) {
+ size += sysfs_emit_at(buf, size, "unavailable\n");
+ return size;
+ }
+
+ switch (clk_type) {
+ case SMU_SCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
+ break;
+ case SMU_MCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
+ break;
+ case SMU_SOCCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.soc_table);
+ break;
+ case SMU_FCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
+ break;
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
+ break;
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
+ break;
+ default:
+ break;
+ }
+
+ switch (clk_type) {
+ case SMU_SCLK:
+ case SMU_MCLK:
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ ret = smu_v13_0_0_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to get current clock freq!");
+ return ret;
+ }
+
+ if (single_dpm_table->is_fine_grained) {
+ /*
+ * For fine grained dpms, there are only two dpm levels:
+ * - level 0 -> min clock freq
+ * - level 1 -> max clock freq
+ * And the current clock frequency can be any value between them.
+ * So, if the current clock frequency is not at level 0 or level 1,
+ * we will fake it as three dpm levels:
+ * - level 0 -> min clock freq
+ * - level 1 -> current actual clock freq
+ * - level 2 -> max clock freq
+ */
+ if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
+ (single_dpm_table->dpm_levels[1].value != curr_freq)) {
+ size += sysfs_emit_at(buf, size, "0: %uMhz\n",
+ single_dpm_table->dpm_levels[0].value);
+ size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
+ curr_freq);
+ size += sysfs_emit_at(buf, size, "2: %uMhz\n",
+ single_dpm_table->dpm_levels[1].value);
+ } else {
+ size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
+ single_dpm_table->dpm_levels[0].value,
+ single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
+ size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
+ single_dpm_table->dpm_levels[1].value,
+ single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
+ }
+ } else {
+ for (i = 0; i < single_dpm_table->count; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, single_dpm_table->dpm_levels[i].value,
+ single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
+ }
+ break;
+ case SMU_PCIE:
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_PCIE_RATE,
+ &gen_speed);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_PCIE_WIDTH,
+ &lane_width);
+ if (ret)
+ return ret;
+
+ pcie_table = &(dpm_context->dpm_tables.pcie_table);
+ for (i = 0; i < pcie_table->num_of_link_levels; i++)
+ size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
+ (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
+ (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
+ (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
+ (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "",
+ (pcie_table->pcie_lane[i] == 1) ? "x1" :
+ (pcie_table->pcie_lane[i] == 2) ? "x2" :
+ (pcie_table->pcie_lane[i] == 3) ? "x4" :
+ (pcie_table->pcie_lane[i] == 4) ? "x8" :
+ (pcie_table->pcie_lane[i] == 5) ? "x12" :
+ (pcie_table->pcie_lane[i] == 6) ? "x16" : "",
+ pcie_table->clk_freq[i],
+ ((gen_speed - 1) == pcie_table->pcie_gen[i]) &&
+ (lane_width == link_width[pcie_table->pcie_lane[i]]) ?
+ "*" : "");
+ break;
+
+ default:
+ break;
+ }
+
+ return size;
+}
+
+static int smu_v13_0_0_force_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t mask)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_13_0_dpm_table *single_dpm_table;
+ uint32_t soft_min_level, soft_max_level;
+ uint32_t min_freq, max_freq;
+ int ret = 0;
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
+ break;
+ case SMU_SOCCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.soc_table);
+ break;
+ case SMU_FCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
+ break;
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
+ break;
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
+ break;
+ default:
+ break;
+ }
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ if (single_dpm_table->is_fine_grained) {
+ /* There is only 2 levels for fine grained DPM */
+ soft_max_level = (soft_max_level >= 1 ? 1 : 0);
+ soft_min_level = (soft_min_level >= 1 ? 1 : 0);
+ } else {
+ if ((soft_max_level >= single_dpm_table->count) ||
+ (soft_min_level >= single_dpm_table->count))
+ return -EINVAL;
+ }
+
+ min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
+ max_freq = single_dpm_table->dpm_levels[soft_max_level].value;
+
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ clk_type,
+ min_freq,
+ max_freq);
+ break;
+ case SMU_DCEFCLK:
+ case SMU_PCIE:
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_0_update_pcie_parameters(struct smu_context *smu,
+ uint32_t pcie_gen_cap,
+ uint32_t pcie_width_cap)
+{
+ struct smu_13_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+ struct smu_13_0_pcie_table *pcie_table =
+ &dpm_context->dpm_tables.pcie_table;
+ uint32_t smu_pcie_arg;
+ int ret, i;
+
+ for (i = 0; i < pcie_table->num_of_link_levels; i++) {
+ if (pcie_table->pcie_gen[i] > pcie_gen_cap)
+ pcie_table->pcie_gen[i] = pcie_gen_cap;
+ if (pcie_table->pcie_lane[i] > pcie_width_cap)
+ pcie_table->pcie_lane[i] = pcie_width_cap;
+
+ smu_pcie_arg = i << 16;
+ smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
+ smu_pcie_arg |= pcie_table->pcie_lane[i];
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_OverridePcieParameters,
+ smu_pcie_arg,
+ NULL);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct smu_temperature_range smu13_thermal_policy[] = {
+ {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
+ { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
+};
+
+static int smu_v13_0_0_get_thermal_temperature_range(struct smu_context *smu,
+ struct smu_temperature_range *range)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_0_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+
+ if (amdgpu_sriov_vf(smu->adev))
+ return 0;
+
+ if (!range)
+ return -EINVAL;
+
+ memcpy(range, &smu13_thermal_policy[0], sizeof(struct smu_temperature_range));
+
+ range->max = pptable->SkuTable.TemperatureLimit[TEMP_EDGE] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->edge_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->hotspot_crit_max = pptable->SkuTable.TemperatureLimit[TEMP_HOTSPOT] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->hotspot_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->mem_crit_max = pptable->SkuTable.TemperatureLimit[TEMP_MEM] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->mem_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
+
+ return 0;
+}
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+static ssize_t smu_v13_0_0_get_gpu_metrics(struct smu_context *smu,
+ void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct gpu_metrics_v1_3 *gpu_metrics =
+ (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
+ SmuMetricsExternal_t metrics_ext;
+ SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu,
+ &metrics_ext,
+ true);
+ if (ret)
+ return ret;
+
+ smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
+
+ gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
+ gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
+ gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
+ gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
+ gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
+ gpu_metrics->temperature_vrmem = MAX(metrics->AvgTemperature[TEMP_VR_MEM0],
+ metrics->AvgTemperature[TEMP_VR_MEM1]);
+
+ gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
+ gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
+ gpu_metrics->average_mm_activity = MAX(metrics->Vcn0ActivityPercentage,
+ metrics->Vcn1ActivityPercentage);
+
+ gpu_metrics->average_socket_power = metrics->AverageSocketPower;
+ gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
+
+ if (metrics->AverageGfxActivity <= SMU_13_0_0_BUSY_THRESHOLD)
+ gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
+ else
+ gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
+
+ if (metrics->AverageUclkActivity <= SMU_13_0_0_BUSY_THRESHOLD)
+ gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
+ else
+ gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
+
+ gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
+ gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
+ gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
+ gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
+
+ gpu_metrics->current_gfxclk = metrics->CurrClock[PPCLK_GFXCLK];
+ gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
+ gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
+ gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
+ gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
+ gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_1];
+ gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_1];
+
+ gpu_metrics->throttle_status =
+ smu_v13_0_get_throttler_status(metrics);
+ gpu_metrics->indep_throttle_status =
+ smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
+ smu_v13_0_0_throttler_map);
+
+ gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
+
+ gpu_metrics->pcie_link_width = metrics->PcieWidth;
+ gpu_metrics->pcie_link_speed = metrics->PcieRate;
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+
+ gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_GFX];
+ gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_SOC];
+ gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VMEMP];
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v1_3);
+}
+
+static int smu_v13_0_0_populate_umd_state_clk(struct smu_context *smu)
+{
+ struct smu_13_0_dpm_context *dpm_context =
+ smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *gfx_table =
+ &dpm_context->dpm_tables.gfx_table;
+ struct smu_13_0_dpm_table *mem_table =
+ &dpm_context->dpm_tables.uclk_table;
+ struct smu_13_0_dpm_table *soc_table =
+ &dpm_context->dpm_tables.soc_table;
+ struct smu_13_0_dpm_table *vclk_table =
+ &dpm_context->dpm_tables.vclk_table;
+ struct smu_13_0_dpm_table *dclk_table =
+ &dpm_context->dpm_tables.dclk_table;
+ struct smu_13_0_dpm_table *fclk_table =
+ &dpm_context->dpm_tables.fclk_table;
+ struct smu_umd_pstate_table *pstate_table =
+ &smu->pstate_table;
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ DriverReportedClocks_t driver_clocks =
+ pptable->SkuTable.DriverReportedClocks;
+
+ pstate_table->gfxclk_pstate.min = gfx_table->min;
+ if (driver_clocks.GameClockAc &&
+ (driver_clocks.GameClockAc < gfx_table->max))
+ pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc;
+ else
+ pstate_table->gfxclk_pstate.peak = gfx_table->max;
+
+ pstate_table->uclk_pstate.min = mem_table->min;
+ pstate_table->uclk_pstate.peak = mem_table->max;
+
+ pstate_table->socclk_pstate.min = soc_table->min;
+ pstate_table->socclk_pstate.peak = soc_table->max;
+
+ pstate_table->vclk_pstate.min = vclk_table->min;
+ pstate_table->vclk_pstate.peak = vclk_table->max;
+
+ pstate_table->dclk_pstate.min = dclk_table->min;
+ pstate_table->dclk_pstate.peak = dclk_table->max;
+
+ pstate_table->fclk_pstate.min = fclk_table->min;
+ pstate_table->fclk_pstate.peak = fclk_table->max;
+
+ if (driver_clocks.BaseClockAc &&
+ driver_clocks.BaseClockAc < gfx_table->max)
+ pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc;
+ else
+ pstate_table->gfxclk_pstate.standard = gfx_table->max;
+ pstate_table->uclk_pstate.standard = mem_table->max;
+ pstate_table->socclk_pstate.standard = soc_table->min;
+ pstate_table->vclk_pstate.standard = vclk_table->min;
+ pstate_table->dclk_pstate.standard = dclk_table->min;
+ pstate_table->fclk_pstate.standard = fclk_table->min;
+
+ return 0;
+}
+
+static void smu_v13_0_0_get_unique_id(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ SmuMetrics_t *metrics =
+ &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t upper32 = 0, lower32 = 0;
+ int ret;
+
+ ret = smu_cmn_get_metrics_table(smu, NULL, false);
+ if (ret)
+ goto out;
+
+ upper32 = metrics->PublicSerialNumberUpper;
+ lower32 = metrics->PublicSerialNumberLower;
+
+out:
+ adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
+ if (adev->serial[0] == '\0')
+ sprintf(adev->serial, "%016llx", adev->unique_id);
+}
+
+static int smu_v13_0_0_get_fan_speed_pwm(struct smu_context *smu,
+ uint32_t *speed)
+{
+ int ret;
+
+ if (!speed)
+ return -EINVAL;
+
+ ret = smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_CURR_FANPWM,
+ speed);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to get fan speed(PWM)!");
+ return ret;
+ }
+
+ /* Convert the PMFW output which is in percent to pwm(255) based */
+ *speed = MIN(*speed * 255 / 100, 255);
+
+ return 0;
+}
+
+static int smu_v13_0_0_get_fan_speed_rpm(struct smu_context *smu,
+ uint32_t *speed)
+{
+ if (!speed)
+ return -EINVAL;
+
+ return smu_v13_0_0_get_smu_metrics_data(smu,
+ METRICS_CURR_FANSPEED,
+ speed);
+}
+
+static int smu_v13_0_0_enable_mgpu_fan_boost(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+
+ /*
+ * Skip the MGpuFanBoost setting for those ASICs
+ * which do not support it
+ */
+ if (skutable->MGpuAcousticLimitRpmThreshold == 0)
+ return 0;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetMGpuFanBoostLimitRpm,
+ 0,
+ NULL);
+}
+
+static int smu_v13_0_0_get_power_limit(struct smu_context *smu,
+ uint32_t *current_power_limit,
+ uint32_t *default_power_limit,
+ uint32_t *max_power_limit)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_0_powerplay_table *powerplay_table =
+ (struct smu_13_0_0_powerplay_table *)table_context->power_play_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+ uint32_t power_limit, od_percent;
+
+ if (smu_v13_0_get_current_power_limit(smu, &power_limit))
+ power_limit = smu->adev->pm.ac_power ?
+ skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
+ skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
+
+ if (current_power_limit)
+ *current_power_limit = power_limit;
+ if (default_power_limit)
+ *default_power_limit = power_limit;
+
+ if (max_power_limit) {
+ if (smu->od_enabled) {
+ od_percent = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]);
+
+ dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n", od_percent, power_limit);
+
+ power_limit *= (100 + od_percent);
+ power_limit /= 100;
+ }
+ *max_power_limit = power_limit;
+ }
+
+ return 0;
+}
+
+static int smu_v13_0_0_get_power_profile_mode(struct smu_context *smu,
+ char *buf)
+{
+ DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
+ DpmActivityMonitorCoeffInt_t *activity_monitor =
+ &(activity_monitor_external.DpmActivityMonitorCoeffInt);
+ static const char *title[] = {
+ "PROFILE_INDEX(NAME)",
+ "CLOCK_TYPE(NAME)",
+ "FPS",
+ "MinActiveFreqType",
+ "MinActiveFreq",
+ "BoosterFreqType",
+ "BoosterFreq",
+ "PD_Data_limit_c",
+ "PD_Data_error_coeff",
+ "PD_Data_error_rate_coeff"};
+ int16_t workload_type = 0;
+ uint32_t i, size = 0;
+ int result = 0;
+
+ if (!buf)
+ return -EINVAL;
+
+ size += sysfs_emit_at(buf, size, "%16s %s %s %s %s %s %s %s %s %s\n",
+ title[0], title[1], title[2], title[3], title[4], title[5],
+ title[6], title[7], title[8], title[9]);
+
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_CUSTOM; i++) {
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_WORKLOAD,
+ i);
+ if (workload_type < 0)
+ return -EINVAL;
+
+ result = smu_cmn_update_table(smu,
+ SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+ workload_type,
+ (void *)(&activity_monitor_external),
+ false);
+ if (result) {
+ dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
+ return result;
+ }
+
+ size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
+ i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");
+
+ size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 0,
+ "GFXCLK",
+ activity_monitor->Gfx_FPS,
+ activity_monitor->Gfx_MinActiveFreqType,
+ activity_monitor->Gfx_MinActiveFreq,
+ activity_monitor->Gfx_BoosterFreqType,
+ activity_monitor->Gfx_BoosterFreq,
+ activity_monitor->Gfx_PD_Data_limit_c,
+ activity_monitor->Gfx_PD_Data_error_coeff,
+ activity_monitor->Gfx_PD_Data_error_rate_coeff);
+
+ size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ " ",
+ 1,
+ "FCLK",
+ activity_monitor->Fclk_FPS,
+ activity_monitor->Fclk_MinActiveFreqType,
+ activity_monitor->Fclk_MinActiveFreq,
+ activity_monitor->Fclk_BoosterFreqType,
+ activity_monitor->Fclk_BoosterFreq,
+ activity_monitor->Fclk_PD_Data_limit_c,
+ activity_monitor->Fclk_PD_Data_error_coeff,
+ activity_monitor->Fclk_PD_Data_error_rate_coeff);
+ }
+
+ return size;
+}
+
+static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu,
+ long *input,
+ uint32_t size)
+{
+ DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
+ DpmActivityMonitorCoeffInt_t *activity_monitor =
+ &(activity_monitor_external.DpmActivityMonitorCoeffInt);
+ int workload_type, ret = 0;
+
+ smu->power_profile_mode = input[size];
+
+ if (smu->power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) {
+ dev_err(smu->adev->dev, "Invalid power profile mode %d\n", smu->power_profile_mode);
+ return -EINVAL;
+ }
+
+ if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+ ret = smu_cmn_update_table(smu,
+ SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+ WORKLOAD_PPLIB_CUSTOM_BIT,
+ (void *)(&activity_monitor_external),
+ false);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
+ return ret;
+ }
+
+ switch (input[0]) {
+ case 0: /* Gfxclk */
+ activity_monitor->Gfx_FPS = input[1];
+ activity_monitor->Gfx_MinActiveFreqType = input[2];
+ activity_monitor->Gfx_MinActiveFreq = input[3];
+ activity_monitor->Gfx_BoosterFreqType = input[4];
+ activity_monitor->Gfx_BoosterFreq = input[5];
+ activity_monitor->Gfx_PD_Data_limit_c = input[6];
+ activity_monitor->Gfx_PD_Data_error_coeff = input[7];
+ activity_monitor->Gfx_PD_Data_error_rate_coeff = input[8];
+ break;
+ case 1: /* Fclk */
+ activity_monitor->Fclk_FPS = input[1];
+ activity_monitor->Fclk_MinActiveFreqType = input[2];
+ activity_monitor->Fclk_MinActiveFreq = input[3];
+ activity_monitor->Fclk_BoosterFreqType = input[4];
+ activity_monitor->Fclk_BoosterFreq = input[5];
+ activity_monitor->Fclk_PD_Data_limit_c = input[6];
+ activity_monitor->Fclk_PD_Data_error_coeff = input[7];
+ activity_monitor->Fclk_PD_Data_error_rate_coeff = input[8];
+ break;
+ }
+
+ ret = smu_cmn_update_table(smu,
+ SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+ WORKLOAD_PPLIB_CUSTOM_BIT,
+ (void *)(&activity_monitor_external),
+ true);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
+ return ret;
+ }
+ }
+
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_WORKLOAD,
+ smu->power_profile_mode);
+ if (workload_type < 0)
+ return -EINVAL;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetWorkloadMask,
+ 1 << workload_type,
+ NULL);
+}
+
+static int smu_v13_0_0_baco_enter(struct smu_context *smu)
+{
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
+ return smu_v13_0_baco_set_armd3_sequence(smu,
+ smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
+ else
+ return smu_v13_0_baco_enter(smu);
+}
+
+static int smu_v13_0_0_baco_exit(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
+ /* Wait for PMFW handling for the Dstate change */
+ usleep_range(10000, 11000);
+ return smu_v13_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
+ } else {
+ return smu_v13_0_baco_exit(smu);
+ }
+}
+
+static bool smu_v13_0_0_is_mode1_reset_supported(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ u32 smu_version;
+
+ /* SRIOV does not support SMU mode1 reset */
+ if (amdgpu_sriov_vf(adev))
+ return false;
+
+ /* PMFW support is available since 78.41 */
+ smu_cmn_get_smc_version(smu, NULL, &smu_version);
+ if (smu_version < 0x004e2900)
+ return false;
+
+ return true;
+}
+
+static int smu_v13_0_0_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msg, int num_msgs)
+{
+ struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
+ struct amdgpu_device *adev = smu_i2c->adev;
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *table = &smu_table->driver_table;
+ SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
+ int i, j, r, c;
+ u16 dir;
+
+ if (!adev->pm.dpm_enabled)
+ return -EBUSY;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->I2CcontrollerPort = smu_i2c->port;
+ req->I2CSpeed = I2C_SPEED_FAST_400K;
+ req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
+ dir = msg[0].flags & I2C_M_RD;
+
+ for (c = i = 0; i < num_msgs; i++) {
+ for (j = 0; j < msg[i].len; j++, c++) {
+ SwI2cCmd_t *cmd = &req->SwI2cCmds[c];
+
+ if (!(msg[i].flags & I2C_M_RD)) {
+ /* write */
+ cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
+ cmd->ReadWriteData = msg[i].buf[j];
+ }
+
+ if ((dir ^ msg[i].flags) & I2C_M_RD) {
+ /* The direction changes.
+ */
+ dir = msg[i].flags & I2C_M_RD;
+ cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
+ }
+
+ req->NumCmds++;
+
+ /*
+ * Insert STOP if we are at the last byte of either last
+ * message for the transaction or the client explicitly
+ * requires a STOP at this particular message.
+ */
+ if ((j == msg[i].len - 1) &&
+ ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
+ cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
+ cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
+ }
+ }
+ }
+ mutex_lock(&adev->pm.mutex);
+ r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
+ mutex_unlock(&adev->pm.mutex);
+ if (r)
+ goto fail;
+
+ for (c = i = 0; i < num_msgs; i++) {
+ if (!(msg[i].flags & I2C_M_RD)) {
+ c += msg[i].len;
+ continue;
+ }
+ for (j = 0; j < msg[i].len; j++, c++) {
+ SwI2cCmd_t *cmd = &res->SwI2cCmds[c];
+
+ msg[i].buf[j] = cmd->ReadWriteData;
+ }
+ }
+ r = num_msgs;
+fail:
+ kfree(req);
+ return r;
+}
+
+static u32 smu_v13_0_0_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm smu_v13_0_0_i2c_algo = {
+ .master_xfer = smu_v13_0_0_i2c_xfer,
+ .functionality = smu_v13_0_0_i2c_func,
+};
+
+static const struct i2c_adapter_quirks smu_v13_0_0_i2c_control_quirks = {
+ .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR | I2C_AQ_NO_ZERO_LEN,
+ .max_read_len = MAX_SW_I2C_COMMANDS,
+ .max_write_len = MAX_SW_I2C_COMMANDS,
+ .max_comb_1st_msg_len = 2,
+ .max_comb_2nd_msg_len = MAX_SW_I2C_COMMANDS - 2,
+};
+
+static int smu_v13_0_0_i2c_control_init(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int res, i;
+
+ for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
+ struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
+ struct i2c_adapter *control = &smu_i2c->adapter;
+
+ smu_i2c->adev = adev;
+ smu_i2c->port = i;
+ mutex_init(&smu_i2c->mutex);
+ control->owner = THIS_MODULE;
+ control->class = I2C_CLASS_SPD;
+ control->dev.parent = &adev->pdev->dev;
+ control->algo = &smu_v13_0_0_i2c_algo;
+ snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i);
+ control->quirks = &smu_v13_0_0_i2c_control_quirks;
+ i2c_set_adapdata(control, smu_i2c);
+
+ res = i2c_add_adapter(control);
+ if (res) {
+ DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
+ goto Out_err;
+ }
+ }
+
+ /* assign the buses used for the FRU EEPROM and RAS EEPROM */
+ /* XXX ideally this would be something in a vbios data table */
+ adev->pm.ras_eeprom_i2c_bus = &adev->pm.smu_i2c[1].adapter;
+ adev->pm.fru_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
+
+ return 0;
+Out_err:
+ for ( ; i >= 0; i--) {
+ struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
+ struct i2c_adapter *control = &smu_i2c->adapter;
+
+ i2c_del_adapter(control);
+ }
+ return res;
+}
+
+static void smu_v13_0_0_i2c_control_fini(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int i;
+
+ for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
+ struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
+ struct i2c_adapter *control = &smu_i2c->adapter;
+
+ i2c_del_adapter(control);
+ }
+ adev->pm.ras_eeprom_i2c_bus = NULL;
+ adev->pm.fru_eeprom_i2c_bus = NULL;
+}
+
+static int smu_v13_0_0_set_mp1_state(struct smu_context *smu,
+ enum pp_mp1_state mp1_state)
+{
+ int ret;
+
+ switch (mp1_state) {
+ case PP_MP1_STATE_UNLOAD:
+ ret = smu_cmn_set_mp1_state(smu, mp1_state);
+ break;
+ default:
+ /* Ignore others */
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_0_set_df_cstate(struct smu_context *smu,
+ enum pp_df_cstate state)
+{
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_DFCstateControl,
+ state,
+ NULL);
+}
+
+static int smu_v13_0_0_mode1_reset(struct smu_context *smu)
+{
+ int ret;
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 10))
+ ret = smu_cmn_send_debug_smc_msg(smu, DEBUGSMC_MSG_Mode1Reset);
+ else
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_Mode1Reset, NULL);
+
+ if (!ret)
+ msleep(SMU13_MODE1_RESET_WAIT_TIME_IN_MS);
+
+ return ret;
+}
+
+static void smu_v13_0_0_set_smu_mailbox_registers(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ smu->param_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_82);
+ smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_66);
+ smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_90);
+
+ smu->debug_param_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_53);
+ smu->debug_msg_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_75);
+ smu->debug_resp_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_54);
+}
+
+static int smu_v13_0_0_smu_send_bad_mem_page_num(struct smu_context *smu,
+ uint32_t size)
+{
+ int ret = 0;
+
+ /* message SMU to update the bad page number on SMUBUS */
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetNumBadMemoryPagesRetired,
+ size, NULL);
+ if (ret)
+ dev_err(smu->adev->dev,
+ "[%s] failed to message SMU to update bad memory pages number\n",
+ __func__);
+
+ return ret;
+}
+
+static int smu_v13_0_0_send_bad_mem_channel_flag(struct smu_context *smu,
+ uint32_t size)
+{
+ int ret = 0;
+
+ /* message SMU to update the bad channel info on SMUBUS */
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetBadMemoryPagesRetiredFlagsPerChannel,
+ size, NULL);
+ if (ret)
+ dev_err(smu->adev->dev,
+ "[%s] failed to message SMU to update bad memory pages channel info\n",
+ __func__);
+
+ return ret;
+}
+
+static const struct pptable_funcs smu_v13_0_0_ppt_funcs = {
+ .get_allowed_feature_mask = smu_v13_0_0_get_allowed_feature_mask,
+ .set_default_dpm_table = smu_v13_0_0_set_default_dpm_table,
+ .i2c_init = smu_v13_0_0_i2c_control_init,
+ .i2c_fini = smu_v13_0_0_i2c_control_fini,
+ .is_dpm_running = smu_v13_0_0_is_dpm_running,
+ .dump_pptable = smu_v13_0_0_dump_pptable,
+ .init_microcode = smu_v13_0_init_microcode,
+ .load_microcode = smu_v13_0_load_microcode,
+ .fini_microcode = smu_v13_0_fini_microcode,
+ .init_smc_tables = smu_v13_0_0_init_smc_tables,
+ .fini_smc_tables = smu_v13_0_fini_smc_tables,
+ .init_power = smu_v13_0_init_power,
+ .fini_power = smu_v13_0_fini_power,
+ .check_fw_status = smu_v13_0_check_fw_status,
+ .setup_pptable = smu_v13_0_0_setup_pptable,
+ .check_fw_version = smu_v13_0_check_fw_version,
+ .write_pptable = smu_cmn_write_pptable,
+ .set_driver_table_location = smu_v13_0_set_driver_table_location,
+ .system_features_control = smu_v13_0_0_system_features_control,
+ .set_allowed_mask = smu_v13_0_set_allowed_mask,
+ .get_enabled_mask = smu_cmn_get_enabled_mask,
+ .dpm_set_vcn_enable = smu_v13_0_set_vcn_enable,
+ .dpm_set_jpeg_enable = smu_v13_0_set_jpeg_enable,
+ .get_dpm_ultimate_freq = smu_v13_0_0_get_dpm_ultimate_freq,
+ .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values,
+ .read_sensor = smu_v13_0_0_read_sensor,
+ .feature_is_enabled = smu_cmn_feature_is_enabled,
+ .print_clk_levels = smu_v13_0_0_print_clk_levels,
+ .force_clk_levels = smu_v13_0_0_force_clk_levels,
+ .update_pcie_parameters = smu_v13_0_0_update_pcie_parameters,
+ .get_thermal_temperature_range = smu_v13_0_0_get_thermal_temperature_range,
+ .register_irq_handler = smu_v13_0_register_irq_handler,
+ .enable_thermal_alert = smu_v13_0_enable_thermal_alert,
+ .disable_thermal_alert = smu_v13_0_disable_thermal_alert,
+ .notify_memory_pool_location = smu_v13_0_notify_memory_pool_location,
+ .get_gpu_metrics = smu_v13_0_0_get_gpu_metrics,
+ .set_soft_freq_limited_range = smu_v13_0_set_soft_freq_limited_range,
+ .init_pptable_microcode = smu_v13_0_init_pptable_microcode,
+ .populate_umd_state_clk = smu_v13_0_0_populate_umd_state_clk,
+ .set_performance_level = smu_v13_0_set_performance_level,
+ .gfx_off_control = smu_v13_0_gfx_off_control,
+ .get_unique_id = smu_v13_0_0_get_unique_id,
+ .get_fan_speed_pwm = smu_v13_0_0_get_fan_speed_pwm,
+ .get_fan_speed_rpm = smu_v13_0_0_get_fan_speed_rpm,
+ .set_fan_speed_pwm = smu_v13_0_set_fan_speed_pwm,
+ .set_fan_speed_rpm = smu_v13_0_set_fan_speed_rpm,
+ .get_fan_control_mode = smu_v13_0_get_fan_control_mode,
+ .set_fan_control_mode = smu_v13_0_set_fan_control_mode,
+ .enable_mgpu_fan_boost = smu_v13_0_0_enable_mgpu_fan_boost,
+ .get_power_limit = smu_v13_0_0_get_power_limit,
+ .set_power_limit = smu_v13_0_set_power_limit,
+ .set_power_source = smu_v13_0_set_power_source,
+ .get_power_profile_mode = smu_v13_0_0_get_power_profile_mode,
+ .set_power_profile_mode = smu_v13_0_0_set_power_profile_mode,
+ .run_btc = smu_v13_0_run_btc,
+ .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
+ .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
+ .set_tool_table_location = smu_v13_0_set_tool_table_location,
+ .deep_sleep_control = smu_v13_0_deep_sleep_control,
+ .gfx_ulv_control = smu_v13_0_gfx_ulv_control,
+ .baco_is_support = smu_v13_0_baco_is_support,
+ .baco_get_state = smu_v13_0_baco_get_state,
+ .baco_set_state = smu_v13_0_baco_set_state,
+ .baco_enter = smu_v13_0_0_baco_enter,
+ .baco_exit = smu_v13_0_0_baco_exit,
+ .mode1_reset_is_support = smu_v13_0_0_is_mode1_reset_supported,
+ .mode1_reset = smu_v13_0_0_mode1_reset,
+ .set_mp1_state = smu_v13_0_0_set_mp1_state,
+ .set_df_cstate = smu_v13_0_0_set_df_cstate,
+ .send_hbm_bad_pages_num = smu_v13_0_0_smu_send_bad_mem_page_num,
+ .send_hbm_bad_channel_flag = smu_v13_0_0_send_bad_mem_channel_flag,
+ .gpo_control = smu_v13_0_gpo_control,
+};
+
+void smu_v13_0_0_set_ppt_funcs(struct smu_context *smu)
+{
+ smu->ppt_funcs = &smu_v13_0_0_ppt_funcs;
+ smu->message_map = smu_v13_0_0_message_map;
+ smu->clock_map = smu_v13_0_0_clk_map;
+ smu->feature_map = smu_v13_0_0_feature_mask_map;
+ smu->table_map = smu_v13_0_0_table_map;
+ smu->pwr_src_map = smu_v13_0_0_pwr_src_map;
+ smu->workload_map = smu_v13_0_0_workload_map;
+ smu_v13_0_0_set_smu_mailbox_registers(smu);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.h
new file mode 100644
index 000000000..ede494856
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SMU_V13_0_0_PPT_H__
+#define __SMU_V13_0_0_PPT_H__
+
+extern void smu_v13_0_0_set_ppt_funcs(struct smu_context *smu);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
new file mode 100644
index 000000000..8fa9a36c3
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
@@ -0,0 +1,1052 @@
+/*
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "smu_types.h"
+#define SWSMU_CODE_LAYER_L2
+
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "smu_v13_0.h"
+#include "smu13_driver_if_v13_0_4.h"
+#include "smu_v13_0_4_ppt.h"
+#include "smu_v13_0_4_ppsmc.h"
+#include "smu_v13_0_4_pmfw.h"
+#include "smu_cmn.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+#define mmMP1_SMN_C2PMSG_66 0x0282
+#define mmMP1_SMN_C2PMSG_66_BASE_IDX 1
+
+#define mmMP1_SMN_C2PMSG_82 0x0292
+#define mmMP1_SMN_C2PMSG_82_BASE_IDX 1
+
+#define mmMP1_SMN_C2PMSG_90 0x029a
+#define mmMP1_SMN_C2PMSG_90_BASE_IDX 1
+
+#define FEATURE_MASK(feature) (1ULL << feature)
+
+#define SMC_DPM_FEATURE ( \
+ FEATURE_MASK(FEATURE_CCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_VCN_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_FCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_SOCCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_MP0CLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_LCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_SHUBCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_DCFCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_ISP_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_IPU_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_GFX_DPM_BIT))
+
+static struct cmn2asic_msg_mapping smu_v13_0_4_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
+ MSG_MAP(GetSmuVersion, PPSMC_MSG_GetPmfwVersion, 1),
+ MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
+ MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 1),
+ MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 1),
+ MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 1),
+ MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 1),
+ MSG_MAP(SetHardMinVcn, PPSMC_MSG_SetHardMinVcn, 1),
+ MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 1),
+ MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
+ MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
+ MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
+ MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 1),
+ MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDeviceDriverReset, 1),
+ MSG_MAP(GetEnabledSmuFeatures, PPSMC_MSG_GetEnabledSmuFeatures, 1),
+ MSG_MAP(SetHardMinSocclkByFreq, PPSMC_MSG_SetHardMinSocclkByFreq, 1),
+ MSG_MAP(SetSoftMinVcn, PPSMC_MSG_SetSoftMinVcn, 1),
+ MSG_MAP(GetGfxclkFrequency, PPSMC_MSG_GetGfxclkFrequency, 1),
+ MSG_MAP(GetFclkFrequency, PPSMC_MSG_GetFclkFrequency, 1),
+ MSG_MAP(SetSoftMaxGfxClk, PPSMC_MSG_SetSoftMaxGfxClk, 1),
+ MSG_MAP(SetHardMinGfxClk, PPSMC_MSG_SetHardMinGfxClk, 1),
+ MSG_MAP(SetSoftMaxSocclkByFreq, PPSMC_MSG_SetSoftMaxSocclkByFreq, 1),
+ MSG_MAP(SetSoftMaxFclkByFreq, PPSMC_MSG_SetSoftMaxFclkByFreq, 1),
+ MSG_MAP(SetSoftMaxVcn, PPSMC_MSG_SetSoftMaxVcn, 1),
+ MSG_MAP(SetPowerLimitPercentage, PPSMC_MSG_SetPowerLimitPercentage, 1),
+ MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 1),
+ MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 1),
+ MSG_MAP(SetHardMinFclkByFreq, PPSMC_MSG_SetHardMinFclkByFreq, 1),
+ MSG_MAP(SetSoftMinSocclkByFreq, PPSMC_MSG_SetSoftMinSocclkByFreq, 1),
+ MSG_MAP(EnableGfxImu, PPSMC_MSG_EnableGfxImu, 1),
+ MSG_MAP(PowerUpIspByTile, PPSMC_MSG_PowerUpIspByTile, 1),
+ MSG_MAP(PowerDownIspByTile, PPSMC_MSG_PowerDownIspByTile, 1),
+};
+
+static struct cmn2asic_mapping smu_v13_0_4_feature_mask_map[SMU_FEATURE_COUNT] = {
+ FEA_MAP(CCLK_DPM),
+ FEA_MAP(FAN_CONTROLLER),
+ FEA_MAP(PPT),
+ FEA_MAP(TDC),
+ FEA_MAP(THERMAL),
+ FEA_MAP(VCN_DPM),
+ FEA_MAP_REVERSE(FCLK),
+ FEA_MAP_REVERSE(SOCCLK),
+ FEA_MAP(LCLK_DPM),
+ FEA_MAP(SHUBCLK_DPM),
+ FEA_MAP(DCFCLK_DPM),
+ FEA_MAP_HALF_REVERSE(GFX),
+ FEA_MAP(DS_GFXCLK),
+ FEA_MAP(DS_SOCCLK),
+ FEA_MAP(DS_LCLK),
+ FEA_MAP(DS_DCFCLK),
+ FEA_MAP(DS_FCLK),
+ FEA_MAP(DS_MP1CLK),
+ FEA_MAP(DS_MP0CLK),
+ FEA_MAP(GFX_DEM),
+ FEA_MAP(PSI),
+ FEA_MAP(PROCHOT),
+ FEA_MAP(CPUOFF),
+ FEA_MAP(STAPM),
+ FEA_MAP(S0I3),
+ FEA_MAP(PERF_LIMIT),
+ FEA_MAP(CORE_DLDO),
+ FEA_MAP(DS_VCN),
+ FEA_MAP(CPPC),
+ FEA_MAP(DF_CSTATES),
+ FEA_MAP(ATHUB_PG),
+};
+
+static struct cmn2asic_mapping smu_v13_0_4_table_map[SMU_TABLE_COUNT] = {
+ TAB_MAP_VALID(WATERMARKS),
+ TAB_MAP_VALID(SMU_METRICS),
+ TAB_MAP_VALID(CUSTOM_DPM),
+ TAB_MAP_VALID(DPMCLOCKS),
+};
+
+static int smu_v13_0_4_init_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ smu_table->clocks_table = kzalloc(sizeof(DpmClocks_t), GFP_KERNEL);
+ if (!smu_table->clocks_table)
+ goto err0_out;
+
+ smu_table->metrics_table = kzalloc(sizeof(SmuMetrics_t), GFP_KERNEL);
+ if (!smu_table->metrics_table)
+ goto err1_out;
+ smu_table->metrics_time = 0;
+
+ smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
+ if (!smu_table->watermarks_table)
+ goto err2_out;
+
+ smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v2_1);
+ smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
+ if (!smu_table->gpu_metrics_table)
+ goto err3_out;
+
+ return 0;
+
+err3_out:
+ kfree(smu_table->watermarks_table);
+err2_out:
+ kfree(smu_table->metrics_table);
+err1_out:
+ kfree(smu_table->clocks_table);
+err0_out:
+ return -ENOMEM;
+}
+
+static int smu_v13_0_4_fini_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ kfree(smu_table->clocks_table);
+ smu_table->clocks_table = NULL;
+
+ kfree(smu_table->metrics_table);
+ smu_table->metrics_table = NULL;
+
+ kfree(smu_table->watermarks_table);
+ smu_table->watermarks_table = NULL;
+
+ kfree(smu_table->gpu_metrics_table);
+ smu_table->gpu_metrics_table = NULL;
+
+ return 0;
+}
+
+static bool smu_v13_0_4_is_dpm_running(struct smu_context *smu)
+{
+ int ret = 0;
+ uint64_t feature_enabled;
+
+ ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
+
+ if (ret)
+ return false;
+
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static int smu_v13_0_4_system_features_control(struct smu_context *smu, bool en)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ if (!en && !adev->in_s0ix)
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL);
+
+ return ret;
+}
+
+static ssize_t smu_v13_0_4_get_gpu_metrics(struct smu_context *smu,
+ void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct gpu_metrics_v2_1 *gpu_metrics =
+ (struct gpu_metrics_v2_1 *)smu_table->gpu_metrics_table;
+ SmuMetrics_t metrics;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu, &metrics, true);
+ if (ret)
+ return ret;
+
+ smu_cmn_init_soft_gpu_metrics(gpu_metrics, 2, 1);
+
+ gpu_metrics->temperature_gfx = metrics.GfxTemperature;
+ gpu_metrics->temperature_soc = metrics.SocTemperature;
+ memcpy(&gpu_metrics->temperature_core[0],
+ &metrics.CoreTemperature[0],
+ sizeof(uint16_t) * 8);
+ gpu_metrics->temperature_l3[0] = metrics.L3Temperature;
+
+ gpu_metrics->average_gfx_activity = metrics.GfxActivity;
+ gpu_metrics->average_mm_activity = metrics.UvdActivity;
+
+ gpu_metrics->average_socket_power = metrics.CurrentSocketPower;
+ gpu_metrics->average_gfx_power = metrics.Power[0];
+ gpu_metrics->average_soc_power = metrics.Power[1];
+ memcpy(&gpu_metrics->average_core_power[0],
+ &metrics.CorePower[0],
+ sizeof(uint16_t) * 8);
+
+ gpu_metrics->average_gfxclk_frequency = metrics.GfxclkFrequency;
+ gpu_metrics->average_socclk_frequency = metrics.SocclkFrequency;
+ gpu_metrics->average_uclk_frequency = metrics.MemclkFrequency;
+ gpu_metrics->average_fclk_frequency = metrics.MemclkFrequency;
+ gpu_metrics->average_vclk_frequency = metrics.VclkFrequency;
+ gpu_metrics->average_dclk_frequency = metrics.DclkFrequency;
+
+ memcpy(&gpu_metrics->current_coreclk[0],
+ &metrics.CoreFrequency[0],
+ sizeof(uint16_t) * 8);
+ gpu_metrics->current_l3clk[0] = metrics.L3Frequency;
+
+ gpu_metrics->throttle_status = metrics.ThrottlerStatus;
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v2_1);
+}
+
+static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu,
+ MetricsMember_t member,
+ uint32_t *value)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ SmuMetrics_t *metrics = (SmuMetrics_t *)smu_table->metrics_table;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu, NULL, false);
+ if (ret)
+ return ret;
+
+ switch (member) {
+ case METRICS_AVERAGE_GFXCLK:
+ *value = metrics->GfxclkFrequency;
+ break;
+ case METRICS_AVERAGE_SOCCLK:
+ *value = metrics->SocclkFrequency;
+ break;
+ case METRICS_AVERAGE_VCLK:
+ *value = metrics->VclkFrequency;
+ break;
+ case METRICS_AVERAGE_DCLK:
+ *value = metrics->DclkFrequency;
+ break;
+ case METRICS_AVERAGE_UCLK:
+ *value = metrics->MemclkFrequency;
+ break;
+ case METRICS_AVERAGE_GFXACTIVITY:
+ *value = metrics->GfxActivity / 100;
+ break;
+ case METRICS_AVERAGE_VCNACTIVITY:
+ *value = metrics->UvdActivity;
+ break;
+ case METRICS_AVERAGE_SOCKETPOWER:
+ *value = (metrics->CurrentSocketPower << 8) / 1000;
+ break;
+ case METRICS_TEMPERATURE_EDGE:
+ *value = metrics->GfxTemperature / 100 *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_HOTSPOT:
+ *value = metrics->SocTemperature / 100 *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_THROTTLER_STATUS:
+ *value = metrics->ThrottlerStatus;
+ break;
+ case METRICS_VOLTAGE_VDDGFX:
+ *value = metrics->Voltage[0];
+ break;
+ case METRICS_VOLTAGE_VDDSOC:
+ *value = metrics->Voltage[1];
+ break;
+ case METRICS_SS_APU_SHARE:
+ /* return the percentage of APU power with respect to APU's power limit.
+ * percentage is reported, this isn't boost value. Smartshift power
+ * boost/shift is only when the percentage is more than 100.
+ */
+ if (metrics->StapmOpnLimit > 0)
+ *value = (metrics->ApuPower * 100) / metrics->StapmOpnLimit;
+ else
+ *value = 0;
+ break;
+ case METRICS_SS_DGPU_SHARE:
+ /* return the percentage of dGPU power with respect to dGPU's power limit.
+ * percentage is reported, this isn't boost value. Smartshift power
+ * boost/shift is only when the percentage is more than 100.
+ */
+ if ((metrics->dGpuPower > 0) &&
+ (metrics->StapmCurrentLimit > metrics->StapmOpnLimit))
+ *value = (metrics->dGpuPower * 100) /
+ (metrics->StapmCurrentLimit - metrics->StapmOpnLimit);
+ else
+ *value = 0;
+ break;
+ default:
+ *value = UINT_MAX;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_4_get_current_clk_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ MetricsMember_t member_type;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ member_type = METRICS_AVERAGE_SOCCLK;
+ break;
+ case SMU_VCLK:
+ member_type = METRICS_AVERAGE_VCLK;
+ break;
+ case SMU_DCLK:
+ member_type = METRICS_AVERAGE_DCLK;
+ break;
+ case SMU_MCLK:
+ member_type = METRICS_AVERAGE_UCLK;
+ break;
+ case SMU_FCLK:
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetFclkFrequency,
+ 0, value);
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetGfxclkFrequency,
+ 0, value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return smu_v13_0_4_get_smu_metrics_data(smu, member_type, value);
+}
+
+static int smu_v13_0_4_get_dpm_freq_by_index(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t dpm_level,
+ uint32_t *freq)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ if (!clk_table || clk_type >= SMU_CLK_COUNT)
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ if (dpm_level >= clk_table->NumSocClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->SocClocks[dpm_level];
+ break;
+ case SMU_VCLK:
+ if (dpm_level >= clk_table->VcnClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->VClocks[dpm_level];
+ break;
+ case SMU_DCLK:
+ if (dpm_level >= clk_table->VcnClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->DClocks[dpm_level];
+ break;
+ case SMU_UCLK:
+ case SMU_MCLK:
+ if (dpm_level >= clk_table->NumDfPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->DfPstateTable[dpm_level].MemClk;
+ break;
+ case SMU_FCLK:
+ if (dpm_level >= clk_table->NumDfPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->DfPstateTable[dpm_level].FClk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smu_v13_0_4_get_dpm_level_count(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *count)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ *count = clk_table->NumSocClkLevelsEnabled;
+ break;
+ case SMU_VCLK:
+ *count = clk_table->VcnClkLevelsEnabled;
+ break;
+ case SMU_DCLK:
+ *count = clk_table->VcnClkLevelsEnabled;
+ break;
+ case SMU_MCLK:
+ *count = clk_table->NumDfPstatesEnabled;
+ break;
+ case SMU_FCLK:
+ *count = clk_table->NumDfPstatesEnabled;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int smu_v13_0_4_print_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type, char *buf)
+{
+ int i, size = 0, ret = 0;
+ uint32_t cur_value = 0, value = 0, count = 0;
+ uint32_t min, max;
+
+ smu_cmn_get_sysfs_buf(&buf, &size);
+
+ switch (clk_type) {
+ case SMU_OD_SCLK:
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_SCLK");
+ size += sysfs_emit_at(buf, size, "0: %10uMhz\n",
+ (smu->gfx_actual_hard_min_freq > 0) ? smu->gfx_actual_hard_min_freq : smu->gfx_default_hard_min_freq);
+ size += sysfs_emit_at(buf, size, "1: %10uMhz\n",
+ (smu->gfx_actual_soft_max_freq > 0) ? smu->gfx_actual_soft_max_freq : smu->gfx_default_soft_max_freq);
+ break;
+ case SMU_OD_RANGE:
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
+ size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
+ smu->gfx_default_hard_min_freq,
+ smu->gfx_default_soft_max_freq);
+ break;
+ case SMU_SOCCLK:
+ case SMU_VCLK:
+ case SMU_DCLK:
+ case SMU_MCLK:
+ case SMU_FCLK:
+ ret = smu_v13_0_4_get_current_clk_freq(smu, clk_type, &cur_value);
+ if (ret)
+ break;
+
+ ret = smu_v13_0_4_get_dpm_level_count(smu, clk_type, &count);
+ if (ret)
+ break;
+
+ for (i = 0; i < count; i++) {
+ ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type, i, &value);
+ if (ret)
+ break;
+
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, value,
+ cur_value == value ? "*" : "");
+ }
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ ret = smu_v13_0_4_get_current_clk_freq(smu, clk_type, &cur_value);
+ if (ret)
+ break;
+ min = (smu->gfx_actual_hard_min_freq > 0) ? smu->gfx_actual_hard_min_freq : smu->gfx_default_hard_min_freq;
+ max = (smu->gfx_actual_soft_max_freq > 0) ? smu->gfx_actual_soft_max_freq : smu->gfx_default_soft_max_freq;
+ if (cur_value == max)
+ i = 2;
+ else if (cur_value == min)
+ i = 0;
+ else
+ i = 1;
+ size += sysfs_emit_at(buf, size, "0: %uMhz %s\n", min,
+ i == 0 ? "*" : "");
+ size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
+ i == 1 ? cur_value : 1100, /* UMD PSTATE GFXCLK 1100 */
+ i == 1 ? "*" : "");
+ size += sysfs_emit_at(buf, size, "2: %uMhz %s\n", max,
+ i == 2 ? "*" : "");
+ break;
+ default:
+ break;
+ }
+
+ return size;
+}
+
+static int smu_v13_0_4_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data, uint32_t *size)
+{
+ int ret = 0;
+
+ if (!data || !size)
+ return -EINVAL;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_SOCKETPOWER,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_EDGE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_HOTSPOT,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_UCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDGFX,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDNB:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDSOC,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_SS_APU_SHARE:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_SS_APU_SHARE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_SS_DGPU_SHARE:
+ ret = smu_v13_0_4_get_smu_metrics_data(smu,
+ METRICS_SS_DGPU_SHARE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_4_set_watermarks_table(struct smu_context *smu,
+ struct pp_smu_wm_range_sets *clock_ranges)
+{
+ int i;
+ int ret = 0;
+ Watermarks_t *table = smu->smu_table.watermarks_table;
+
+ if (!table || !clock_ranges)
+ return -EINVAL;
+
+ if (clock_ranges->num_reader_wm_sets > NUM_WM_RANGES ||
+ clock_ranges->num_writer_wm_sets > NUM_WM_RANGES)
+ return -EINVAL;
+
+ for (i = 0; i < clock_ranges->num_reader_wm_sets; i++) {
+ table->WatermarkRow[WM_DCFCLK][i].MinClock =
+ clock_ranges->reader_wm_sets[i].min_drain_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MaxClock =
+ clock_ranges->reader_wm_sets[i].max_drain_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MinMclk =
+ clock_ranges->reader_wm_sets[i].min_fill_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MaxMclk =
+ clock_ranges->reader_wm_sets[i].max_fill_clk_mhz;
+
+ table->WatermarkRow[WM_DCFCLK][i].WmSetting =
+ clock_ranges->reader_wm_sets[i].wm_inst;
+ }
+
+ for (i = 0; i < clock_ranges->num_writer_wm_sets; i++) {
+ table->WatermarkRow[WM_SOCCLK][i].MinClock =
+ clock_ranges->writer_wm_sets[i].min_fill_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MaxClock =
+ clock_ranges->writer_wm_sets[i].max_fill_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MinMclk =
+ clock_ranges->writer_wm_sets[i].min_drain_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MaxMclk =
+ clock_ranges->writer_wm_sets[i].max_drain_clk_mhz;
+
+ table->WatermarkRow[WM_SOCCLK][i].WmSetting =
+ clock_ranges->writer_wm_sets[i].wm_inst;
+ }
+
+ smu->watermarks_bitmap |= WATERMARKS_EXIST;
+
+ /* pass data to smu controller */
+ if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
+ !(smu->watermarks_bitmap & WATERMARKS_LOADED)) {
+ ret = smu_cmn_write_watermarks_table(smu);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to update WMTABLE!");
+ return ret;
+ }
+ smu->watermarks_bitmap |= WATERMARKS_LOADED;
+ }
+
+ return 0;
+}
+
+static bool smu_v13_0_4_clk_dpm_is_enabled(struct smu_context *smu,
+ enum smu_clk_type clk_type)
+{
+ enum smu_feature_mask feature_id = 0;
+
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ feature_id = SMU_FEATURE_DPM_FCLK_BIT;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ feature_id = SMU_FEATURE_DPM_GFXCLK_BIT;
+ break;
+ case SMU_SOCCLK:
+ feature_id = SMU_FEATURE_DPM_SOCCLK_BIT;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ feature_id = SMU_FEATURE_VCN_DPM_BIT;
+ break;
+ default:
+ return true;
+ }
+
+ return smu_cmn_feature_is_enabled(smu, feature_id);
+}
+
+static int smu_v13_0_4_get_dpm_ultimate_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *min,
+ uint32_t *max)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+ uint32_t clock_limit;
+ uint32_t max_dpm_level, min_dpm_level;
+ int ret = 0;
+
+ if (!smu_v13_0_4_clk_dpm_is_enabled(smu, clk_type)) {
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ clock_limit = smu->smu_table.boot_values.uclk;
+ break;
+ case SMU_FCLK:
+ clock_limit = smu->smu_table.boot_values.fclk;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ clock_limit = smu->smu_table.boot_values.gfxclk;
+ break;
+ case SMU_SOCCLK:
+ clock_limit = smu->smu_table.boot_values.socclk;
+ break;
+ case SMU_VCLK:
+ clock_limit = smu->smu_table.boot_values.vclk;
+ break;
+ case SMU_DCLK:
+ clock_limit = smu->smu_table.boot_values.dclk;
+ break;
+ default:
+ clock_limit = 0;
+ break;
+ }
+
+ /* clock in Mhz unit */
+ if (min)
+ *min = clock_limit / 100;
+ if (max)
+ *max = clock_limit / 100;
+
+ return 0;
+ }
+
+ if (max) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *max = clk_table->MaxGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ max_dpm_level = 0;
+ break;
+ case SMU_SOCCLK:
+ max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ max_dpm_level = clk_table->VcnClkLevelsEnabled - 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type,
+ max_dpm_level,
+ max);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (min) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *min = clk_table->MinGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ min_dpm_level = clk_table->NumDfPstatesEnabled - 1;
+ break;
+ case SMU_SOCCLK:
+ min_dpm_level = 0;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ min_dpm_level = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type,
+ min_dpm_level,
+ min);
+ }
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_4_set_soft_freq_limited_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t min,
+ uint32_t max)
+{
+ enum smu_message_type msg_set_min, msg_set_max;
+ int ret = 0;
+
+ if (!smu_v13_0_4_clk_dpm_is_enabled(smu, clk_type))
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ msg_set_min = SMU_MSG_SetHardMinGfxClk;
+ msg_set_max = SMU_MSG_SetSoftMaxGfxClk;
+ break;
+ case SMU_FCLK:
+ msg_set_min = SMU_MSG_SetHardMinFclkByFreq;
+ msg_set_max = SMU_MSG_SetSoftMaxFclkByFreq;
+ break;
+ case SMU_SOCCLK:
+ msg_set_min = SMU_MSG_SetHardMinSocclkByFreq;
+ msg_set_max = SMU_MSG_SetSoftMaxSocclkByFreq;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ msg_set_min = SMU_MSG_SetHardMinVcn;
+ msg_set_max = SMU_MSG_SetSoftMaxVcn;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, msg_set_min, min, NULL);
+ if (ret)
+ return ret;
+
+ return smu_cmn_send_smc_msg_with_param(smu, msg_set_max,
+ max, NULL);
+}
+
+static int smu_v13_0_4_force_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t mask)
+{
+ uint32_t soft_min_level = 0, soft_max_level = 0;
+ uint32_t min_freq = 0, max_freq = 0;
+ int ret = 0;
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ case SMU_VCLK:
+ case SMU_DCLK:
+ ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type, soft_min_level, &min_freq);
+ if (ret)
+ break;
+
+ ret = smu_v13_0_4_get_dpm_freq_by_index(smu, clk_type, soft_max_level, &max_freq);
+ if (ret)
+ break;
+
+ ret = smu_v13_0_4_set_soft_freq_limited_range(smu, clk_type, min_freq, max_freq);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_4_set_performance_level(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t sclk_min = 0, sclk_max = 0;
+ uint32_t fclk_min = 0, fclk_max = 0;
+ uint32_t socclk_min = 0, socclk_max = 0;
+ int ret = 0;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_SCLK, NULL, &sclk_max);
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_FCLK, NULL, &fclk_max);
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_SOCCLK, NULL, &socclk_max);
+ sclk_min = sclk_max;
+ fclk_min = fclk_max;
+ socclk_min = socclk_max;
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_SCLK, &sclk_min, NULL);
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_FCLK, &fclk_min, NULL);
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_SOCCLK, &socclk_min, NULL);
+ sclk_max = sclk_min;
+ fclk_max = fclk_min;
+ socclk_max = socclk_min;
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_SCLK, &sclk_min, &sclk_max);
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_FCLK, &fclk_min, &fclk_max);
+ smu_v13_0_4_get_dpm_ultimate_freq(smu, SMU_SOCCLK, &socclk_min, &socclk_max);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ /* Temporarily do nothing since the optimal clocks haven't been provided yet */
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ return 0;
+ default:
+ dev_err(adev->dev, "Invalid performance level %d\n", level);
+ return -EINVAL;
+ }
+
+ if (sclk_min && sclk_max) {
+ ret = smu_v13_0_4_set_soft_freq_limited_range(smu,
+ SMU_SCLK,
+ sclk_min,
+ sclk_max);
+ if (ret)
+ return ret;
+
+ smu->gfx_actual_hard_min_freq = sclk_min;
+ smu->gfx_actual_soft_max_freq = sclk_max;
+ }
+
+ if (fclk_min && fclk_max) {
+ ret = smu_v13_0_4_set_soft_freq_limited_range(smu,
+ SMU_FCLK,
+ fclk_min,
+ fclk_max);
+ if (ret)
+ return ret;
+ }
+
+ if (socclk_min && socclk_max) {
+ ret = smu_v13_0_4_set_soft_freq_limited_range(smu,
+ SMU_SOCCLK,
+ socclk_min,
+ socclk_max);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_4_mode2_reset(struct smu_context *smu)
+{
+ return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset,
+ SMU_RESET_MODE_2, NULL);
+}
+
+static int smu_v13_0_4_set_fine_grain_gfx_freq_parameters(struct smu_context *smu)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ smu->gfx_default_hard_min_freq = clk_table->MinGfxClk;
+ smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk;
+ smu->gfx_actual_hard_min_freq = 0;
+ smu->gfx_actual_soft_max_freq = 0;
+
+ return 0;
+}
+
+static const struct pptable_funcs smu_v13_0_4_ppt_funcs = {
+ .check_fw_status = smu_v13_0_check_fw_status,
+ .check_fw_version = smu_v13_0_check_fw_version,
+ .init_smc_tables = smu_v13_0_4_init_smc_tables,
+ .fini_smc_tables = smu_v13_0_4_fini_smc_tables,
+ .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values,
+ .system_features_control = smu_v13_0_4_system_features_control,
+ .send_smc_msg_with_param = smu_cmn_send_smc_msg_with_param,
+ .send_smc_msg = smu_cmn_send_smc_msg,
+ .dpm_set_vcn_enable = smu_v13_0_set_vcn_enable,
+ .dpm_set_jpeg_enable = smu_v13_0_set_jpeg_enable,
+ .set_default_dpm_table = smu_v13_0_set_default_dpm_tables,
+ .read_sensor = smu_v13_0_4_read_sensor,
+ .is_dpm_running = smu_v13_0_4_is_dpm_running,
+ .set_watermarks_table = smu_v13_0_4_set_watermarks_table,
+ .get_gpu_metrics = smu_v13_0_4_get_gpu_metrics,
+ .get_enabled_mask = smu_cmn_get_enabled_mask,
+ .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
+ .set_driver_table_location = smu_v13_0_set_driver_table_location,
+ .gfx_off_control = smu_v13_0_gfx_off_control,
+ .mode2_reset = smu_v13_0_4_mode2_reset,
+ .get_dpm_ultimate_freq = smu_v13_0_4_get_dpm_ultimate_freq,
+ .od_edit_dpm_table = smu_v13_0_od_edit_dpm_table,
+ .print_clk_levels = smu_v13_0_4_print_clk_levels,
+ .force_clk_levels = smu_v13_0_4_force_clk_levels,
+ .set_performance_level = smu_v13_0_4_set_performance_level,
+ .set_fine_grain_gfx_freq_parameters = smu_v13_0_4_set_fine_grain_gfx_freq_parameters,
+ .set_gfx_power_up_by_imu = smu_v13_0_set_gfx_power_up_by_imu,
+};
+
+static void smu_v13_0_4_set_smu_mailbox_registers(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ smu->param_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_82);
+ smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_66);
+ smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_SMN_C2PMSG_90);
+}
+
+void smu_v13_0_4_set_ppt_funcs(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ smu->ppt_funcs = &smu_v13_0_4_ppt_funcs;
+ smu->message_map = smu_v13_0_4_message_map;
+ smu->feature_map = smu_v13_0_4_feature_mask_map;
+ smu->table_map = smu_v13_0_4_table_map;
+ smu->is_apu = true;
+
+ if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 4))
+ smu_v13_0_4_set_smu_mailbox_registers(smu);
+ else
+ smu_v13_0_set_smu_mailbox_registers(smu);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.h
new file mode 100644
index 000000000..f0bd5d3ac
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SMU_V13_0_4_PPT_H__
+#define __SMU_V13_0_4_PPT_H__
+
+extern void smu_v13_0_4_set_ppt_funcs(struct smu_context *smu);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
new file mode 100644
index 000000000..66445964e
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define SWSMU_CODE_LAYER_L2
+
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "smu_v13_0.h"
+#include "smu13_driver_if_v13_0_5.h"
+#include "smu_v13_0_5_ppt.h"
+#include "smu_v13_0_5_ppsmc.h"
+#include "smu_v13_0_5_pmfw.h"
+#include "smu_cmn.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+#define mmMP1_C2PMSG_2 (0xbee142 + 0xb00000 / 4)
+#define mmMP1_C2PMSG_2_BASE_IDX 0
+
+#define mmMP1_C2PMSG_34 (0xbee262 + 0xb00000 / 4)
+#define mmMP1_C2PMSG_34_BASE_IDX 0
+
+#define mmMP1_C2PMSG_33 (0xbee261 + 0xb00000 / 4)
+#define mmMP1_C2PMSG_33_BASE_IDX 0
+
+#define FEATURE_MASK(feature) (1ULL << feature)
+#define SMC_DPM_FEATURE ( \
+ FEATURE_MASK(FEATURE_CCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_FCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_LCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_GFX_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_VCN_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_DCFCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_SOCCLK_DPM_BIT)| \
+ FEATURE_MASK(FEATURE_MP0CLK_DPM_BIT)| \
+ FEATURE_MASK(FEATURE_SHUBCLK_DPM_BIT))
+
+static struct cmn2asic_msg_mapping smu_v13_0_5_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
+ MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
+ MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 1),
+ MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 1),
+ MSG_MAP(SetHardMinVcn, PPSMC_MSG_SetHardMinVcn, 1),
+ MSG_MAP(SetSoftMinGfxclk, PPSMC_MSG_SetSoftMinGfxclk, 1),
+ MSG_MAP(Spare0, PPSMC_MSG_Spare0, 1),
+ MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDeviceDriverReset, 1),
+ MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
+ MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
+ MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
+ MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu , 1),
+ MSG_MAP(GetGfxclkFrequency, PPSMC_MSG_GetGfxclkFrequency, 1),
+ MSG_MAP(GetEnabledSmuFeatures, PPSMC_MSG_GetEnabledSmuFeatures, 1),
+ MSG_MAP(SetSoftMaxVcn, PPSMC_MSG_SetSoftMaxVcn, 1),
+ MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 1),
+ MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 1),
+ MSG_MAP(SetSoftMaxGfxClk, PPSMC_MSG_SetSoftMaxGfxClk, 1),
+ MSG_MAP(SetHardMinGfxClk, PPSMC_MSG_SetHardMinGfxClk, 1),
+ MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 1),
+ MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 1),
+ MSG_MAP(SetSoftMinVcn, PPSMC_MSG_SetSoftMinVcn, 1),
+ MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
+ MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 1),
+};
+
+static struct cmn2asic_mapping smu_v13_0_5_feature_mask_map[SMU_FEATURE_COUNT] = {
+ FEA_MAP(DATA_CALCULATION),
+ FEA_MAP(PPT),
+ FEA_MAP(TDC),
+ FEA_MAP(THERMAL),
+ FEA_MAP(PROCHOT),
+ FEA_MAP(CCLK_DPM),
+ FEA_MAP_REVERSE(FCLK),
+ FEA_MAP(LCLK_DPM),
+ FEA_MAP(DF_CSTATES),
+ FEA_MAP(FAN_CONTROLLER),
+ FEA_MAP(CPPC),
+ FEA_MAP_HALF_REVERSE(GFX),
+ FEA_MAP(DS_GFXCLK),
+ FEA_MAP(S0I3),
+ FEA_MAP(VCN_DPM),
+ FEA_MAP(DS_VCN),
+ FEA_MAP(DCFCLK_DPM),
+ FEA_MAP(ATHUB_PG),
+ FEA_MAP_REVERSE(SOCCLK),
+ FEA_MAP(SHUBCLK_DPM),
+ FEA_MAP(GFXOFF),
+};
+
+static struct cmn2asic_mapping smu_v13_0_5_table_map[SMU_TABLE_COUNT] = {
+ TAB_MAP_VALID(WATERMARKS),
+ TAB_MAP_VALID(SMU_METRICS),
+ TAB_MAP_VALID(CUSTOM_DPM),
+ TAB_MAP_VALID(DPMCLOCKS),
+};
+
+static int smu_v13_0_5_init_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ smu_table->clocks_table = kzalloc(sizeof(DpmClocks_t), GFP_KERNEL);
+ if (!smu_table->clocks_table)
+ goto err0_out;
+
+ smu_table->metrics_table = kzalloc(sizeof(SmuMetrics_t), GFP_KERNEL);
+ if (!smu_table->metrics_table)
+ goto err1_out;
+ smu_table->metrics_time = 0;
+
+ smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
+ if (!smu_table->watermarks_table)
+ goto err2_out;
+
+ smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v2_1);
+ smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
+ if (!smu_table->gpu_metrics_table)
+ goto err3_out;
+
+ return 0;
+
+err3_out:
+ kfree(smu_table->watermarks_table);
+err2_out:
+ kfree(smu_table->metrics_table);
+err1_out:
+ kfree(smu_table->clocks_table);
+err0_out:
+ return -ENOMEM;
+}
+
+static int smu_v13_0_5_fini_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ kfree(smu_table->clocks_table);
+ smu_table->clocks_table = NULL;
+
+ kfree(smu_table->metrics_table);
+ smu_table->metrics_table = NULL;
+
+ kfree(smu_table->watermarks_table);
+ smu_table->watermarks_table = NULL;
+
+ kfree(smu_table->gpu_metrics_table);
+ smu_table->gpu_metrics_table = NULL;
+
+ return 0;
+}
+
+static int smu_v13_0_5_system_features_control(struct smu_context *smu, bool en)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ if (!en && !adev->in_s0ix)
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL);
+
+ return ret;
+}
+
+static int smu_v13_0_5_dpm_set_vcn_enable(struct smu_context *smu, bool enable)
+{
+ int ret = 0;
+
+ /* vcn dpm on is a prerequisite for vcn power gate messages */
+ if (enable)
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerUpVcn,
+ 0, NULL);
+ else
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerDownVcn,
+ 0, NULL);
+
+ return ret;
+}
+
+static int smu_v13_0_5_dpm_set_jpeg_enable(struct smu_context *smu, bool enable)
+{
+ int ret = 0;
+
+ if (enable)
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerUpJpeg,
+ 0, NULL);
+ else
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_PowerDownJpeg, 0,
+ NULL);
+
+ return ret;
+}
+
+
+static bool smu_v13_0_5_is_dpm_running(struct smu_context *smu)
+{
+ int ret = 0;
+ uint64_t feature_enabled;
+
+ ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
+
+ if (ret)
+ return false;
+
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static int smu_v13_0_5_mode_reset(struct smu_context *smu, int type)
+{
+ int ret = 0;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, type, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "Failed to mode reset!\n");
+
+ return ret;
+}
+
+static int smu_v13_0_5_mode2_reset(struct smu_context *smu)
+{
+ return smu_v13_0_5_mode_reset(smu, SMU_RESET_MODE_2);
+}
+
+static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu,
+ MetricsMember_t member,
+ uint32_t *value)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ SmuMetrics_t *metrics = (SmuMetrics_t *)smu_table->metrics_table;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu, NULL, false);
+ if (ret)
+ return ret;
+
+ switch (member) {
+ case METRICS_AVERAGE_GFXCLK:
+ *value = metrics->GfxclkFrequency;
+ break;
+ case METRICS_AVERAGE_SOCCLK:
+ *value = metrics->SocclkFrequency;
+ break;
+ case METRICS_AVERAGE_VCLK:
+ *value = metrics->VclkFrequency;
+ break;
+ case METRICS_AVERAGE_DCLK:
+ *value = metrics->DclkFrequency;
+ break;
+ case METRICS_AVERAGE_UCLK:
+ *value = metrics->MemclkFrequency;
+ break;
+ case METRICS_AVERAGE_GFXACTIVITY:
+ *value = metrics->GfxActivity / 100;
+ break;
+ case METRICS_AVERAGE_VCNACTIVITY:
+ *value = metrics->UvdActivity;
+ break;
+ case METRICS_AVERAGE_SOCKETPOWER:
+ *value = (metrics->CurrentSocketPower << 8) / 1000;
+ break;
+ case METRICS_TEMPERATURE_EDGE:
+ *value = metrics->GfxTemperature / 100 *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_HOTSPOT:
+ *value = metrics->SocTemperature / 100 *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_THROTTLER_STATUS:
+ *value = metrics->ThrottlerStatus;
+ break;
+ case METRICS_VOLTAGE_VDDGFX:
+ *value = metrics->Voltage[0];
+ break;
+ case METRICS_VOLTAGE_VDDSOC:
+ *value = metrics->Voltage[1];
+ break;
+ default:
+ *value = UINT_MAX;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_5_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data, uint32_t *size)
+{
+ int ret = 0;
+
+ if (!data || !size)
+ return -EINVAL;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_SOCKETPOWER,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_EDGE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_HOTSPOT,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_UCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDGFX,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDNB:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDSOC,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_SS_APU_SHARE:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_SS_APU_SHARE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_SS_DGPU_SHARE:
+ ret = smu_v13_0_5_get_smu_metrics_data(smu,
+ METRICS_SS_DGPU_SHARE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_5_set_watermarks_table(struct smu_context *smu,
+ struct pp_smu_wm_range_sets *clock_ranges)
+{
+ int i;
+ int ret = 0;
+ Watermarks_t *table = smu->smu_table.watermarks_table;
+
+ if (!table || !clock_ranges)
+ return -EINVAL;
+
+ if (clock_ranges) {
+ if (clock_ranges->num_reader_wm_sets > NUM_WM_RANGES ||
+ clock_ranges->num_writer_wm_sets > NUM_WM_RANGES)
+ return -EINVAL;
+
+ for (i = 0; i < clock_ranges->num_reader_wm_sets; i++) {
+ table->WatermarkRow[WM_DCFCLK][i].MinClock =
+ clock_ranges->reader_wm_sets[i].min_drain_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MaxClock =
+ clock_ranges->reader_wm_sets[i].max_drain_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MinMclk =
+ clock_ranges->reader_wm_sets[i].min_fill_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MaxMclk =
+ clock_ranges->reader_wm_sets[i].max_fill_clk_mhz;
+
+ table->WatermarkRow[WM_DCFCLK][i].WmSetting =
+ clock_ranges->reader_wm_sets[i].wm_inst;
+ }
+
+ for (i = 0; i < clock_ranges->num_writer_wm_sets; i++) {
+ table->WatermarkRow[WM_SOCCLK][i].MinClock =
+ clock_ranges->writer_wm_sets[i].min_fill_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MaxClock =
+ clock_ranges->writer_wm_sets[i].max_fill_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MinMclk =
+ clock_ranges->writer_wm_sets[i].min_drain_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MaxMclk =
+ clock_ranges->writer_wm_sets[i].max_drain_clk_mhz;
+
+ table->WatermarkRow[WM_SOCCLK][i].WmSetting =
+ clock_ranges->writer_wm_sets[i].wm_inst;
+ }
+
+ smu->watermarks_bitmap |= WATERMARKS_EXIST;
+ }
+
+ /* pass data to smu controller */
+ if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
+ !(smu->watermarks_bitmap & WATERMARKS_LOADED)) {
+ ret = smu_cmn_write_watermarks_table(smu);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to update WMTABLE!");
+ return ret;
+ }
+ smu->watermarks_bitmap |= WATERMARKS_LOADED;
+ }
+
+ return 0;
+}
+
+static ssize_t smu_v13_0_5_get_gpu_metrics(struct smu_context *smu,
+ void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct gpu_metrics_v2_1 *gpu_metrics =
+ (struct gpu_metrics_v2_1 *)smu_table->gpu_metrics_table;
+ SmuMetrics_t metrics;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu, &metrics, true);
+ if (ret)
+ return ret;
+
+ smu_cmn_init_soft_gpu_metrics(gpu_metrics, 2, 1);
+
+ gpu_metrics->temperature_gfx = metrics.GfxTemperature;
+ gpu_metrics->temperature_soc = metrics.SocTemperature;
+
+ gpu_metrics->average_gfx_activity = metrics.GfxActivity;
+ gpu_metrics->average_mm_activity = metrics.UvdActivity;
+
+ gpu_metrics->average_socket_power = metrics.CurrentSocketPower;
+ gpu_metrics->average_gfx_power = metrics.Power[0];
+ gpu_metrics->average_soc_power = metrics.Power[1];
+ gpu_metrics->average_gfxclk_frequency = metrics.GfxclkFrequency;
+ gpu_metrics->average_socclk_frequency = metrics.SocclkFrequency;
+ gpu_metrics->average_uclk_frequency = metrics.MemclkFrequency;
+ gpu_metrics->average_fclk_frequency = metrics.MemclkFrequency;
+ gpu_metrics->average_vclk_frequency = metrics.VclkFrequency;
+ gpu_metrics->average_dclk_frequency = metrics.DclkFrequency;
+ gpu_metrics->throttle_status = metrics.ThrottlerStatus;
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v2_1);
+}
+
+static int smu_v13_0_5_set_default_dpm_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ return smu_cmn_update_table(smu, SMU_TABLE_DPMCLOCKS, 0, smu_table->clocks_table, false);
+}
+
+static int smu_v13_0_5_od_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type,
+ long input[], uint32_t size)
+{
+ struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
+ int ret = 0;
+
+ /* Only allowed in manual mode */
+ if (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+ return -EINVAL;
+
+ switch (type) {
+ case PP_OD_EDIT_SCLK_VDDC_TABLE:
+ if (size != 2) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+
+ if (input[0] == 0) {
+ if (input[1] < smu->gfx_default_hard_min_freq) {
+ dev_warn(smu->adev->dev,
+ "Fine grain setting minimum sclk (%ld) MHz is less than the minimum allowed (%d) MHz\n",
+ input[1], smu->gfx_default_hard_min_freq);
+ return -EINVAL;
+ }
+ smu->gfx_actual_hard_min_freq = input[1];
+ } else if (input[0] == 1) {
+ if (input[1] > smu->gfx_default_soft_max_freq) {
+ dev_warn(smu->adev->dev,
+ "Fine grain setting maximum sclk (%ld) MHz is greater than the maximum allowed (%d) MHz\n",
+ input[1], smu->gfx_default_soft_max_freq);
+ return -EINVAL;
+ }
+ smu->gfx_actual_soft_max_freq = input[1];
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case PP_OD_RESTORE_DEFAULT_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ } else {
+ smu->gfx_actual_hard_min_freq = smu->gfx_default_hard_min_freq;
+ smu->gfx_actual_soft_max_freq = smu->gfx_default_soft_max_freq;
+ }
+ break;
+ case PP_OD_COMMIT_DPM_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ } else {
+ if (smu->gfx_actual_hard_min_freq > smu->gfx_actual_soft_max_freq) {
+ dev_err(smu->adev->dev,
+ "The setting minimum sclk (%d) MHz is greater than the setting maximum sclk (%d) MHz\n",
+ smu->gfx_actual_hard_min_freq,
+ smu->gfx_actual_soft_max_freq);
+ return -EINVAL;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinGfxClk,
+ smu->gfx_actual_hard_min_freq, NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Set hard min sclk failed!");
+ return ret;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxGfxClk,
+ smu->gfx_actual_soft_max_freq, NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Set soft max sclk failed!");
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_5_get_current_clk_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ MetricsMember_t member_type;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ member_type = METRICS_AVERAGE_SOCCLK;
+ break;
+ case SMU_VCLK:
+ member_type = METRICS_AVERAGE_VCLK;
+ break;
+ case SMU_DCLK:
+ member_type = METRICS_AVERAGE_DCLK;
+ break;
+ case SMU_MCLK:
+ member_type = METRICS_AVERAGE_UCLK;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetGfxclkFrequency, 0, value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return smu_v13_0_5_get_smu_metrics_data(smu, member_type, value);
+}
+
+static int smu_v13_0_5_get_dpm_level_count(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *count)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ *count = clk_table->NumSocClkLevelsEnabled;
+ break;
+ case SMU_VCLK:
+ *count = clk_table->VcnClkLevelsEnabled;
+ break;
+ case SMU_DCLK:
+ *count = clk_table->VcnClkLevelsEnabled;
+ break;
+ case SMU_MCLK:
+ *count = clk_table->NumDfPstatesEnabled;
+ break;
+ case SMU_FCLK:
+ *count = clk_table->NumDfPstatesEnabled;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int smu_v13_0_5_get_dpm_freq_by_index(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t dpm_level,
+ uint32_t *freq)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ if (!clk_table || clk_type >= SMU_CLK_COUNT)
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ if (dpm_level >= clk_table->NumSocClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->SocClocks[dpm_level];
+ break;
+ case SMU_VCLK:
+ if (dpm_level >= clk_table->VcnClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->VClocks[dpm_level];
+ break;
+ case SMU_DCLK:
+ if (dpm_level >= clk_table->VcnClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->DClocks[dpm_level];
+ break;
+ case SMU_UCLK:
+ case SMU_MCLK:
+ if (dpm_level >= clk_table->NumDfPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->DfPstateTable[dpm_level].MemClk;
+ break;
+ case SMU_FCLK:
+ if (dpm_level >= clk_table->NumDfPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->DfPstateTable[dpm_level].FClk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool smu_v13_0_5_clk_dpm_is_enabled(struct smu_context *smu,
+ enum smu_clk_type clk_type)
+{
+ enum smu_feature_mask feature_id = 0;
+
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ feature_id = SMU_FEATURE_DPM_FCLK_BIT;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ feature_id = SMU_FEATURE_DPM_GFXCLK_BIT;
+ break;
+ case SMU_SOCCLK:
+ feature_id = SMU_FEATURE_DPM_SOCCLK_BIT;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ feature_id = SMU_FEATURE_VCN_DPM_BIT;
+ break;
+ default:
+ return true;
+ }
+
+ return smu_cmn_feature_is_enabled(smu, feature_id);
+}
+
+static int smu_v13_0_5_get_dpm_ultimate_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *min,
+ uint32_t *max)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+ uint32_t clock_limit;
+ uint32_t max_dpm_level, min_dpm_level;
+ int ret = 0;
+
+ if (!smu_v13_0_5_clk_dpm_is_enabled(smu, clk_type)) {
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ clock_limit = smu->smu_table.boot_values.uclk;
+ break;
+ case SMU_FCLK:
+ clock_limit = smu->smu_table.boot_values.fclk;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ clock_limit = smu->smu_table.boot_values.gfxclk;
+ break;
+ case SMU_SOCCLK:
+ clock_limit = smu->smu_table.boot_values.socclk;
+ break;
+ case SMU_VCLK:
+ clock_limit = smu->smu_table.boot_values.vclk;
+ break;
+ case SMU_DCLK:
+ clock_limit = smu->smu_table.boot_values.dclk;
+ break;
+ default:
+ clock_limit = 0;
+ break;
+ }
+
+ /* clock in Mhz unit */
+ if (min)
+ *min = clock_limit / 100;
+ if (max)
+ *max = clock_limit / 100;
+
+ return 0;
+ }
+
+ if (max) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *max = clk_table->MaxGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ max_dpm_level = 0;
+ break;
+ case SMU_SOCCLK:
+ max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ max_dpm_level = clk_table->VcnClkLevelsEnabled - 1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, max_dpm_level, max);
+ if (ret)
+ goto failed;
+ }
+ }
+
+ if (min) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *min = clk_table->MinGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ min_dpm_level = clk_table->NumDfPstatesEnabled - 1;
+ break;
+ case SMU_SOCCLK:
+ min_dpm_level = 0;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ min_dpm_level = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, min_dpm_level, min);
+ if (ret)
+ goto failed;
+ }
+ }
+
+failed:
+ return ret;
+}
+
+static int smu_v13_0_5_set_soft_freq_limited_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t min,
+ uint32_t max)
+{
+ enum smu_message_type msg_set_min, msg_set_max;
+ int ret = 0;
+
+ if (!smu_v13_0_5_clk_dpm_is_enabled(smu, clk_type))
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ msg_set_min = SMU_MSG_SetHardMinGfxClk;
+ msg_set_max = SMU_MSG_SetSoftMaxGfxClk;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ msg_set_min = SMU_MSG_SetHardMinVcn;
+ msg_set_max = SMU_MSG_SetSoftMaxVcn;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, msg_set_min, min, NULL);
+ if (ret)
+ goto out;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, msg_set_max, max, NULL);
+ if (ret)
+ goto out;
+
+out:
+ return ret;
+}
+
+static int smu_v13_0_5_print_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type, char *buf)
+{
+ int i, size = 0, ret = 0;
+ uint32_t cur_value = 0, value = 0, count = 0;
+ uint32_t min = 0, max = 0;
+
+ smu_cmn_get_sysfs_buf(&buf, &size);
+
+ switch (clk_type) {
+ case SMU_OD_SCLK:
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_SCLK");
+ size += sysfs_emit_at(buf, size, "0: %10uMhz\n",
+ (smu->gfx_actual_hard_min_freq > 0) ? smu->gfx_actual_hard_min_freq : smu->gfx_default_hard_min_freq);
+ size += sysfs_emit_at(buf, size, "1: %10uMhz\n",
+ (smu->gfx_actual_soft_max_freq > 0) ? smu->gfx_actual_soft_max_freq : smu->gfx_default_soft_max_freq);
+ break;
+ case SMU_OD_RANGE:
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
+ size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
+ smu->gfx_default_hard_min_freq, smu->gfx_default_soft_max_freq);
+ break;
+ case SMU_SOCCLK:
+ case SMU_VCLK:
+ case SMU_DCLK:
+ case SMU_MCLK:
+ ret = smu_v13_0_5_get_current_clk_freq(smu, clk_type, &cur_value);
+ if (ret)
+ goto print_clk_out;
+
+ ret = smu_v13_0_5_get_dpm_level_count(smu, clk_type, &count);
+ if (ret)
+ goto print_clk_out;
+
+ for (i = 0; i < count; i++) {
+ ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, i, &value);
+ if (ret)
+ goto print_clk_out;
+
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, value,
+ cur_value == value ? "*" : "");
+ }
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ ret = smu_v13_0_5_get_current_clk_freq(smu, clk_type, &cur_value);
+ if (ret)
+ goto print_clk_out;
+ min = (smu->gfx_actual_hard_min_freq > 0) ? smu->gfx_actual_hard_min_freq : smu->gfx_default_hard_min_freq;
+ max = (smu->gfx_actual_soft_max_freq > 0) ? smu->gfx_actual_soft_max_freq : smu->gfx_default_soft_max_freq;
+ if (cur_value == max)
+ i = 2;
+ else if (cur_value == min)
+ i = 0;
+ else
+ i = 1;
+ size += sysfs_emit_at(buf, size, "0: %uMhz %s\n", min,
+ i == 0 ? "*" : "");
+ size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
+ i == 1 ? cur_value : SMU_13_0_5_UMD_PSTATE_GFXCLK,
+ i == 1 ? "*" : "");
+ size += sysfs_emit_at(buf, size, "2: %uMhz %s\n", max,
+ i == 2 ? "*" : "");
+ break;
+ default:
+ break;
+ }
+
+print_clk_out:
+ return size;
+}
+
+
+static int smu_v13_0_5_force_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type, uint32_t mask)
+{
+ uint32_t soft_min_level = 0, soft_max_level = 0;
+ uint32_t min_freq = 0, max_freq = 0;
+ int ret = 0;
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ switch (clk_type) {
+ case SMU_VCLK:
+ case SMU_DCLK:
+ ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, soft_min_level, &min_freq);
+ if (ret)
+ goto force_level_out;
+
+ ret = smu_v13_0_5_get_dpm_freq_by_index(smu, clk_type, soft_max_level, &max_freq);
+ if (ret)
+ goto force_level_out;
+
+ ret = smu_v13_0_5_set_soft_freq_limited_range(smu, clk_type, min_freq, max_freq);
+ if (ret)
+ goto force_level_out;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+force_level_out:
+ return ret;
+}
+
+static int smu_v13_0_5_set_performance_level(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t sclk_min = 0, sclk_max = 0;
+ int ret = 0;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ smu_v13_0_5_get_dpm_ultimate_freq(smu, SMU_SCLK, NULL, &sclk_max);
+ sclk_min = sclk_max;
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ smu_v13_0_5_get_dpm_ultimate_freq(smu, SMU_SCLK, &sclk_min, NULL);
+ sclk_max = sclk_min;
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ smu_v13_0_5_get_dpm_ultimate_freq(smu, SMU_SCLK, &sclk_min, &sclk_max);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ /* Temporarily do nothing since the optimal clocks haven't been provided yet */
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ return 0;
+ default:
+ dev_err(adev->dev, "Invalid performance level %d\n", level);
+ return -EINVAL;
+ }
+
+ if (sclk_min && sclk_max && smu_v13_0_5_clk_dpm_is_enabled(smu, SMU_SCLK)) {
+ ret = smu_v13_0_5_set_soft_freq_limited_range(smu,
+ SMU_SCLK,
+ sclk_min,
+ sclk_max);
+ if (ret)
+ return ret;
+
+ smu->gfx_actual_hard_min_freq = sclk_min;
+ smu->gfx_actual_soft_max_freq = sclk_max;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_5_set_fine_grain_gfx_freq_parameters(struct smu_context *smu)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ smu->gfx_default_hard_min_freq = clk_table->MinGfxClk;
+ smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk;
+ smu->gfx_actual_hard_min_freq = 0;
+ smu->gfx_actual_soft_max_freq = 0;
+
+ return 0;
+}
+
+static const struct pptable_funcs smu_v13_0_5_ppt_funcs = {
+ .check_fw_status = smu_v13_0_check_fw_status,
+ .check_fw_version = smu_v13_0_check_fw_version,
+ .init_smc_tables = smu_v13_0_5_init_smc_tables,
+ .fini_smc_tables = smu_v13_0_5_fini_smc_tables,
+ .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values,
+ .system_features_control = smu_v13_0_5_system_features_control,
+ .send_smc_msg_with_param = smu_cmn_send_smc_msg_with_param,
+ .send_smc_msg = smu_cmn_send_smc_msg,
+ .dpm_set_vcn_enable = smu_v13_0_5_dpm_set_vcn_enable,
+ .dpm_set_jpeg_enable = smu_v13_0_5_dpm_set_jpeg_enable,
+ .set_default_dpm_table = smu_v13_0_5_set_default_dpm_tables,
+ .read_sensor = smu_v13_0_5_read_sensor,
+ .is_dpm_running = smu_v13_0_5_is_dpm_running,
+ .set_watermarks_table = smu_v13_0_5_set_watermarks_table,
+ .get_gpu_metrics = smu_v13_0_5_get_gpu_metrics,
+ .get_enabled_mask = smu_cmn_get_enabled_mask,
+ .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
+ .set_driver_table_location = smu_v13_0_set_driver_table_location,
+ .gfx_off_control = smu_v13_0_gfx_off_control,
+ .mode2_reset = smu_v13_0_5_mode2_reset,
+ .get_dpm_ultimate_freq = smu_v13_0_5_get_dpm_ultimate_freq,
+ .od_edit_dpm_table = smu_v13_0_5_od_edit_dpm_table,
+ .print_clk_levels = smu_v13_0_5_print_clk_levels,
+ .force_clk_levels = smu_v13_0_5_force_clk_levels,
+ .set_performance_level = smu_v13_0_5_set_performance_level,
+ .set_fine_grain_gfx_freq_parameters = smu_v13_0_5_set_fine_grain_gfx_freq_parameters,
+};
+
+void smu_v13_0_5_set_ppt_funcs(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ smu->ppt_funcs = &smu_v13_0_5_ppt_funcs;
+ smu->message_map = smu_v13_0_5_message_map;
+ smu->feature_map = smu_v13_0_5_feature_mask_map;
+ smu->table_map = smu_v13_0_5_table_map;
+ smu->is_apu = true;
+ smu->param_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_C2PMSG_34);
+ smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_C2PMSG_2);
+ smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, mmMP1_C2PMSG_33);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.h
new file mode 100644
index 000000000..40bc0f8e6
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SMU_V13_0_5_PPT_H__
+#define __SMU_V13_0_5_PPT_H__
+
+extern void smu_v13_0_5_set_ppt_funcs(struct smu_context *smu);
+#define SMU_13_0_5_UMD_PSTATE_GFXCLK 1100
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
new file mode 100644
index 000000000..9e1967d80
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -0,0 +1,1731 @@
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define SWSMU_CODE_LAYER_L2
+
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "atomfirmware.h"
+#include "amdgpu_atomfirmware.h"
+#include "amdgpu_atombios.h"
+#include "smu_v13_0.h"
+#include "smu13_driver_if_v13_0_7.h"
+#include "soc15_common.h"
+#include "atom.h"
+#include "smu_v13_0_7_ppt.h"
+#include "smu_v13_0_7_pptable.h"
+#include "smu_v13_0_7_ppsmc.h"
+#include "nbio/nbio_4_3_0_offset.h"
+#include "nbio/nbio_4_3_0_sh_mask.h"
+#include "mp/mp_13_0_0_offset.h"
+#include "mp/mp_13_0_0_sh_mask.h"
+
+#include "asic_reg/mp/mp_13_0_0_sh_mask.h"
+#include "smu_cmn.h"
+#include "amdgpu_ras.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
+
+#define FEATURE_MASK(feature) (1ULL << feature)
+#define SMC_DPM_FEATURE ( \
+ FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_LINK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_FCLK_BIT) | \
+ FEATURE_MASK(FEATURE_DPM_MP0CLK_BIT))
+
+#define smnMP1_FIRMWARE_FLAGS_SMU_13_0_7 0x3b10028
+
+#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
+
+static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
+ MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
+ MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
+ MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow, 0),
+ MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh, 0),
+ MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
+ MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
+ MSG_MAP(EnableSmuFeaturesLow, PPSMC_MSG_EnableSmuFeaturesLow, 1),
+ MSG_MAP(EnableSmuFeaturesHigh, PPSMC_MSG_EnableSmuFeaturesHigh, 1),
+ MSG_MAP(DisableSmuFeaturesLow, PPSMC_MSG_DisableSmuFeaturesLow, 1),
+ MSG_MAP(DisableSmuFeaturesHigh, PPSMC_MSG_DisableSmuFeaturesHigh, 1),
+ MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetRunningSmuFeaturesLow, 1),
+ MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh, 1),
+ MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
+ MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
+ MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
+ MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
+ MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
+ MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
+ MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
+ MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
+ MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
+ MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
+ MSG_MAP(EnterBaco, PPSMC_MSG_EnterBaco, 0),
+ MSG_MAP(ExitBaco, PPSMC_MSG_ExitBaco, 0),
+ MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 1),
+ MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 1),
+ MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 1),
+ MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
+ MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 1),
+ MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 1),
+ MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
+ MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 0),
+ MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 0),
+ MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 0),
+ MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 0),
+ MSG_MAP(GetDcModeMaxDpmFreq, PPSMC_MSG_GetDcModeMaxDpmFreq, 1),
+ MSG_MAP(OverridePcieParameters, PPSMC_MSG_OverridePcieParameters, 0),
+ MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0),
+ MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
+ MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
+ MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
+ MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
+ MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 0),
+ MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 0),
+ MSG_MAP(Mode1Reset, PPSMC_MSG_Mode1Reset, 0),
+ MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
+ MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0),
+ MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
+ MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
+ MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0),
+ MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
+};
+
+static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = {
+ CLK_MAP(GFXCLK, PPCLK_GFXCLK),
+ CLK_MAP(SCLK, PPCLK_GFXCLK),
+ CLK_MAP(SOCCLK, PPCLK_SOCCLK),
+ CLK_MAP(FCLK, PPCLK_FCLK),
+ CLK_MAP(UCLK, PPCLK_UCLK),
+ CLK_MAP(MCLK, PPCLK_UCLK),
+ CLK_MAP(VCLK, PPCLK_VCLK_0),
+ CLK_MAP(VCLK1, PPCLK_VCLK_1),
+ CLK_MAP(DCLK, PPCLK_DCLK_0),
+ CLK_MAP(DCLK1, PPCLK_DCLK_1),
+};
+
+static struct cmn2asic_mapping smu_v13_0_7_feature_mask_map[SMU_FEATURE_COUNT] = {
+ FEA_MAP(FW_DATA_READ),
+ FEA_MAP(DPM_GFXCLK),
+ FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
+ FEA_MAP(DPM_UCLK),
+ FEA_MAP(DPM_FCLK),
+ FEA_MAP(DPM_SOCCLK),
+ FEA_MAP(DPM_MP0CLK),
+ FEA_MAP(DPM_LINK),
+ FEA_MAP(DPM_DCN),
+ FEA_MAP(VMEMP_SCALING),
+ FEA_MAP(VDDIO_MEM_SCALING),
+ FEA_MAP(DS_GFXCLK),
+ FEA_MAP(DS_SOCCLK),
+ FEA_MAP(DS_FCLK),
+ FEA_MAP(DS_LCLK),
+ FEA_MAP(DS_DCFCLK),
+ FEA_MAP(DS_UCLK),
+ FEA_MAP(GFX_ULV),
+ FEA_MAP(FW_DSTATE),
+ FEA_MAP(GFXOFF),
+ FEA_MAP(BACO),
+ FEA_MAP(MM_DPM),
+ FEA_MAP(SOC_MPCLK_DS),
+ FEA_MAP(BACO_MPCLK_DS),
+ FEA_MAP(THROTTLERS),
+ FEA_MAP(SMARTSHIFT),
+ FEA_MAP(GTHR),
+ FEA_MAP(ACDC),
+ FEA_MAP(VR0HOT),
+ FEA_MAP(FW_CTF),
+ FEA_MAP(FAN_CONTROL),
+ FEA_MAP(GFX_DCS),
+ FEA_MAP(GFX_READ_MARGIN),
+ FEA_MAP(LED_DISPLAY),
+ FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
+ FEA_MAP(OUT_OF_BAND_MONITOR),
+ FEA_MAP(OPTIMIZED_VMIN),
+ FEA_MAP(GFX_IMU),
+ FEA_MAP(BOOT_TIME_CAL),
+ FEA_MAP(GFX_PCC_DFLL),
+ FEA_MAP(SOC_CG),
+ FEA_MAP(DF_CSTATE),
+ FEA_MAP(GFX_EDC),
+ FEA_MAP(BOOT_POWER_OPT),
+ FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
+ FEA_MAP(DS_VCN),
+ FEA_MAP(BACO_CG),
+ FEA_MAP(MEM_TEMP_READ),
+ FEA_MAP(ATHUB_MMHUB_PG),
+ FEA_MAP(SOC_PCC),
+ [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
+ [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
+ [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
+};
+
+static struct cmn2asic_mapping smu_v13_0_7_table_map[SMU_TABLE_COUNT] = {
+ TAB_MAP(PPTABLE),
+ TAB_MAP(WATERMARKS),
+ TAB_MAP(AVFS_PSM_DEBUG),
+ TAB_MAP(PMSTATUSLOG),
+ TAB_MAP(SMU_METRICS),
+ TAB_MAP(DRIVER_SMU_CONFIG),
+ TAB_MAP(ACTIVITY_MONITOR_COEFF),
+ [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
+};
+
+static struct cmn2asic_mapping smu_v13_0_7_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
+ PWR_MAP(AC),
+ PWR_MAP(DC),
+};
+
+static struct cmn2asic_mapping smu_v13_0_7_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D, WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING, WORKLOAD_PPLIB_POWER_SAVING_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO, WORKLOAD_PPLIB_VIDEO_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR, WORKLOAD_PPLIB_VR_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE, WORKLOAD_PPLIB_COMPUTE_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM, WORKLOAD_PPLIB_CUSTOM_BIT),
+ WORKLOAD_MAP(PP_SMC_POWER_PROFILE_WINDOW3D, WORKLOAD_PPLIB_WINDOW_3D_BIT),
+};
+
+static const uint8_t smu_v13_0_7_throttler_map[] = {
+ [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
+ [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
+ [THROTTLER_PPT2_BIT] = (SMU_THROTTLER_PPT2_BIT),
+ [THROTTLER_PPT3_BIT] = (SMU_THROTTLER_PPT3_BIT),
+ [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
+ [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
+ [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
+ [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
+ [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
+ [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
+ [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
+ [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
+ [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
+ [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
+ [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
+ [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
+ [THROTTLER_FIT_BIT] = (SMU_THROTTLER_FIT_BIT),
+};
+
+static int
+smu_v13_0_7_get_allowed_feature_mask(struct smu_context *smu,
+ uint32_t *feature_mask, uint32_t num)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ if (num > 2)
+ return -EINVAL;
+
+ memset(feature_mask, 0, sizeof(uint32_t) * num);
+
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_FW_DATA_READ_BIT);
+
+ if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_IMU_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFX_POWER_OPTIMIZER_BIT);
+ }
+
+ if (adev->pm.pp_feature & PP_GFXOFF_MASK)
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFXOFF_BIT);
+
+ if (adev->pm.pp_feature & PP_MCLK_DPM_MASK) {
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_FCLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
+ }
+
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);
+
+ if (adev->pm.pp_feature & PP_PCIE_DPM_MASK)
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_LINK_BIT);
+
+ if (adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK)
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);
+
+ if (adev->pm.pp_feature & PP_ULV_MASK)
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_ULV_BIT);
+
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_LCLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_MP0CLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_MM_DPM_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_VCN_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_FCLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DF_CSTATE_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_THROTTLERS_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_VR0HOT_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_FW_CTF_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_FAN_CONTROL_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DS_SOCCLK_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFXCLK_SPREAD_SPECTRUM_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_MEM_TEMP_READ_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_FW_DSTATE_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_SOC_MPCLK_DS_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_BACO_MPCLK_DS_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_PCC_DFLL_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_SOC_CG_BIT);
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_BACO_BIT);
+
+ if (adev->pm.pp_feature & PP_DCEFCLK_DPM_MASK)
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_DCN_BIT);
+
+ if ((adev->pg_flags & AMD_PG_SUPPORT_ATHUB) &&
+ (adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
+ *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);
+
+ return 0;
+}
+
+static int smu_v13_0_7_check_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_7_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ BoardTable_t *BoardTable = &smc_pptable->BoardTable;
+
+ if (powerplay_table->platform_caps & SMU_13_0_7_PP_PLATFORM_CAP_HARDWAREDC)
+ smu->dc_controlled_by_gpio = true;
+
+ if (powerplay_table->platform_caps & SMU_13_0_7_PP_PLATFORM_CAP_BACO ||
+ powerplay_table->platform_caps & SMU_13_0_7_PP_PLATFORM_CAP_MACO)
+ smu_baco->platform_support = true;
+
+ if (smu_baco->platform_support && (BoardTable->HsrEnabled || BoardTable->VddqOffEnabled))
+ smu_baco->maco_support = true;
+
+ table_context->thermal_controller_type =
+ powerplay_table->thermal_controller_type;
+
+ /*
+ * Instead of having its own buffer space and get overdrive_table copied,
+ * smu->od_settings just points to the actual overdrive_table
+ */
+ smu->od_settings = &powerplay_table->overdrive_table;
+
+ return 0;
+}
+
+static int smu_v13_0_7_store_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_7_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->pdev->device == 0x51)
+ powerplay_table->smc_pptable.SkuTable.DebugOverrides |= 0x00000080;
+
+ memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
+ sizeof(PPTable_t));
+
+ return 0;
+}
+
+static int smu_v13_0_7_check_fw_status(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t mp1_fw_flags;
+
+ mp1_fw_flags = RREG32_PCIE(MP1_Public |
+ (smnMP1_FIRMWARE_FLAGS_SMU_13_0_7 & 0xffffffff));
+
+ if ((mp1_fw_flags & MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >>
+ MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT)
+ return 0;
+
+ return -EIO;
+}
+
+#ifndef atom_smc_dpm_info_table_13_0_7
+struct atom_smc_dpm_info_table_13_0_7
+{
+ struct atom_common_table_header table_header;
+ BoardTable_t BoardTable;
+};
+#endif
+
+static int smu_v13_0_7_append_powerplay_table(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+
+ struct atom_smc_dpm_info_table_13_0_7 *smc_dpm_table;
+
+ BoardTable_t *BoardTable = &smc_pptable->BoardTable;
+
+ int index, ret;
+
+ index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
+ smc_dpm_info);
+
+ ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
+ (uint8_t **)&smc_dpm_table);
+ if (ret)
+ return ret;
+
+ memcpy(BoardTable, &smc_dpm_table->BoardTable, sizeof(BoardTable_t));
+
+ return 0;
+}
+
+static int smu_v13_0_7_get_pptable_from_pmfw(struct smu_context *smu,
+ void **table,
+ uint32_t *size)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ void *combo_pptable = smu_table->combo_pptable;
+ int ret = 0;
+
+ ret = smu_cmn_get_combo_pptable(smu);
+ if (ret)
+ return ret;
+
+ *table = combo_pptable;
+ *size = sizeof(struct smu_13_0_7_powerplay_table);
+
+ return 0;
+}
+
+static int smu_v13_0_7_setup_pptable(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ /*
+ * With SCPM enabled, the pptable used will be signed. It cannot
+ * be used directly by driver. To get the raw pptable, we need to
+ * rely on the combo pptable(and its revelant SMU message).
+ */
+ ret = smu_v13_0_7_get_pptable_from_pmfw(smu,
+ &smu_table->power_play_table,
+ &smu_table->power_play_table_size);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_7_store_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ /*
+ * With SCPM enabled, the operation below will be handled
+ * by PSP. Driver involvment is unnecessary and useless.
+ */
+ if (!adev->scpm_enabled) {
+ ret = smu_v13_0_7_append_powerplay_table(smu);
+ if (ret)
+ return ret;
+ }
+
+ ret = smu_v13_0_7_check_powerplay_table(smu);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int smu_v13_0_7_tables_init(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU13_TOOL_SIZE,
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+ sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
+ if (!smu_table->metrics_table)
+ goto err0_out;
+ smu_table->metrics_time = 0;
+
+ smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
+ smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
+ if (!smu_table->gpu_metrics_table)
+ goto err1_out;
+
+ smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
+ if (!smu_table->watermarks_table)
+ goto err2_out;
+
+ return 0;
+
+err2_out:
+ kfree(smu_table->gpu_metrics_table);
+err1_out:
+ kfree(smu_table->metrics_table);
+err0_out:
+ return -ENOMEM;
+}
+
+static int smu_v13_0_7_allocate_dpm_context(struct smu_context *smu)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+
+ smu_dpm->dpm_context = kzalloc(sizeof(struct smu_13_0_dpm_context),
+ GFP_KERNEL);
+ if (!smu_dpm->dpm_context)
+ return -ENOMEM;
+
+ smu_dpm->dpm_context_size = sizeof(struct smu_13_0_dpm_context);
+
+ return 0;
+}
+
+static int smu_v13_0_7_init_smc_tables(struct smu_context *smu)
+{
+ int ret = 0;
+
+ ret = smu_v13_0_7_tables_init(smu);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_7_allocate_dpm_context(smu);
+ if (ret)
+ return ret;
+
+ return smu_v13_0_init_smc_tables(smu);
+}
+
+static int smu_v13_0_7_set_default_dpm_table(struct smu_context *smu)
+{
+ struct smu_13_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+ PPTable_t *driver_ppt = smu->smu_table.driver_pptable;
+ SkuTable_t *skutable = &driver_ppt->SkuTable;
+ struct smu_13_0_dpm_table *dpm_table;
+ struct smu_13_0_pcie_table *pcie_table;
+ uint32_t link_level;
+ int ret = 0;
+
+ /* socclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.soc_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_SOCCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* gfxclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.gfx_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_GFXCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* uclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.uclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_UCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* fclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.fclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_FCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* vclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.vclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_VCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* dclk dpm table setup */
+ dpm_table = &dpm_context->dpm_tables.dclk_table;
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
+ ret = smu_v13_0_set_single_dpm_table(smu,
+ SMU_DCLK,
+ dpm_table);
+ if (ret)
+ return ret;
+ } else {
+ dpm_table->count = 1;
+ dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
+ dpm_table->dpm_levels[0].enabled = true;
+ dpm_table->min = dpm_table->dpm_levels[0].value;
+ dpm_table->max = dpm_table->dpm_levels[0].value;
+ }
+
+ /* lclk dpm table setup */
+ pcie_table = &dpm_context->dpm_tables.pcie_table;
+ pcie_table->num_of_link_levels = 0;
+ for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
+ if (!skutable->PcieGenSpeed[link_level] &&
+ !skutable->PcieLaneCount[link_level] &&
+ !skutable->LclkFreq[link_level])
+ continue;
+
+ pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
+ skutable->PcieGenSpeed[link_level];
+ pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
+ skutable->PcieLaneCount[link_level];
+ pcie_table->clk_freq[pcie_table->num_of_link_levels] =
+ skutable->LclkFreq[link_level];
+ pcie_table->num_of_link_levels++;
+ }
+
+ return 0;
+}
+
+static bool smu_v13_0_7_is_dpm_running(struct smu_context *smu)
+{
+ int ret = 0;
+ uint64_t feature_enabled;
+
+ ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
+ if (ret)
+ return false;
+
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static void smu_v13_0_7_dump_pptable(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+
+ dev_info(smu->adev->dev, "Dumped PPTable:\n");
+
+ dev_info(smu->adev->dev, "Version = 0x%08x\n", skutable->Version);
+ dev_info(smu->adev->dev, "FeaturesToRun[0] = 0x%08x\n", skutable->FeaturesToRun[0]);
+ dev_info(smu->adev->dev, "FeaturesToRun[1] = 0x%08x\n", skutable->FeaturesToRun[1]);
+}
+
+static uint32_t smu_v13_0_7_get_throttler_status(SmuMetrics_t *metrics)
+{
+ uint32_t throttler_status = 0;
+ int i;
+
+ for (i = 0; i < THROTTLER_COUNT; i++)
+ throttler_status |=
+ (metrics->ThrottlingPercentage[i] ? 1U << i : 0);
+
+ return throttler_status;
+}
+
+#define SMU_13_0_7_BUSY_THRESHOLD 15
+static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu,
+ MetricsMember_t member,
+ uint32_t *value)
+{
+ struct smu_table_context *smu_table= &smu->smu_table;
+ SmuMetrics_t *metrics =
+ &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu,
+ NULL,
+ false);
+ if (ret)
+ return ret;
+
+ switch (member) {
+ case METRICS_CURR_GFXCLK:
+ *value = metrics->CurrClock[PPCLK_GFXCLK];
+ break;
+ case METRICS_CURR_SOCCLK:
+ *value = metrics->CurrClock[PPCLK_SOCCLK];
+ break;
+ case METRICS_CURR_UCLK:
+ *value = metrics->CurrClock[PPCLK_UCLK];
+ break;
+ case METRICS_CURR_VCLK:
+ *value = metrics->CurrClock[PPCLK_VCLK_0];
+ break;
+ case METRICS_CURR_VCLK1:
+ *value = metrics->CurrClock[PPCLK_VCLK_1];
+ break;
+ case METRICS_CURR_DCLK:
+ *value = metrics->CurrClock[PPCLK_DCLK_0];
+ break;
+ case METRICS_CURR_DCLK1:
+ *value = metrics->CurrClock[PPCLK_DCLK_1];
+ break;
+ case METRICS_CURR_FCLK:
+ *value = metrics->CurrClock[PPCLK_FCLK];
+ break;
+ case METRICS_AVERAGE_GFXCLK:
+ *value = metrics->AverageGfxclkFrequencyPreDs;
+ break;
+ case METRICS_AVERAGE_FCLK:
+ if (metrics->AverageUclkActivity <= SMU_13_0_7_BUSY_THRESHOLD)
+ *value = metrics->AverageFclkFrequencyPostDs;
+ else
+ *value = metrics->AverageFclkFrequencyPreDs;
+ break;
+ case METRICS_AVERAGE_UCLK:
+ if (metrics->AverageUclkActivity <= SMU_13_0_7_BUSY_THRESHOLD)
+ *value = metrics->AverageMemclkFrequencyPostDs;
+ else
+ *value = metrics->AverageMemclkFrequencyPreDs;
+ break;
+ case METRICS_AVERAGE_VCLK:
+ *value = metrics->AverageVclk0Frequency;
+ break;
+ case METRICS_AVERAGE_DCLK:
+ *value = metrics->AverageDclk0Frequency;
+ break;
+ case METRICS_AVERAGE_VCLK1:
+ *value = metrics->AverageVclk1Frequency;
+ break;
+ case METRICS_AVERAGE_DCLK1:
+ *value = metrics->AverageDclk1Frequency;
+ break;
+ case METRICS_AVERAGE_GFXACTIVITY:
+ *value = metrics->AverageGfxActivity;
+ break;
+ case METRICS_AVERAGE_MEMACTIVITY:
+ *value = metrics->AverageUclkActivity;
+ break;
+ case METRICS_AVERAGE_SOCKETPOWER:
+ *value = metrics->AverageSocketPower << 8;
+ break;
+ case METRICS_TEMPERATURE_EDGE:
+ *value = metrics->AvgTemperature[TEMP_EDGE] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_HOTSPOT:
+ *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_MEM:
+ *value = metrics->AvgTemperature[TEMP_MEM] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRGFX:
+ *value = metrics->AvgTemperature[TEMP_VR_GFX] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_VRSOC:
+ *value = metrics->AvgTemperature[TEMP_VR_SOC] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_THROTTLER_STATUS:
+ *value = smu_v13_0_7_get_throttler_status(metrics);
+ break;
+ case METRICS_CURR_FANSPEED:
+ *value = metrics->AvgFanRpm;
+ break;
+ case METRICS_CURR_FANPWM:
+ *value = metrics->AvgFanPwm;
+ break;
+ case METRICS_VOLTAGE_VDDGFX:
+ *value = metrics->AvgVoltage[SVI_PLANE_GFX];
+ break;
+ case METRICS_PCIE_RATE:
+ *value = metrics->PcieRate;
+ break;
+ case METRICS_PCIE_WIDTH:
+ *value = metrics->PcieWidth;
+ break;
+ default:
+ *value = UINT_MAX;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_7_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data,
+ uint32_t *size)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *smc_pptable = table_context->driver_pptable;
+ int ret = 0;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
+ *(uint16_t *)data = smc_pptable->SkuTable.FanMaximumRpm;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_LOAD:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_MEMACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_SOCKETPOWER,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_HOTSPOT,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_EDGE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_MEM_TEMP:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_MEM,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_UCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDGFX,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_7_get_current_clk_freq_by_table(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ MetricsMember_t member_type;
+ int clk_id = 0;
+
+ clk_id = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_CLK,
+ clk_type);
+ if (clk_id < 0)
+ return -EINVAL;
+
+ switch (clk_id) {
+ case PPCLK_GFXCLK:
+ member_type = METRICS_AVERAGE_GFXCLK;
+ break;
+ case PPCLK_UCLK:
+ member_type = METRICS_CURR_UCLK;
+ break;
+ case PPCLK_FCLK:
+ member_type = METRICS_CURR_FCLK;
+ break;
+ case PPCLK_SOCCLK:
+ member_type = METRICS_CURR_SOCCLK;
+ break;
+ case PPCLK_VCLK_0:
+ member_type = METRICS_CURR_VCLK;
+ break;
+ case PPCLK_DCLK_0:
+ member_type = METRICS_CURR_DCLK;
+ break;
+ case PPCLK_VCLK_1:
+ member_type = METRICS_CURR_VCLK1;
+ break;
+ case PPCLK_DCLK_1:
+ member_type = METRICS_CURR_DCLK1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return smu_v13_0_7_get_smu_metrics_data(smu,
+ member_type,
+ value);
+}
+
+static int smu_v13_0_7_print_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ char *buf)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_13_0_dpm_table *single_dpm_table;
+ struct smu_13_0_pcie_table *pcie_table;
+ uint32_t gen_speed, lane_width;
+ int i, curr_freq, size = 0;
+ int ret = 0;
+
+ smu_cmn_get_sysfs_buf(&buf, &size);
+
+ if (amdgpu_ras_intr_triggered()) {
+ size += sysfs_emit_at(buf, size, "unavailable\n");
+ return size;
+ }
+
+ switch (clk_type) {
+ case SMU_SCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
+ break;
+ case SMU_MCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
+ break;
+ case SMU_SOCCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.soc_table);
+ break;
+ case SMU_FCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
+ break;
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
+ break;
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
+ break;
+ default:
+ break;
+ }
+
+ switch (clk_type) {
+ case SMU_SCLK:
+ case SMU_MCLK:
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ ret = smu_v13_0_7_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to get current clock freq!");
+ return ret;
+ }
+
+ if (single_dpm_table->is_fine_grained) {
+ /*
+ * For fine grained dpms, there are only two dpm levels:
+ * - level 0 -> min clock freq
+ * - level 1 -> max clock freq
+ * And the current clock frequency can be any value between them.
+ * So, if the current clock frequency is not at level 0 or level 1,
+ * we will fake it as three dpm levels:
+ * - level 0 -> min clock freq
+ * - level 1 -> current actual clock freq
+ * - level 2 -> max clock freq
+ */
+ if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
+ (single_dpm_table->dpm_levels[1].value != curr_freq)) {
+ size += sysfs_emit_at(buf, size, "0: %uMhz\n",
+ single_dpm_table->dpm_levels[0].value);
+ size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
+ curr_freq);
+ size += sysfs_emit_at(buf, size, "2: %uMhz\n",
+ single_dpm_table->dpm_levels[1].value);
+ } else {
+ size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
+ single_dpm_table->dpm_levels[0].value,
+ single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
+ size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
+ single_dpm_table->dpm_levels[1].value,
+ single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
+ }
+ } else {
+ for (i = 0; i < single_dpm_table->count; i++)
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
+ i, single_dpm_table->dpm_levels[i].value,
+ single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
+ }
+ break;
+ case SMU_PCIE:
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_PCIE_RATE,
+ &gen_speed);
+ if (ret)
+ return ret;
+
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_PCIE_WIDTH,
+ &lane_width);
+ if (ret)
+ return ret;
+
+ pcie_table = &(dpm_context->dpm_tables.pcie_table);
+ for (i = 0; i < pcie_table->num_of_link_levels; i++)
+ size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
+ (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
+ (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
+ (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
+ (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "",
+ (pcie_table->pcie_lane[i] == 1) ? "x1" :
+ (pcie_table->pcie_lane[i] == 2) ? "x2" :
+ (pcie_table->pcie_lane[i] == 3) ? "x4" :
+ (pcie_table->pcie_lane[i] == 4) ? "x8" :
+ (pcie_table->pcie_lane[i] == 5) ? "x12" :
+ (pcie_table->pcie_lane[i] == 6) ? "x16" : "",
+ pcie_table->clk_freq[i],
+ (gen_speed == pcie_table->pcie_gen[i]) &&
+ (lane_width == pcie_table->pcie_lane[i]) ?
+ "*" : "");
+ break;
+
+ default:
+ break;
+ }
+
+ return size;
+}
+
+static int smu_v13_0_7_force_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t mask)
+{
+ struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
+ struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
+ struct smu_13_0_dpm_table *single_dpm_table;
+ uint32_t soft_min_level, soft_max_level;
+ uint32_t min_freq, max_freq;
+ int ret = 0;
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
+ break;
+ case SMU_SOCCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.soc_table);
+ break;
+ case SMU_FCLK:
+ single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
+ break;
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
+ break;
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
+ break;
+ default:
+ break;
+ }
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ case SMU_VCLK:
+ case SMU_VCLK1:
+ case SMU_DCLK:
+ case SMU_DCLK1:
+ if (single_dpm_table->is_fine_grained) {
+ /* There is only 2 levels for fine grained DPM */
+ soft_max_level = (soft_max_level >= 1 ? 1 : 0);
+ soft_min_level = (soft_min_level >= 1 ? 1 : 0);
+ } else {
+ if ((soft_max_level >= single_dpm_table->count) ||
+ (soft_min_level >= single_dpm_table->count))
+ return -EINVAL;
+ }
+
+ min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
+ max_freq = single_dpm_table->dpm_levels[soft_max_level].value;
+
+ ret = smu_v13_0_set_soft_freq_limited_range(smu,
+ clk_type,
+ min_freq,
+ max_freq);
+ break;
+ case SMU_DCEFCLK:
+ case SMU_PCIE:
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_7_update_pcie_parameters(struct smu_context *smu,
+ uint32_t pcie_gen_cap,
+ uint32_t pcie_width_cap)
+{
+ struct smu_13_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+ struct smu_13_0_pcie_table *pcie_table =
+ &dpm_context->dpm_tables.pcie_table;
+ uint32_t smu_pcie_arg;
+ int ret, i;
+
+ for (i = 0; i < pcie_table->num_of_link_levels; i++) {
+ if (pcie_table->pcie_gen[i] > pcie_gen_cap)
+ pcie_table->pcie_gen[i] = pcie_gen_cap;
+ if (pcie_table->pcie_lane[i] > pcie_width_cap)
+ pcie_table->pcie_lane[i] = pcie_width_cap;
+
+ smu_pcie_arg = i << 16;
+ smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
+ smu_pcie_arg |= pcie_table->pcie_lane[i];
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_OverridePcieParameters,
+ smu_pcie_arg,
+ NULL);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct smu_temperature_range smu13_thermal_policy[] =
+{
+ {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
+ { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
+};
+
+static int smu_v13_0_7_get_thermal_temperature_range(struct smu_context *smu,
+ struct smu_temperature_range *range)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_7_powerplay_table *powerplay_table =
+ table_context->power_play_table;
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+
+ if (!range)
+ return -EINVAL;
+
+ memcpy(range, &smu13_thermal_policy[0], sizeof(struct smu_temperature_range));
+
+ range->max = pptable->SkuTable.TemperatureLimit[TEMP_EDGE] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->edge_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->hotspot_crit_max = pptable->SkuTable.TemperatureLimit[TEMP_HOTSPOT] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->hotspot_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->mem_crit_max = pptable->SkuTable.TemperatureLimit[TEMP_MEM] *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->mem_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
+ range->software_shutdown_temp_offset = pptable->SkuTable.FanAbnormalTempLimitOffset;
+
+ return 0;
+}
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+static ssize_t smu_v13_0_7_get_gpu_metrics(struct smu_context *smu,
+ void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct gpu_metrics_v1_3 *gpu_metrics =
+ (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
+ SmuMetricsExternal_t metrics_ext;
+ SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu,
+ &metrics_ext,
+ true);
+ if (ret)
+ return ret;
+
+ smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
+
+ gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
+ gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
+ gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
+ gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
+ gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
+ gpu_metrics->temperature_vrmem = MAX(metrics->AvgTemperature[TEMP_VR_MEM0],
+ metrics->AvgTemperature[TEMP_VR_MEM1]);
+
+ gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
+ gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
+ gpu_metrics->average_mm_activity = MAX(metrics->Vcn0ActivityPercentage,
+ metrics->Vcn1ActivityPercentage);
+
+ gpu_metrics->average_socket_power = metrics->AverageSocketPower;
+ gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
+
+ if (metrics->AverageGfxActivity <= SMU_13_0_7_BUSY_THRESHOLD)
+ gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
+ else
+ gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
+
+ if (metrics->AverageUclkActivity <= SMU_13_0_7_BUSY_THRESHOLD)
+ gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
+ else
+ gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
+
+ gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
+ gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
+ gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
+ gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
+
+ gpu_metrics->current_gfxclk = metrics->CurrClock[PPCLK_GFXCLK];
+ gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
+ gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
+ gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_1];
+ gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_1];
+
+ gpu_metrics->throttle_status =
+ smu_v13_0_7_get_throttler_status(metrics);
+ gpu_metrics->indep_throttle_status =
+ smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
+ smu_v13_0_7_throttler_map);
+
+ gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
+
+ gpu_metrics->pcie_link_width = metrics->PcieWidth;
+ gpu_metrics->pcie_link_speed = metrics->PcieRate;
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+
+ gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_GFX];
+ gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_SOC];
+ gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VMEMP];
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v1_3);
+}
+
+static int smu_v13_0_7_populate_umd_state_clk(struct smu_context *smu)
+{
+ struct smu_13_0_dpm_context *dpm_context =
+ smu->smu_dpm.dpm_context;
+ struct smu_13_0_dpm_table *gfx_table =
+ &dpm_context->dpm_tables.gfx_table;
+ struct smu_13_0_dpm_table *mem_table =
+ &dpm_context->dpm_tables.uclk_table;
+ struct smu_13_0_dpm_table *soc_table =
+ &dpm_context->dpm_tables.soc_table;
+ struct smu_13_0_dpm_table *vclk_table =
+ &dpm_context->dpm_tables.vclk_table;
+ struct smu_13_0_dpm_table *dclk_table =
+ &dpm_context->dpm_tables.dclk_table;
+ struct smu_13_0_dpm_table *fclk_table =
+ &dpm_context->dpm_tables.fclk_table;
+ struct smu_umd_pstate_table *pstate_table =
+ &smu->pstate_table;
+
+ pstate_table->gfxclk_pstate.min = gfx_table->min;
+ pstate_table->gfxclk_pstate.peak = gfx_table->max;
+
+ pstate_table->uclk_pstate.min = mem_table->min;
+ pstate_table->uclk_pstate.peak = mem_table->max;
+
+ pstate_table->socclk_pstate.min = soc_table->min;
+ pstate_table->socclk_pstate.peak = soc_table->max;
+
+ pstate_table->vclk_pstate.min = vclk_table->min;
+ pstate_table->vclk_pstate.peak = vclk_table->max;
+
+ pstate_table->dclk_pstate.min = dclk_table->min;
+ pstate_table->dclk_pstate.peak = dclk_table->max;
+
+ pstate_table->fclk_pstate.min = fclk_table->min;
+ pstate_table->fclk_pstate.peak = fclk_table->max;
+
+ /*
+ * For now, just use the mininum clock frequency.
+ * TODO: update them when the real pstate settings available
+ */
+ pstate_table->gfxclk_pstate.standard = gfx_table->min;
+ pstate_table->uclk_pstate.standard = mem_table->min;
+ pstate_table->socclk_pstate.standard = soc_table->min;
+ pstate_table->vclk_pstate.standard = vclk_table->min;
+ pstate_table->dclk_pstate.standard = dclk_table->min;
+ pstate_table->fclk_pstate.standard = fclk_table->min;
+
+ return 0;
+}
+
+static int smu_v13_0_7_get_fan_speed_pwm(struct smu_context *smu,
+ uint32_t *speed)
+{
+ int ret;
+
+ if (!speed)
+ return -EINVAL;
+
+ ret = smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_CURR_FANPWM,
+ speed);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to get fan speed(PWM)!");
+ return ret;
+ }
+
+ /* Convert the PMFW output which is in percent to pwm(255) based */
+ *speed = MIN(*speed * 255 / 100, 255);
+
+ return 0;
+}
+
+static int smu_v13_0_7_get_fan_speed_rpm(struct smu_context *smu,
+ uint32_t *speed)
+{
+ if (!speed)
+ return -EINVAL;
+
+ return smu_v13_0_7_get_smu_metrics_data(smu,
+ METRICS_CURR_FANSPEED,
+ speed);
+}
+
+static int smu_v13_0_7_enable_mgpu_fan_boost(struct smu_context *smu)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+
+ /*
+ * Skip the MGpuFanBoost setting for those ASICs
+ * which do not support it
+ */
+ if (skutable->MGpuAcousticLimitRpmThreshold == 0)
+ return 0;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetMGpuFanBoostLimitRpm,
+ 0,
+ NULL);
+}
+
+static int smu_v13_0_7_get_power_limit(struct smu_context *smu,
+ uint32_t *current_power_limit,
+ uint32_t *default_power_limit,
+ uint32_t *max_power_limit)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ struct smu_13_0_7_powerplay_table *powerplay_table =
+ (struct smu_13_0_7_powerplay_table *)table_context->power_play_table;
+ PPTable_t *pptable = table_context->driver_pptable;
+ SkuTable_t *skutable = &pptable->SkuTable;
+ uint32_t power_limit, od_percent;
+
+ if (smu_v13_0_get_current_power_limit(smu, &power_limit))
+ power_limit = smu->adev->pm.ac_power ?
+ skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
+ skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
+
+ if (current_power_limit)
+ *current_power_limit = power_limit;
+ if (default_power_limit)
+ *default_power_limit = power_limit;
+
+ if (max_power_limit) {
+ if (smu->od_enabled) {
+ od_percent = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]);
+
+ dev_dbg(smu->adev->dev, "ODSETTING_POWERPERCENTAGE: %d (default: %d)\n", od_percent, power_limit);
+
+ power_limit *= (100 + od_percent);
+ power_limit /= 100;
+ }
+ *max_power_limit = power_limit;
+ }
+
+ return 0;
+}
+
+static int smu_v13_0_7_get_power_profile_mode(struct smu_context *smu, char *buf)
+{
+ DpmActivityMonitorCoeffIntExternal_t *activity_monitor_external;
+ uint32_t i, j, size = 0;
+ int16_t workload_type = 0;
+ int result = 0;
+
+ if (!buf)
+ return -EINVAL;
+
+ activity_monitor_external = kcalloc(PP_SMC_POWER_PROFILE_COUNT,
+ sizeof(*activity_monitor_external),
+ GFP_KERNEL);
+ if (!activity_monitor_external)
+ return -ENOMEM;
+
+ size += sysfs_emit_at(buf, size, " ");
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_WINDOW3D; i++)
+ size += sysfs_emit_at(buf, size, "%-14s%s", amdgpu_pp_profile_name[i],
+ (i == smu->power_profile_mode) ? "* " : " ");
+
+ size += sysfs_emit_at(buf, size, "\n");
+
+ for (i = 0; i <= PP_SMC_POWER_PROFILE_WINDOW3D; i++) {
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_WORKLOAD,
+ i);
+ if (workload_type < 0) {
+ result = -EINVAL;
+ goto out;
+ }
+
+ result = smu_cmn_update_table(smu,
+ SMU_TABLE_ACTIVITY_MONITOR_COEFF, workload_type,
+ (void *)(&activity_monitor_external[i]), false);
+ if (result) {
+ dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
+ goto out;
+ }
+ }
+
+#define PRINT_DPM_MONITOR(field) \
+do { \
+ size += sysfs_emit_at(buf, size, "%-30s", #field); \
+ for (j = 0; j <= PP_SMC_POWER_PROFILE_WINDOW3D; j++) \
+ size += sysfs_emit_at(buf, size, "%-16d", activity_monitor_external[j].DpmActivityMonitorCoeffInt.field); \
+ size += sysfs_emit_at(buf, size, "\n"); \
+} while (0)
+
+ PRINT_DPM_MONITOR(Gfx_ActiveHystLimit);
+ PRINT_DPM_MONITOR(Gfx_IdleHystLimit);
+ PRINT_DPM_MONITOR(Gfx_FPS);
+ PRINT_DPM_MONITOR(Gfx_MinActiveFreqType);
+ PRINT_DPM_MONITOR(Gfx_BoosterFreqType);
+ PRINT_DPM_MONITOR(Gfx_MinActiveFreq);
+ PRINT_DPM_MONITOR(Gfx_BoosterFreq);
+ PRINT_DPM_MONITOR(Fclk_ActiveHystLimit);
+ PRINT_DPM_MONITOR(Fclk_IdleHystLimit);
+ PRINT_DPM_MONITOR(Fclk_FPS);
+ PRINT_DPM_MONITOR(Fclk_MinActiveFreqType);
+ PRINT_DPM_MONITOR(Fclk_BoosterFreqType);
+ PRINT_DPM_MONITOR(Fclk_MinActiveFreq);
+ PRINT_DPM_MONITOR(Fclk_BoosterFreq);
+#undef PRINT_DPM_MONITOR
+
+ result = size;
+out:
+ kfree(activity_monitor_external);
+ return result;
+}
+
+static int smu_v13_0_7_set_power_profile_mode(struct smu_context *smu, long *input, uint32_t size)
+{
+
+ DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
+ DpmActivityMonitorCoeffInt_t *activity_monitor =
+ &(activity_monitor_external.DpmActivityMonitorCoeffInt);
+ int workload_type, ret = 0;
+
+ smu->power_profile_mode = input[size];
+
+ if (smu->power_profile_mode > PP_SMC_POWER_PROFILE_WINDOW3D) {
+ dev_err(smu->adev->dev, "Invalid power profile mode %d\n", smu->power_profile_mode);
+ return -EINVAL;
+ }
+
+ if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
+
+ ret = smu_cmn_update_table(smu,
+ SMU_TABLE_ACTIVITY_MONITOR_COEFF, WORKLOAD_PPLIB_CUSTOM_BIT,
+ (void *)(&activity_monitor_external), false);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
+ return ret;
+ }
+
+ switch (input[0]) {
+ case 0: /* Gfxclk */
+ activity_monitor->Gfx_ActiveHystLimit = input[1];
+ activity_monitor->Gfx_IdleHystLimit = input[2];
+ activity_monitor->Gfx_FPS = input[3];
+ activity_monitor->Gfx_MinActiveFreqType = input[4];
+ activity_monitor->Gfx_BoosterFreqType = input[5];
+ activity_monitor->Gfx_MinActiveFreq = input[6];
+ activity_monitor->Gfx_BoosterFreq = input[7];
+ break;
+ case 1: /* Fclk */
+ activity_monitor->Fclk_ActiveHystLimit = input[1];
+ activity_monitor->Fclk_IdleHystLimit = input[2];
+ activity_monitor->Fclk_FPS = input[3];
+ activity_monitor->Fclk_MinActiveFreqType = input[4];
+ activity_monitor->Fclk_BoosterFreqType = input[5];
+ activity_monitor->Fclk_MinActiveFreq = input[6];
+ activity_monitor->Fclk_BoosterFreq = input[7];
+ break;
+ }
+
+ ret = smu_cmn_update_table(smu,
+ SMU_TABLE_ACTIVITY_MONITOR_COEFF, WORKLOAD_PPLIB_CUSTOM_BIT,
+ (void *)(&activity_monitor_external), true);
+ if (ret) {
+ dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
+ return ret;
+ }
+ }
+
+ /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+ workload_type = smu_cmn_to_asic_specific_index(smu,
+ CMN2ASIC_MAPPING_WORKLOAD,
+ smu->power_profile_mode);
+ if (workload_type < 0)
+ return -EINVAL;
+ smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetWorkloadMask,
+ 1 << workload_type, NULL);
+
+ return ret;
+}
+
+static int smu_v13_0_7_set_mp1_state(struct smu_context *smu,
+ enum pp_mp1_state mp1_state)
+{
+ int ret;
+
+ switch (mp1_state) {
+ case PP_MP1_STATE_UNLOAD:
+ ret = smu_cmn_set_mp1_state(smu, mp1_state);
+ break;
+ default:
+ /* Ignore others */
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int smu_v13_0_7_baco_enter(struct smu_context *smu)
+{
+ struct smu_baco_context *smu_baco = &smu->smu_baco;
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
+ return smu_v13_0_baco_set_armd3_sequence(smu,
+ smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
+ else
+ return smu_v13_0_baco_enter(smu);
+}
+
+static int smu_v13_0_7_baco_exit(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
+ /* Wait for PMFW handling for the Dstate change */
+ usleep_range(10000, 11000);
+ return smu_v13_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
+ } else {
+ return smu_v13_0_baco_exit(smu);
+ }
+}
+
+static bool smu_v13_0_7_is_mode1_reset_supported(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ /* SRIOV does not support SMU mode1 reset */
+ if (amdgpu_sriov_vf(adev))
+ return false;
+
+ return true;
+}
+
+static int smu_v13_0_7_set_df_cstate(struct smu_context *smu,
+ enum pp_df_cstate state)
+{
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_DFCstateControl,
+ state,
+ NULL);
+}
+
+static const struct pptable_funcs smu_v13_0_7_ppt_funcs = {
+ .get_allowed_feature_mask = smu_v13_0_7_get_allowed_feature_mask,
+ .set_default_dpm_table = smu_v13_0_7_set_default_dpm_table,
+ .is_dpm_running = smu_v13_0_7_is_dpm_running,
+ .dump_pptable = smu_v13_0_7_dump_pptable,
+ .init_microcode = smu_v13_0_init_microcode,
+ .load_microcode = smu_v13_0_load_microcode,
+ .fini_microcode = smu_v13_0_fini_microcode,
+ .init_smc_tables = smu_v13_0_7_init_smc_tables,
+ .fini_smc_tables = smu_v13_0_fini_smc_tables,
+ .init_power = smu_v13_0_init_power,
+ .fini_power = smu_v13_0_fini_power,
+ .check_fw_status = smu_v13_0_7_check_fw_status,
+ .setup_pptable = smu_v13_0_7_setup_pptable,
+ .check_fw_version = smu_v13_0_check_fw_version,
+ .write_pptable = smu_cmn_write_pptable,
+ .set_driver_table_location = smu_v13_0_set_driver_table_location,
+ .system_features_control = smu_v13_0_system_features_control,
+ .set_allowed_mask = smu_v13_0_set_allowed_mask,
+ .get_enabled_mask = smu_cmn_get_enabled_mask,
+ .dpm_set_vcn_enable = smu_v13_0_set_vcn_enable,
+ .dpm_set_jpeg_enable = smu_v13_0_set_jpeg_enable,
+ .init_pptable_microcode = smu_v13_0_init_pptable_microcode,
+ .populate_umd_state_clk = smu_v13_0_7_populate_umd_state_clk,
+ .get_dpm_ultimate_freq = smu_v13_0_get_dpm_ultimate_freq,
+ .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values,
+ .read_sensor = smu_v13_0_7_read_sensor,
+ .feature_is_enabled = smu_cmn_feature_is_enabled,
+ .print_clk_levels = smu_v13_0_7_print_clk_levels,
+ .force_clk_levels = smu_v13_0_7_force_clk_levels,
+ .update_pcie_parameters = smu_v13_0_7_update_pcie_parameters,
+ .get_thermal_temperature_range = smu_v13_0_7_get_thermal_temperature_range,
+ .register_irq_handler = smu_v13_0_register_irq_handler,
+ .enable_thermal_alert = smu_v13_0_enable_thermal_alert,
+ .disable_thermal_alert = smu_v13_0_disable_thermal_alert,
+ .notify_memory_pool_location = smu_v13_0_notify_memory_pool_location,
+ .get_gpu_metrics = smu_v13_0_7_get_gpu_metrics,
+ .set_soft_freq_limited_range = smu_v13_0_set_soft_freq_limited_range,
+ .set_performance_level = smu_v13_0_set_performance_level,
+ .gfx_off_control = smu_v13_0_gfx_off_control,
+ .get_fan_speed_pwm = smu_v13_0_7_get_fan_speed_pwm,
+ .get_fan_speed_rpm = smu_v13_0_7_get_fan_speed_rpm,
+ .set_fan_speed_pwm = smu_v13_0_set_fan_speed_pwm,
+ .set_fan_speed_rpm = smu_v13_0_set_fan_speed_rpm,
+ .get_fan_control_mode = smu_v13_0_get_fan_control_mode,
+ .set_fan_control_mode = smu_v13_0_set_fan_control_mode,
+ .enable_mgpu_fan_boost = smu_v13_0_7_enable_mgpu_fan_boost,
+ .get_power_limit = smu_v13_0_7_get_power_limit,
+ .set_power_limit = smu_v13_0_set_power_limit,
+ .get_power_profile_mode = smu_v13_0_7_get_power_profile_mode,
+ .set_power_profile_mode = smu_v13_0_7_set_power_profile_mode,
+ .set_tool_table_location = smu_v13_0_set_tool_table_location,
+ .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
+ .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
+ .baco_is_support = smu_v13_0_baco_is_support,
+ .baco_get_state = smu_v13_0_baco_get_state,
+ .baco_set_state = smu_v13_0_baco_set_state,
+ .baco_enter = smu_v13_0_7_baco_enter,
+ .baco_exit = smu_v13_0_7_baco_exit,
+ .mode1_reset_is_support = smu_v13_0_7_is_mode1_reset_supported,
+ .mode1_reset = smu_v13_0_mode1_reset,
+ .set_mp1_state = smu_v13_0_7_set_mp1_state,
+ .set_df_cstate = smu_v13_0_7_set_df_cstate,
+ .gpo_control = smu_v13_0_gpo_control,
+};
+
+void smu_v13_0_7_set_ppt_funcs(struct smu_context *smu)
+{
+ smu->ppt_funcs = &smu_v13_0_7_ppt_funcs;
+ smu->message_map = smu_v13_0_7_message_map;
+ smu->clock_map = smu_v13_0_7_clk_map;
+ smu->feature_map = smu_v13_0_7_feature_mask_map;
+ smu->table_map = smu_v13_0_7_table_map;
+ smu->pwr_src_map = smu_v13_0_7_pwr_src_map;
+ smu->workload_map = smu_v13_0_7_workload_map;
+ smu_v13_0_set_smu_mailbox_registers(smu);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.h
new file mode 100644
index 000000000..a4763cfed
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SMU_V13_0_7_PPT_H__
+#define __SMU_V13_0_7_PPT_H__
+
+extern void smu_v13_0_7_set_ppt_funcs(struct smu_context *smu);
+
+#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
new file mode 100644
index 000000000..04e56b0b3
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
@@ -0,0 +1,1238 @@
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define SWSMU_CODE_LAYER_L2
+
+#include "amdgpu.h"
+#include "amdgpu_smu.h"
+#include "smu_v13_0.h"
+#include "smu13_driver_if_yellow_carp.h"
+#include "yellow_carp_ppt.h"
+#include "smu_v13_0_1_ppsmc.h"
+#include "smu_v13_0_1_pmfw.h"
+#include "smu_cmn.h"
+
+/*
+ * DO NOT use these for err/warn/info/debug messages.
+ * Use dev_err, dev_warn, dev_info and dev_dbg instead.
+ * They are more MGPU friendly.
+ */
+#undef pr_err
+#undef pr_warn
+#undef pr_info
+#undef pr_debug
+
+#define regSMUIO_GFX_MISC_CNTL 0x00c5
+#define regSMUIO_GFX_MISC_CNTL_BASE_IDX 0
+#define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK 0x00000006L
+#define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS__SHIFT 0x1L
+
+#define FEATURE_MASK(feature) (1ULL << feature)
+#define SMC_DPM_FEATURE ( \
+ FEATURE_MASK(FEATURE_CCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_VCN_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_FCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_SOCCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_MP0CLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_LCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_SHUBCLK_DPM_BIT) | \
+ FEATURE_MASK(FEATURE_DCFCLK_DPM_BIT)| \
+ FEATURE_MASK(FEATURE_GFX_DPM_BIT))
+
+static struct cmn2asic_msg_mapping yellow_carp_message_map[SMU_MSG_MAX_COUNT] = {
+ MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
+ MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
+ MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
+ MSG_MAP(EnableGfxOff, PPSMC_MSG_EnableGfxOff, 1),
+ MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 1),
+ MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 1),
+ MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 1),
+ MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 1),
+ MSG_MAP(SetHardMinVcn, PPSMC_MSG_SetHardMinVcn, 1),
+ MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 1),
+ MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
+ MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
+ MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
+ MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 1),
+ MSG_MAP(GfxDeviceDriverReset, PPSMC_MSG_GfxDeviceDriverReset, 1),
+ MSG_MAP(GetEnabledSmuFeatures, PPSMC_MSG_GetEnabledSmuFeatures, 1),
+ MSG_MAP(SetHardMinSocclkByFreq, PPSMC_MSG_SetHardMinSocclkByFreq, 1),
+ MSG_MAP(SetSoftMinVcn, PPSMC_MSG_SetSoftMinVcn, 1),
+ MSG_MAP(GetGfxclkFrequency, PPSMC_MSG_GetGfxclkFrequency, 1),
+ MSG_MAP(GetFclkFrequency, PPSMC_MSG_GetFclkFrequency, 1),
+ MSG_MAP(SetSoftMaxGfxClk, PPSMC_MSG_SetSoftMaxGfxClk, 1),
+ MSG_MAP(SetHardMinGfxClk, PPSMC_MSG_SetHardMinGfxClk, 1),
+ MSG_MAP(SetSoftMaxSocclkByFreq, PPSMC_MSG_SetSoftMaxSocclkByFreq, 1),
+ MSG_MAP(SetSoftMaxFclkByFreq, PPSMC_MSG_SetSoftMaxFclkByFreq, 1),
+ MSG_MAP(SetSoftMaxVcn, PPSMC_MSG_SetSoftMaxVcn, 1),
+ MSG_MAP(SetPowerLimitPercentage, PPSMC_MSG_SetPowerLimitPercentage, 1),
+ MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 1),
+ MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 1),
+ MSG_MAP(SetHardMinFclkByFreq, PPSMC_MSG_SetHardMinFclkByFreq, 1),
+ MSG_MAP(SetSoftMinSocclkByFreq, PPSMC_MSG_SetSoftMinSocclkByFreq, 1),
+};
+
+static struct cmn2asic_mapping yellow_carp_feature_mask_map[SMU_FEATURE_COUNT] = {
+ FEA_MAP(CCLK_DPM),
+ FEA_MAP(FAN_CONTROLLER),
+ FEA_MAP(PPT),
+ FEA_MAP(TDC),
+ FEA_MAP(THERMAL),
+ FEA_MAP(ULV),
+ FEA_MAP(VCN_DPM),
+ FEA_MAP_REVERSE(FCLK),
+ FEA_MAP_REVERSE(SOCCLK),
+ FEA_MAP(LCLK_DPM),
+ FEA_MAP(SHUBCLK_DPM),
+ FEA_MAP(DCFCLK_DPM),
+ FEA_MAP_HALF_REVERSE(GFX),
+ FEA_MAP(DS_GFXCLK),
+ FEA_MAP(DS_SOCCLK),
+ FEA_MAP(DS_LCLK),
+ FEA_MAP(DS_DCFCLK),
+ FEA_MAP(DS_FCLK),
+ FEA_MAP(DS_MP1CLK),
+ FEA_MAP(DS_MP0CLK),
+ FEA_MAP(GFX_DEM),
+ FEA_MAP(PSI),
+ FEA_MAP(PROCHOT),
+ FEA_MAP(CPUOFF),
+ FEA_MAP(STAPM),
+ FEA_MAP(S0I3),
+ FEA_MAP(PERF_LIMIT),
+ FEA_MAP(CORE_DLDO),
+ FEA_MAP(RSMU_LOW_POWER),
+ FEA_MAP(SMN_LOW_POWER),
+ FEA_MAP(THM_LOW_POWER),
+ FEA_MAP(SMUIO_LOW_POWER),
+ FEA_MAP(MP1_LOW_POWER),
+ FEA_MAP(DS_VCN),
+ FEA_MAP(CPPC),
+ FEA_MAP(DF_CSTATES),
+ FEA_MAP(MSMU_LOW_POWER),
+ FEA_MAP(ATHUB_PG),
+};
+
+static struct cmn2asic_mapping yellow_carp_table_map[SMU_TABLE_COUNT] = {
+ TAB_MAP_VALID(WATERMARKS),
+ TAB_MAP_VALID(SMU_METRICS),
+ TAB_MAP_VALID(CUSTOM_DPM),
+ TAB_MAP_VALID(DPMCLOCKS),
+};
+
+static int yellow_carp_init_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+
+ SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+ SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
+ PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
+
+ smu_table->clocks_table = kzalloc(sizeof(DpmClocks_t), GFP_KERNEL);
+ if (!smu_table->clocks_table)
+ goto err0_out;
+
+ smu_table->metrics_table = kzalloc(sizeof(SmuMetrics_t), GFP_KERNEL);
+ if (!smu_table->metrics_table)
+ goto err1_out;
+ smu_table->metrics_time = 0;
+
+ smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
+ if (!smu_table->watermarks_table)
+ goto err2_out;
+
+ smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v2_1);
+ smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
+ if (!smu_table->gpu_metrics_table)
+ goto err3_out;
+
+ return 0;
+
+err3_out:
+ kfree(smu_table->watermarks_table);
+err2_out:
+ kfree(smu_table->metrics_table);
+err1_out:
+ kfree(smu_table->clocks_table);
+err0_out:
+ return -ENOMEM;
+}
+
+static int yellow_carp_fini_smc_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ kfree(smu_table->clocks_table);
+ smu_table->clocks_table = NULL;
+
+ kfree(smu_table->metrics_table);
+ smu_table->metrics_table = NULL;
+
+ kfree(smu_table->watermarks_table);
+ smu_table->watermarks_table = NULL;
+
+ kfree(smu_table->gpu_metrics_table);
+ smu_table->gpu_metrics_table = NULL;
+
+ return 0;
+}
+
+static int yellow_carp_system_features_control(struct smu_context *smu, bool en)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ if (!en && !adev->in_s0ix)
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL);
+
+ return ret;
+}
+
+static int yellow_carp_dpm_set_vcn_enable(struct smu_context *smu, bool enable)
+{
+ int ret = 0;
+
+ /* vcn dpm on is a prerequisite for vcn power gate messages */
+ if (enable)
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerUpVcn,
+ 0, NULL);
+ else
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerDownVcn,
+ 0, NULL);
+
+ return ret;
+}
+
+static int yellow_carp_dpm_set_jpeg_enable(struct smu_context *smu, bool enable)
+{
+ int ret = 0;
+
+ if (enable)
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PowerUpJpeg,
+ 0, NULL);
+ else
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_PowerDownJpeg, 0,
+ NULL);
+
+ return ret;
+}
+
+
+static bool yellow_carp_is_dpm_running(struct smu_context *smu)
+{
+ int ret = 0;
+ uint64_t feature_enabled;
+
+ ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
+
+ if (ret)
+ return false;
+
+ return !!(feature_enabled & SMC_DPM_FEATURE);
+}
+
+static int yellow_carp_post_smu_init(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+ /* allow message will be sent after enable message on Yellow Carp*/
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_EnableGfxOff, NULL);
+ if (ret)
+ dev_err(adev->dev, "Failed to Enable GfxOff!\n");
+ return ret;
+}
+
+static int yellow_carp_mode_reset(struct smu_context *smu, int type)
+{
+ int ret = 0;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, type, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "Failed to mode reset!\n");
+
+ return ret;
+}
+
+static int yellow_carp_mode2_reset(struct smu_context *smu)
+{
+ return yellow_carp_mode_reset(smu, SMU_RESET_MODE_2);
+}
+
+
+static void yellow_carp_get_ss_power_percent(SmuMetrics_t *metrics,
+ uint32_t *apu_percent, uint32_t *dgpu_percent)
+{
+ uint32_t apu_boost = 0;
+ uint32_t dgpu_boost = 0;
+ uint16_t apu_limit = 0;
+ uint16_t dgpu_limit = 0;
+ uint16_t apu_power = 0;
+ uint16_t dgpu_power = 0;
+
+ /* APU and dGPU power values are reported in milli Watts
+ * and STAPM power limits are in Watts */
+ apu_power = metrics->ApuPower/1000;
+ apu_limit = metrics->StapmOpnLimit;
+ if (apu_power > apu_limit && apu_limit != 0)
+ apu_boost = ((apu_power - apu_limit) * 100) / apu_limit;
+ apu_boost = (apu_boost > 100) ? 100 : apu_boost;
+
+ dgpu_power = metrics->dGpuPower/1000;
+ if (metrics->StapmCurrentLimit > metrics->StapmOpnLimit)
+ dgpu_limit = metrics->StapmCurrentLimit - metrics->StapmOpnLimit;
+ if (dgpu_power > dgpu_limit && dgpu_limit != 0)
+ dgpu_boost = ((dgpu_power - dgpu_limit) * 100) / dgpu_limit;
+ dgpu_boost = (dgpu_boost > 100) ? 100 : dgpu_boost;
+
+ if (dgpu_boost >= apu_boost)
+ apu_boost = 0;
+ else
+ dgpu_boost = 0;
+
+ *apu_percent = apu_boost;
+ *dgpu_percent = dgpu_boost;
+
+}
+
+static int yellow_carp_get_smu_metrics_data(struct smu_context *smu,
+ MetricsMember_t member,
+ uint32_t *value)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ SmuMetrics_t *metrics = (SmuMetrics_t *)smu_table->metrics_table;
+ int ret = 0;
+ uint32_t apu_percent = 0;
+ uint32_t dgpu_percent = 0;
+
+ ret = smu_cmn_get_metrics_table(smu, NULL, false);
+ if (ret)
+ return ret;
+
+ switch (member) {
+ case METRICS_AVERAGE_GFXCLK:
+ *value = metrics->GfxclkFrequency;
+ break;
+ case METRICS_AVERAGE_SOCCLK:
+ *value = metrics->SocclkFrequency;
+ break;
+ case METRICS_AVERAGE_VCLK:
+ *value = metrics->VclkFrequency;
+ break;
+ case METRICS_AVERAGE_DCLK:
+ *value = metrics->DclkFrequency;
+ break;
+ case METRICS_AVERAGE_UCLK:
+ *value = metrics->MemclkFrequency;
+ break;
+ case METRICS_AVERAGE_GFXACTIVITY:
+ *value = metrics->GfxActivity / 100;
+ break;
+ case METRICS_AVERAGE_VCNACTIVITY:
+ *value = metrics->UvdActivity;
+ break;
+ case METRICS_AVERAGE_SOCKETPOWER:
+ *value = (metrics->CurrentSocketPower << 8) / 1000;
+ break;
+ case METRICS_TEMPERATURE_EDGE:
+ *value = metrics->GfxTemperature / 100 *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_TEMPERATURE_HOTSPOT:
+ *value = metrics->SocTemperature / 100 *
+ SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
+ break;
+ case METRICS_THROTTLER_STATUS:
+ *value = metrics->ThrottlerStatus;
+ break;
+ case METRICS_VOLTAGE_VDDGFX:
+ *value = metrics->Voltage[0];
+ break;
+ case METRICS_VOLTAGE_VDDSOC:
+ *value = metrics->Voltage[1];
+ break;
+ case METRICS_SS_APU_SHARE:
+ /* return the percentage of APU power boost
+ * with respect to APU's power limit.
+ */
+ yellow_carp_get_ss_power_percent(metrics, &apu_percent, &dgpu_percent);
+ *value = apu_percent;
+ break;
+ case METRICS_SS_DGPU_SHARE:
+ /* return the percentage of dGPU power boost
+ * with respect to dGPU's power limit.
+ */
+ yellow_carp_get_ss_power_percent(metrics, &apu_percent, &dgpu_percent);
+ *value = dgpu_percent;
+ break;
+ default:
+ *value = UINT_MAX;
+ break;
+ }
+
+ return ret;
+}
+
+static int yellow_carp_read_sensor(struct smu_context *smu,
+ enum amd_pp_sensors sensor,
+ void *data, uint32_t *size)
+{
+ int ret = 0;
+
+ if (!data || !size)
+ return -EINVAL;
+
+ switch (sensor) {
+ case AMDGPU_PP_SENSOR_GPU_LOAD:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXACTIVITY,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GPU_POWER:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_SOCKETPOWER,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_EDGE_TEMP:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_EDGE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_TEMPERATURE_HOTSPOT,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_MCLK:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_UCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_GFX_SCLK:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_AVERAGE_GFXCLK,
+ (uint32_t *)data);
+ *(uint32_t *)data *= 100;
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDGFX:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDGFX,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_VDDNB:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_VOLTAGE_VDDSOC,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_SS_APU_SHARE:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_SS_APU_SHARE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ case AMDGPU_PP_SENSOR_SS_DGPU_SHARE:
+ ret = yellow_carp_get_smu_metrics_data(smu,
+ METRICS_SS_DGPU_SHARE,
+ (uint32_t *)data);
+ *size = 4;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int yellow_carp_set_watermarks_table(struct smu_context *smu,
+ struct pp_smu_wm_range_sets *clock_ranges)
+{
+ int i;
+ int ret = 0;
+ Watermarks_t *table = smu->smu_table.watermarks_table;
+
+ if (!table || !clock_ranges)
+ return -EINVAL;
+
+ if (clock_ranges) {
+ if (clock_ranges->num_reader_wm_sets > NUM_WM_RANGES ||
+ clock_ranges->num_writer_wm_sets > NUM_WM_RANGES)
+ return -EINVAL;
+
+ for (i = 0; i < clock_ranges->num_reader_wm_sets; i++) {
+ table->WatermarkRow[WM_DCFCLK][i].MinClock =
+ clock_ranges->reader_wm_sets[i].min_drain_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MaxClock =
+ clock_ranges->reader_wm_sets[i].max_drain_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MinMclk =
+ clock_ranges->reader_wm_sets[i].min_fill_clk_mhz;
+ table->WatermarkRow[WM_DCFCLK][i].MaxMclk =
+ clock_ranges->reader_wm_sets[i].max_fill_clk_mhz;
+
+ table->WatermarkRow[WM_DCFCLK][i].WmSetting =
+ clock_ranges->reader_wm_sets[i].wm_inst;
+ }
+
+ for (i = 0; i < clock_ranges->num_writer_wm_sets; i++) {
+ table->WatermarkRow[WM_SOCCLK][i].MinClock =
+ clock_ranges->writer_wm_sets[i].min_fill_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MaxClock =
+ clock_ranges->writer_wm_sets[i].max_fill_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MinMclk =
+ clock_ranges->writer_wm_sets[i].min_drain_clk_mhz;
+ table->WatermarkRow[WM_SOCCLK][i].MaxMclk =
+ clock_ranges->writer_wm_sets[i].max_drain_clk_mhz;
+
+ table->WatermarkRow[WM_SOCCLK][i].WmSetting =
+ clock_ranges->writer_wm_sets[i].wm_inst;
+ }
+
+ smu->watermarks_bitmap |= WATERMARKS_EXIST;
+ }
+
+ /* pass data to smu controller */
+ if ((smu->watermarks_bitmap & WATERMARKS_EXIST) &&
+ !(smu->watermarks_bitmap & WATERMARKS_LOADED)) {
+ ret = smu_cmn_write_watermarks_table(smu);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to update WMTABLE!");
+ return ret;
+ }
+ smu->watermarks_bitmap |= WATERMARKS_LOADED;
+ }
+
+ return 0;
+}
+
+static ssize_t yellow_carp_get_gpu_metrics(struct smu_context *smu,
+ void **table)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct gpu_metrics_v2_1 *gpu_metrics =
+ (struct gpu_metrics_v2_1 *)smu_table->gpu_metrics_table;
+ SmuMetrics_t metrics;
+ int ret = 0;
+
+ ret = smu_cmn_get_metrics_table(smu, &metrics, true);
+ if (ret)
+ return ret;
+
+ smu_cmn_init_soft_gpu_metrics(gpu_metrics, 2, 1);
+
+ gpu_metrics->temperature_gfx = metrics.GfxTemperature;
+ gpu_metrics->temperature_soc = metrics.SocTemperature;
+ memcpy(&gpu_metrics->temperature_core[0],
+ &metrics.CoreTemperature[0],
+ sizeof(uint16_t) * 8);
+ gpu_metrics->temperature_l3[0] = metrics.L3Temperature;
+
+ gpu_metrics->average_gfx_activity = metrics.GfxActivity;
+ gpu_metrics->average_mm_activity = metrics.UvdActivity;
+
+ gpu_metrics->average_socket_power = metrics.CurrentSocketPower;
+ gpu_metrics->average_gfx_power = metrics.Power[0];
+ gpu_metrics->average_soc_power = metrics.Power[1];
+ memcpy(&gpu_metrics->average_core_power[0],
+ &metrics.CorePower[0],
+ sizeof(uint16_t) * 8);
+
+ gpu_metrics->average_gfxclk_frequency = metrics.GfxclkFrequency;
+ gpu_metrics->average_socclk_frequency = metrics.SocclkFrequency;
+ gpu_metrics->average_uclk_frequency = metrics.MemclkFrequency;
+ gpu_metrics->average_fclk_frequency = metrics.MemclkFrequency;
+ gpu_metrics->average_vclk_frequency = metrics.VclkFrequency;
+ gpu_metrics->average_dclk_frequency = metrics.DclkFrequency;
+
+ memcpy(&gpu_metrics->current_coreclk[0],
+ &metrics.CoreFrequency[0],
+ sizeof(uint16_t) * 8);
+ gpu_metrics->current_l3clk[0] = metrics.L3Frequency;
+
+ gpu_metrics->throttle_status = metrics.ThrottlerStatus;
+
+ gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
+
+ *table = (void *)gpu_metrics;
+
+ return sizeof(struct gpu_metrics_v2_1);
+}
+
+/**
+ * yellow_carp_get_gfxoff_status - get gfxoff status
+ *
+ * @smu: smu_context pointer
+ *
+ * This function will be used to get gfxoff status
+ *
+ * Returns 0=GFXOFF(default).
+ * Returns 1=Transition out of GFX State.
+ * Returns 2=Not in GFXOFF.
+ * Returns 3=Transition into GFXOFF.
+ */
+static uint32_t yellow_carp_get_gfxoff_status(struct smu_context *smu)
+{
+ uint32_t reg;
+ uint32_t gfxoff_status = 0;
+ struct amdgpu_device *adev = smu->adev;
+
+ reg = RREG32_SOC15(SMUIO, 0, regSMUIO_GFX_MISC_CNTL);
+ gfxoff_status = (reg & SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK)
+ >> SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS__SHIFT;
+
+ return gfxoff_status;
+}
+
+static int yellow_carp_set_default_dpm_tables(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+
+ return smu_cmn_update_table(smu, SMU_TABLE_DPMCLOCKS, 0, smu_table->clocks_table, false);
+}
+
+static int yellow_carp_od_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type,
+ long input[], uint32_t size)
+{
+ struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
+ int ret = 0;
+
+ /* Only allowed in manual mode */
+ if (smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+ return -EINVAL;
+
+ switch (type) {
+ case PP_OD_EDIT_SCLK_VDDC_TABLE:
+ if (size != 2) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+
+ if (input[0] == 0) {
+ if (input[1] < smu->gfx_default_hard_min_freq) {
+ dev_warn(smu->adev->dev,
+ "Fine grain setting minimum sclk (%ld) MHz is less than the minimum allowed (%d) MHz\n",
+ input[1], smu->gfx_default_hard_min_freq);
+ return -EINVAL;
+ }
+ smu->gfx_actual_hard_min_freq = input[1];
+ } else if (input[0] == 1) {
+ if (input[1] > smu->gfx_default_soft_max_freq) {
+ dev_warn(smu->adev->dev,
+ "Fine grain setting maximum sclk (%ld) MHz is greater than the maximum allowed (%d) MHz\n",
+ input[1], smu->gfx_default_soft_max_freq);
+ return -EINVAL;
+ }
+ smu->gfx_actual_soft_max_freq = input[1];
+ } else {
+ return -EINVAL;
+ }
+ break;
+ case PP_OD_RESTORE_DEFAULT_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ } else {
+ smu->gfx_actual_hard_min_freq = smu->gfx_default_hard_min_freq;
+ smu->gfx_actual_soft_max_freq = smu->gfx_default_soft_max_freq;
+ }
+ break;
+ case PP_OD_COMMIT_DPM_TABLE:
+ if (size != 0) {
+ dev_err(smu->adev->dev, "Input parameter number not correct\n");
+ return -EINVAL;
+ } else {
+ if (smu->gfx_actual_hard_min_freq > smu->gfx_actual_soft_max_freq) {
+ dev_err(smu->adev->dev,
+ "The setting minimum sclk (%d) MHz is greater than the setting maximum sclk (%d) MHz\n",
+ smu->gfx_actual_hard_min_freq,
+ smu->gfx_actual_soft_max_freq);
+ return -EINVAL;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinGfxClk,
+ smu->gfx_actual_hard_min_freq, NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Set hard min sclk failed!");
+ return ret;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetSoftMaxGfxClk,
+ smu->gfx_actual_soft_max_freq, NULL);
+ if (ret) {
+ dev_err(smu->adev->dev, "Set soft max sclk failed!");
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return ret;
+}
+
+static int yellow_carp_get_current_clk_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *value)
+{
+ MetricsMember_t member_type;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ member_type = METRICS_AVERAGE_SOCCLK;
+ break;
+ case SMU_VCLK:
+ member_type = METRICS_AVERAGE_VCLK;
+ break;
+ case SMU_DCLK:
+ member_type = METRICS_AVERAGE_DCLK;
+ break;
+ case SMU_MCLK:
+ member_type = METRICS_AVERAGE_UCLK;
+ break;
+ case SMU_FCLK:
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetFclkFrequency, 0, value);
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetGfxclkFrequency, 0, value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return yellow_carp_get_smu_metrics_data(smu, member_type, value);
+}
+
+static int yellow_carp_get_dpm_level_count(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *count)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ *count = clk_table->NumSocClkLevelsEnabled;
+ break;
+ case SMU_VCLK:
+ *count = clk_table->VcnClkLevelsEnabled;
+ break;
+ case SMU_DCLK:
+ *count = clk_table->VcnClkLevelsEnabled;
+ break;
+ case SMU_MCLK:
+ *count = clk_table->NumDfPstatesEnabled;
+ break;
+ case SMU_FCLK:
+ *count = clk_table->NumDfPstatesEnabled;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int yellow_carp_get_dpm_freq_by_index(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t dpm_level,
+ uint32_t *freq)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ if (!clk_table || clk_type >= SMU_CLK_COUNT)
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ if (dpm_level >= clk_table->NumSocClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->SocClocks[dpm_level];
+ break;
+ case SMU_VCLK:
+ if (dpm_level >= clk_table->VcnClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->VClocks[dpm_level];
+ break;
+ case SMU_DCLK:
+ if (dpm_level >= clk_table->VcnClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->DClocks[dpm_level];
+ break;
+ case SMU_UCLK:
+ case SMU_MCLK:
+ if (dpm_level >= clk_table->NumDfPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->DfPstateTable[dpm_level].MemClk;
+ break;
+ case SMU_FCLK:
+ if (dpm_level >= clk_table->NumDfPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->DfPstateTable[dpm_level].FClk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool yellow_carp_clk_dpm_is_enabled(struct smu_context *smu,
+ enum smu_clk_type clk_type)
+{
+ enum smu_feature_mask feature_id = 0;
+
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ feature_id = SMU_FEATURE_DPM_FCLK_BIT;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ feature_id = SMU_FEATURE_DPM_GFXCLK_BIT;
+ break;
+ case SMU_SOCCLK:
+ feature_id = SMU_FEATURE_DPM_SOCCLK_BIT;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ feature_id = SMU_FEATURE_VCN_DPM_BIT;
+ break;
+ default:
+ return true;
+ }
+
+ return smu_cmn_feature_is_enabled(smu, feature_id);
+}
+
+static int yellow_carp_get_dpm_ultimate_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *min,
+ uint32_t *max)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+ uint32_t clock_limit;
+ uint32_t max_dpm_level, min_dpm_level;
+ int ret = 0;
+
+ if (!yellow_carp_clk_dpm_is_enabled(smu, clk_type)) {
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ clock_limit = smu->smu_table.boot_values.uclk;
+ break;
+ case SMU_FCLK:
+ clock_limit = smu->smu_table.boot_values.fclk;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ clock_limit = smu->smu_table.boot_values.gfxclk;
+ break;
+ case SMU_SOCCLK:
+ clock_limit = smu->smu_table.boot_values.socclk;
+ break;
+ case SMU_VCLK:
+ clock_limit = smu->smu_table.boot_values.vclk;
+ break;
+ case SMU_DCLK:
+ clock_limit = smu->smu_table.boot_values.dclk;
+ break;
+ default:
+ clock_limit = 0;
+ break;
+ }
+
+ /* clock in Mhz unit */
+ if (min)
+ *min = clock_limit / 100;
+ if (max)
+ *max = clock_limit / 100;
+
+ return 0;
+ }
+
+ if (max) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *max = clk_table->MaxGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ max_dpm_level = 0;
+ break;
+ case SMU_SOCCLK:
+ max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ max_dpm_level = clk_table->VcnClkLevelsEnabled - 1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, max_dpm_level, max);
+ if (ret)
+ goto failed;
+ }
+ }
+
+ if (min) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *min = clk_table->MinGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ case SMU_FCLK:
+ min_dpm_level = clk_table->NumDfPstatesEnabled - 1;
+ break;
+ case SMU_SOCCLK:
+ min_dpm_level = 0;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ min_dpm_level = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, min_dpm_level, min);
+ if (ret)
+ goto failed;
+ }
+ }
+
+failed:
+ return ret;
+}
+
+static int yellow_carp_set_soft_freq_limited_range(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t min,
+ uint32_t max)
+{
+ enum smu_message_type msg_set_min, msg_set_max;
+ int ret = 0;
+
+ if (!yellow_carp_clk_dpm_is_enabled(smu, clk_type))
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ msg_set_min = SMU_MSG_SetHardMinGfxClk;
+ msg_set_max = SMU_MSG_SetSoftMaxGfxClk;
+ break;
+ case SMU_FCLK:
+ msg_set_min = SMU_MSG_SetHardMinFclkByFreq;
+ msg_set_max = SMU_MSG_SetSoftMaxFclkByFreq;
+ break;
+ case SMU_SOCCLK:
+ msg_set_min = SMU_MSG_SetHardMinSocclkByFreq;
+ msg_set_max = SMU_MSG_SetSoftMaxSocclkByFreq;
+ break;
+ case SMU_VCLK:
+ case SMU_DCLK:
+ msg_set_min = SMU_MSG_SetHardMinVcn;
+ msg_set_max = SMU_MSG_SetSoftMaxVcn;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, msg_set_min, min, NULL);
+ if (ret)
+ goto out;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, msg_set_max, max, NULL);
+ if (ret)
+ goto out;
+
+out:
+ return ret;
+}
+
+static int yellow_carp_print_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type, char *buf)
+{
+ int i, size = 0, ret = 0;
+ uint32_t cur_value = 0, value = 0, count = 0;
+ uint32_t min, max;
+
+ smu_cmn_get_sysfs_buf(&buf, &size);
+
+ switch (clk_type) {
+ case SMU_OD_SCLK:
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_SCLK");
+ size += sysfs_emit_at(buf, size, "0: %10uMhz\n",
+ (smu->gfx_actual_hard_min_freq > 0) ? smu->gfx_actual_hard_min_freq : smu->gfx_default_hard_min_freq);
+ size += sysfs_emit_at(buf, size, "1: %10uMhz\n",
+ (smu->gfx_actual_soft_max_freq > 0) ? smu->gfx_actual_soft_max_freq : smu->gfx_default_soft_max_freq);
+ break;
+ case SMU_OD_RANGE:
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
+ size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
+ smu->gfx_default_hard_min_freq, smu->gfx_default_soft_max_freq);
+ break;
+ case SMU_SOCCLK:
+ case SMU_VCLK:
+ case SMU_DCLK:
+ case SMU_MCLK:
+ case SMU_FCLK:
+ ret = yellow_carp_get_current_clk_freq(smu, clk_type, &cur_value);
+ if (ret)
+ goto print_clk_out;
+
+ ret = yellow_carp_get_dpm_level_count(smu, clk_type, &count);
+ if (ret)
+ goto print_clk_out;
+
+ for (i = 0; i < count; i++) {
+ ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, i, &value);
+ if (ret)
+ goto print_clk_out;
+
+ size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, value,
+ cur_value == value ? "*" : "");
+ }
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ ret = yellow_carp_get_current_clk_freq(smu, clk_type, &cur_value);
+ if (ret)
+ goto print_clk_out;
+ min = (smu->gfx_actual_hard_min_freq > 0) ? smu->gfx_actual_hard_min_freq : smu->gfx_default_hard_min_freq;
+ max = (smu->gfx_actual_soft_max_freq > 0) ? smu->gfx_actual_soft_max_freq : smu->gfx_default_soft_max_freq;
+ if (cur_value == max)
+ i = 2;
+ else if (cur_value == min)
+ i = 0;
+ else
+ i = 1;
+ size += sysfs_emit_at(buf, size, "0: %uMhz %s\n", min,
+ i == 0 ? "*" : "");
+ size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
+ i == 1 ? cur_value : YELLOW_CARP_UMD_PSTATE_GFXCLK,
+ i == 1 ? "*" : "");
+ size += sysfs_emit_at(buf, size, "2: %uMhz %s\n", max,
+ i == 2 ? "*" : "");
+ break;
+ default:
+ break;
+ }
+
+print_clk_out:
+ return size;
+}
+
+static int yellow_carp_force_clk_levels(struct smu_context *smu,
+ enum smu_clk_type clk_type, uint32_t mask)
+{
+ uint32_t soft_min_level = 0, soft_max_level = 0;
+ uint32_t min_freq = 0, max_freq = 0;
+ int ret = 0;
+
+ soft_min_level = mask ? (ffs(mask) - 1) : 0;
+ soft_max_level = mask ? (fls(mask) - 1) : 0;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ case SMU_FCLK:
+ case SMU_VCLK:
+ case SMU_DCLK:
+ ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, soft_min_level, &min_freq);
+ if (ret)
+ goto force_level_out;
+
+ ret = yellow_carp_get_dpm_freq_by_index(smu, clk_type, soft_max_level, &max_freq);
+ if (ret)
+ goto force_level_out;
+
+ ret = yellow_carp_set_soft_freq_limited_range(smu, clk_type, min_freq, max_freq);
+ if (ret)
+ goto force_level_out;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+force_level_out:
+ return ret;
+}
+
+static int yellow_carp_set_performance_level(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct amdgpu_device *adev = smu->adev;
+ uint32_t sclk_min = 0, sclk_max = 0;
+ uint32_t fclk_min = 0, fclk_max = 0;
+ uint32_t socclk_min = 0, socclk_max = 0;
+ int ret = 0;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_HIGH:
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_SCLK, NULL, &sclk_max);
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_FCLK, NULL, &fclk_max);
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_SOCCLK, NULL, &socclk_max);
+ sclk_min = sclk_max;
+ fclk_min = fclk_max;
+ socclk_min = socclk_max;
+ break;
+ case AMD_DPM_FORCED_LEVEL_LOW:
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_SCLK, &sclk_min, NULL);
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_FCLK, &fclk_min, NULL);
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_SOCCLK, &socclk_min, NULL);
+ sclk_max = sclk_min;
+ fclk_max = fclk_min;
+ socclk_max = socclk_min;
+ break;
+ case AMD_DPM_FORCED_LEVEL_AUTO:
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_SCLK, &sclk_min, &sclk_max);
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_FCLK, &fclk_min, &fclk_max);
+ yellow_carp_get_dpm_ultimate_freq(smu, SMU_SOCCLK, &socclk_min, &socclk_max);
+ break;
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ /* Temporarily do nothing since the optimal clocks haven't been provided yet */
+ break;
+ case AMD_DPM_FORCED_LEVEL_MANUAL:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
+ return 0;
+ default:
+ dev_err(adev->dev, "Invalid performance level %d\n", level);
+ return -EINVAL;
+ }
+
+ if (sclk_min && sclk_max) {
+ ret = yellow_carp_set_soft_freq_limited_range(smu,
+ SMU_SCLK,
+ sclk_min,
+ sclk_max);
+ if (ret)
+ return ret;
+
+ smu->gfx_actual_hard_min_freq = sclk_min;
+ smu->gfx_actual_soft_max_freq = sclk_max;
+ }
+
+ if (fclk_min && fclk_max) {
+ ret = yellow_carp_set_soft_freq_limited_range(smu,
+ SMU_FCLK,
+ fclk_min,
+ fclk_max);
+ if (ret)
+ return ret;
+ }
+
+ if (socclk_min && socclk_max) {
+ ret = yellow_carp_set_soft_freq_limited_range(smu,
+ SMU_SOCCLK,
+ socclk_min,
+ socclk_max);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int yellow_carp_set_fine_grain_gfx_freq_parameters(struct smu_context *smu)
+{
+ DpmClocks_t *clk_table = smu->smu_table.clocks_table;
+
+ smu->gfx_default_hard_min_freq = clk_table->MinGfxClk;
+ smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk;
+ smu->gfx_actual_hard_min_freq = 0;
+ smu->gfx_actual_soft_max_freq = 0;
+
+ return 0;
+}
+
+static const struct pptable_funcs yellow_carp_ppt_funcs = {
+ .check_fw_status = smu_v13_0_check_fw_status,
+ .check_fw_version = smu_v13_0_check_fw_version,
+ .init_smc_tables = yellow_carp_init_smc_tables,
+ .fini_smc_tables = yellow_carp_fini_smc_tables,
+ .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values,
+ .system_features_control = yellow_carp_system_features_control,
+ .send_smc_msg_with_param = smu_cmn_send_smc_msg_with_param,
+ .send_smc_msg = smu_cmn_send_smc_msg,
+ .dpm_set_vcn_enable = yellow_carp_dpm_set_vcn_enable,
+ .dpm_set_jpeg_enable = yellow_carp_dpm_set_jpeg_enable,
+ .set_default_dpm_table = yellow_carp_set_default_dpm_tables,
+ .read_sensor = yellow_carp_read_sensor,
+ .is_dpm_running = yellow_carp_is_dpm_running,
+ .set_watermarks_table = yellow_carp_set_watermarks_table,
+ .get_gpu_metrics = yellow_carp_get_gpu_metrics,
+ .get_enabled_mask = smu_cmn_get_enabled_mask,
+ .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
+ .set_driver_table_location = smu_v13_0_set_driver_table_location,
+ .gfx_off_control = smu_v13_0_gfx_off_control,
+ .get_gfx_off_status = yellow_carp_get_gfxoff_status,
+ .post_init = yellow_carp_post_smu_init,
+ .mode2_reset = yellow_carp_mode2_reset,
+ .get_dpm_ultimate_freq = yellow_carp_get_dpm_ultimate_freq,
+ .od_edit_dpm_table = yellow_carp_od_edit_dpm_table,
+ .print_clk_levels = yellow_carp_print_clk_levels,
+ .force_clk_levels = yellow_carp_force_clk_levels,
+ .set_performance_level = yellow_carp_set_performance_level,
+ .set_fine_grain_gfx_freq_parameters = yellow_carp_set_fine_grain_gfx_freq_parameters,
+};
+
+void yellow_carp_set_ppt_funcs(struct smu_context *smu)
+{
+ smu->ppt_funcs = &yellow_carp_ppt_funcs;
+ smu->message_map = yellow_carp_message_map;
+ smu->feature_map = yellow_carp_feature_mask_map;
+ smu->table_map = yellow_carp_table_map;
+ smu->is_apu = true;
+ smu_v13_0_set_smu_mailbox_registers(smu);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.h
new file mode 100644
index 000000000..a9205a8ea
--- /dev/null
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __YELLOW_CARP_PPT_H__
+#define __YELLOW_CARP_PPT_H__
+
+extern void yellow_carp_set_ppt_funcs(struct smu_context *smu);
+#define YELLOW_CARP_UMD_PSTATE_GFXCLK 1100
+
+#endif