#!/usr/bin/env python3
#
# Copyright (C) Advanced Micro Devices. All rights reserved.
#
# 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 AUTHORS OR
# COPYRIGHT HOLDERS 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.

import json
import logging
import math
import sys

from amdsmi_cli_exceptions import AmdSmiRequiredCommandException

from amdsmi import amdsmi_exception, amdsmi_interface
from amdsmi.amdsmi_interface import AMDSMI_MAX_PPT_LIMIT, AMDSMI_MAX_UTIL


class SetValueCommands:
    def set_core(
        self,
        args,
        multiple_devices=False,
        core=None,
        core_boost_limit=None,
        core_floor_limit=None,
        core_msr_floor_limit=None,
    ):
        """Issue set commands to target core(s)

        Args:
            args (Namespace): Namespace containing the parsed CLI args
            multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False.
            core (device_handle, optional): device_handle for target device. Defaults to None.
            core_boost_limit (list, optional): Value override for args.core_boost_limit. Defaults to None.
            core_floor_limit (list, optional): Value override for args.core_floor_limit. Defaults to None.
            core_msr_floor_limit (list, optional): Value override for args.core_msr_floor_limit. Defaults to None.

        Raises:
            ValueError: Value error if no core value is provided
            IndexError: Index error if core list is empty

        Return:
            Nothing
        """
        if core:
            args.core = core
        if core_boost_limit:
            args.core_boost_limit = core_boost_limit
        if core_floor_limit:
            args.core_floor_limit = core_floor_limit
        if core_msr_floor_limit:
            args.core_msr_floor_limit = core_msr_floor_limit

        if args.core == None:
            raise ValueError("No Core provided, specific Core targets(S) are needed")

        # Handle multiple cores
        handled_multiple_cores, device_handle = self.helpers.handle_cores(
            args, self.logger, self.set_core
        )
        if handled_multiple_cores:
            return  # This function is recursive

        # Error if no subcommand args are passed
        if not any([args.core_boost_limit, args.core_floor_limit, args.core_msr_floor_limit]):
            command = " ".join(sys.argv[1:])
            raise AmdSmiRequiredCommandException(command, self.logger.format)

        args.core = device_handle
        # build core string for errors
        try:
            core_id = self.helpers.get_core_id_from_device_handle(args.core)
        except IndexError:
            core_id = f"ID Unavailable for {args.core}"

        static_dict = {}
        if args.core_boost_limit:
            static_dict["set_core_boost_limit"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_core_boostlimit(
                    args.core, args.core_boost_limit[0][0]
                )
                # Verify the core boost limit is set
                boost_limit = amdsmi_interface.amdsmi_get_cpu_core_boostlimit(args.core)
                # Extract numeric value from response (remove units if present)
                if isinstance(boost_limit, str):
                    # Extract just the number part (assumes format like "5000 MHz" or "5000")
                    boost_limit = int(boost_limit.split()[0])
                else:
                    boost_limit = int(boost_limit)

                if boost_limit < args.core_boost_limit[0][0]:
                    static_dict["set_core_boost_limit"]["Response"] = (
                        f"Max allowed boostlimit is {boost_limit} MHz"
                    )
                elif boost_limit > args.core_boost_limit[0][0]:
                    static_dict["set_core_boost_limit"]["Response"] = (
                        f"Min allowed boostlimit is {boost_limit} MHz"
                    )
                else:
                    static_dict["set_core_boost_limit"]["Response"] = f"{boost_limit} MHz"
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_core_boost_limit"]["Response"] = (
                    f"Error occurred for Core {core_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set core boost limit for core %s | %s", core_id, e.get_error_info()
                )

        # Set core floor limit
        if args.core_floor_limit:
            static_dict["floor_limit"] = {}
            try:
                core_floor_limit = args.core_floor_limit[0][0]
                amdsmi_interface.amdsmi_set_cpu_core_floor_freq_limit(args.core, core_floor_limit)

                # Verify the core boost limit is set
                flimit = amdsmi_interface.amdsmi_get_cpu_core_floor_freq_limit(args.core)
                freq_range = amdsmi_interface.amdsmi_get_cpu_freq_range()
                fmax = freq_range["fmax"]
                fmin = freq_range["fmin"]

                if flimit < core_floor_limit:
                    if fmax and flimit != fmax:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful"
                        )
                    else:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful. Max allowed cpu floor limit is {flimit} MHz"
                        )
                elif flimit > core_floor_limit:
                    if fmin and flimit != fmin:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful"
                        )
                    else:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful. Min allowed cpu floor limit is {flimit} MHz"
                        )
                else:
                    static_dict["floor_limit"]["Response"] = f"Set, VALUE: {flimit} MHz, successful"
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["floor_limit"]["Response"] = (
                    f"Error occurred for Core {core_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set core floor limit for core %s | %s", core_id, e.get_error_info()
                )

        # Set core MSR floor limit
        if args.core_msr_floor_limit:
            static_dict["msr_floor_limit"] = {}
            try:
                msr_floor_limit = args.core_msr_floor_limit[0][0]
                amdsmi_interface.amdsmi_set_cpu_core_msr_floor_freq_limit(
                    args.core, msr_floor_limit
                )
                # Verify the core msr floor limit is set
                effflimit = amdsmi_interface.amdsmi_get_cpu_core_eff_floor_freq_limit(args.core)
                if effflimit == 0:
                    effflimit = msr_floor_limit
                freq_range = amdsmi_interface.amdsmi_get_cpu_freq_range()
                fmax = freq_range["fmax"]
                fmin = freq_range["fmin"]

                if effflimit < msr_floor_limit:
                    if fmax and effflimit != fmax:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful"
                        )
                    else:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful. Max allowed msr cpu floor limit is {effflimit} MHz"
                        )
                elif effflimit > msr_floor_limit:
                    if fmin and effflimit != fmin:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful"
                        )
                    else:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful. Min allowed msr cpu floor limit is {effflimit} MHz"
                        )
                else:
                    static_dict["msr_floor_limit"]["Response"] = (
                        f"Set, VALUE: {effflimit} MHz, successful"
                    )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["msr_floor_limit"]["Response"] = (
                    f"Error occurred for Core {core_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set core MSR floor limit for core %s | %s",
                    core_id,
                    e.get_error_info(),
                )

        multiple_devices_csv_override = False
        self.logger.store_core_output(args.core, "values", static_dict)
        if multiple_devices:
            self.logger.store_multiple_device_output()
            return  # Skip printing when there are multiple devices
        self.logger.print_output(multiple_device_enabled=multiple_devices_csv_override)

    def set_cpu(
        self,
        args,
        multiple_devices=False,
        cpu=None,
        cpu_pwr_limit=None,
        cpu_xgmi_link_width=None,
        cpu_lclk_dpm_level=None,
        cpu_pwr_eff_mode=None,
        cpu_gmi3_link_width=None,
        cpu_pcie_link_rate=None,
        cpu_df_pstate_range=None,
        cpu_enable_apb=None,
        cpu_disable_apb=None,
        soc_boost_limit=None,
        cpu_xgmi_pstate_range=None,
        cpu_railisofreq_policy=None,
        cpu_dfcstate_ctrl=None,
        cpu_pc6_enable=None,
        cpu_cc6_enable=None,
        cpu_floor_limit=None,
        cpu_msr_floor_limit=None,
        cpu_dimm_sb_reg=None,
        cpu_sdps_limit=None,
    ):
        """Issue set commands to target cpu(s)

        Args:
            args (Namespace): Namespace containing the parsed CLI args
            multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False.
            cpu (cpu_handle, optional): device_handle for target device. Defaults to None.
            cpu_pwr_limit (int, optional): Value override for args.cpu_pwr_limit. Defaults to None.
            cpu_xgmi_link_width (List[int], optional): Value override for args.cpu_xgmi_link_width. Defaults to None.
            cpu_lclk_dpm_level (List[int], optional): Value override for args.cpu_lclk_dpm_level. Defaults to None.
            cpu_pwr_eff_mode (List[int], optional): Value override for args.cpu_pwr_eff_mode [mode, util, ppt_limit]. Defaults to None.
            cpu_gmi3_link_width (List[int], optional): Value override for args.cpu_gmi3_link_width. Defaults to None.
            cpu_pcie_link_rate (int, optional): Value override for args.cpu_pcie_link_rate. Defaults to None.
            cpu_df_pstate_range (List[int], optional): Value override for args.cpu_df_pstate_range. Defaults to None.
            cpu_enable_apb (bool, optional): Value override for args.cpu_enable_apb. Defaults to None.
            cpu_disable_apb (int, optional): Value override for args.cpu_disable_apb. Defaults to None.
            soc_boost_limit (int, optional): Value override for args.soc_boost_limit. Defaults to None.
            cpu_xgmi_pstate_range (List[int], optional): Value override for args.cpu_xgmi_pstate_range. Defaults to None.
            cpu_railisofreq_policy (int, optional): Value override for args.cpu_railisofreq_policy. Defaults to None.
            cpu_dfcstate_ctrl (int, optional): Value override for args.cpu_dfcstate_ctrl. Defaults to None.
            cpu_pc6_enable (int, optional): Value override for args.cpu_pc6_enable. Defaults to None.
            cpu_cc6_enable (int, optional): Value override for args.cpu_cc6_enable. Defaults to None.
            cpu_floor_limit (int, optional): Value override for args.cpu_floor_limit. Defaults to None.
            cpu_msr_floor_limit (int, optional): Value override for args.cpu_msr_floor_limit. Defaults to None.
            cpu_dimm_sb_reg (list, optional): DIMM sideband register write parameters [dimm_addr, lid, reg_offset, reg_space, write_data] for write operation. Value override for args.cpu_dimm_sb_reg. Defaults to None.
            cpu_sdps_limit (int, optional): Value override for args.cpu_sdps_limit. Defaults to None.

        Raises:
            ValueError: Value error if no cpu value is provided
            IndexError: Index error if cpu list is empty

        Return:
            Nothing
        """
        if cpu:
            args.cpu = cpu
        if cpu_pwr_limit:
            args.cpu_pwr_limit = cpu_pwr_limit
        if cpu_xgmi_link_width:
            args.cpu_xgmi_link_width = cpu_xgmi_link_width
        if cpu_lclk_dpm_level:
            args.cpu_lclk_dpm_level = cpu_lclk_dpm_level
        if cpu_pwr_eff_mode:
            args.cpu_pwr_eff_mode = cpu_pwr_eff_mode
        if cpu_gmi3_link_width:
            args.cpu_gmi3_link_width = cpu_gmi3_link_width
        if cpu_pcie_link_rate:
            args.cpu_pcie_link_rate = cpu_pcie_link_rate
        if cpu_df_pstate_range:
            args.cpu_df_pstate_range = cpu_df_pstate_range
        if cpu_enable_apb:
            args.cpu_enable_apb = cpu_enable_apb
        if cpu_disable_apb:
            args.cpu_disable_apb = cpu_disable_apb
        if soc_boost_limit:
            args.soc_boost_limit = soc_boost_limit
        if cpu_xgmi_pstate_range:
            args.cpu_xgmi_pstate_range = cpu_xgmi_pstate_range
        if cpu_railisofreq_policy:
            args.cpu_railisofreq_policy = cpu_railisofreq_policy
        if cpu_dfcstate_ctrl:
            args.cpu_dfcstate_ctrl = cpu_dfcstate_ctrl
        if cpu_pc6_enable:
            args.cpu_pc6_enable = cpu_pc6_enable
        if cpu_cc6_enable:
            args.cpu_cc6_enable = cpu_cc6_enable
        if cpu_floor_limit:
            args.cpu_floor_limit = cpu_floor_limit
        if cpu_msr_floor_limit:
            args.cpu_msr_floor_limit = cpu_msr_floor_limit
        if cpu_dimm_sb_reg:
            args.cpu_dimm_sb_reg = cpu_dimm_sb_reg
        if cpu_sdps_limit:
            args.cpu_sdps_limit = cpu_sdps_limit

        if args.cpu == None:
            raise ValueError("No CPU provided, specific CPU targets(S) are needed")

        # Handle multiple CPU's
        handled_multiple_cpus, device_handle = self.helpers.handle_cpus(
            args, self.logger, self.set_cpu
        )
        if handled_multiple_cpus:
            return  # This function is recursive

        args.cpu = device_handle
        # Error if no subcommand args are passed
        if not any(
            [
                args.cpu_pwr_limit,
                args.cpu_xgmi_link_width,
                args.cpu_lclk_dpm_level,
                args.cpu_pwr_eff_mode,
                args.cpu_gmi3_link_width,
                args.cpu_pcie_link_rate,
                args.cpu_df_pstate_range,
                args.cpu_enable_apb,
                args.cpu_disable_apb,
                args.soc_boost_limit,
                args.cpu_xgmi_pstate_range,
                args.cpu_railisofreq_policy,
                args.cpu_dfcstate_ctrl,
                args.cpu_pc6_enable,
                args.cpu_cc6_enable,
                args.cpu_floor_limit,
                args.cpu_msr_floor_limit,
                args.cpu_dimm_sb_reg,
                args.cpu_sdps_limit,
            ]
        ):
            command = " ".join(sys.argv[1:])
            raise AmdSmiRequiredCommandException(command, self.logger.format)

        # Build CPU string for errors
        try:
            cpu_id = self.helpers.get_cpu_id_from_device_handle(args.cpu)
        except IndexError:
            cpu_id = f"ID Unavailable for {args.cpu}"

        static_dict = {}

        if args.cpu_pwr_limit:
            static_dict["set_pwr_limit"] = {}
            try:
                # max is returned in mW, the same unit as the requested limit
                max_power = amdsmi_interface.amdsmi_get_cpu_socket_power_cap_max(args.cpu)
                if args.cpu_pwr_limit[0][0] > max_power:
                    args.cpu_pwr_limit[0][0] = max_power
                    static_dict["set_pwr_limit"]["Warning"] = (
                        f"requested power limit exceeds maximum of {max_power} mW; setting to {max_power} mW instead"
                    )

                amdsmi_interface.amdsmi_set_cpu_socket_power_cap(args.cpu, args.cpu_pwr_limit[0][0])
                static_dict["set_pwr_limit"]["Response"] = (
                    f"{args.cpu_pwr_limit[0][0] / 1000:.3f} W"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_pwr_limit"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set power limit for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_xgmi_link_width:
            static_dict["set_xgmi_link_width"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_xgmi_width(
                    args.cpu, args.cpu_xgmi_link_width[0][0], args.cpu_xgmi_link_width[0][1]
                )
                static_dict["set_xgmi_link_width"]["Response"] = (
                    f"{args.cpu_xgmi_link_width[0][0]} - {args.cpu_xgmi_link_width[0][1]}"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_xgmi_link_width"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set xgmi link width for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_lclk_dpm_level:
            static_dict["set_lclk_dpm_level"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_socket_lclk_dpm_level(
                    args.cpu,
                    args.cpu_lclk_dpm_level[0][0],
                    args.cpu_lclk_dpm_level[0][1],
                    args.cpu_lclk_dpm_level[0][2],
                )
                static_dict["set_lclk_dpm_level"]["Response"] = (
                    f"NBIO[{args.cpu_lclk_dpm_level[0][0]}]"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_lclk_dpm_level"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set lclk dpm level for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_pwr_eff_mode:
            static_dict["pwr_eff_mode"] = {}
            try:
                mode = args.cpu_pwr_eff_mode[0][0]
                util = (
                    args.cpu_pwr_eff_mode[0][1]
                    if len(args.cpu_pwr_eff_mode[0]) > 1
                    else AMDSMI_MAX_UTIL
                )
                ppt_limit = (
                    args.cpu_pwr_eff_mode[0][2]
                    if len(args.cpu_pwr_eff_mode[0]) > 2
                    else AMDSMI_MAX_PPT_LIMIT
                )
                updated_util, updated_ppt_limit = (
                    amdsmi_interface.amdsmi_set_cpu_pwr_efficiency_mode(
                        args.cpu, mode, util, ppt_limit
                    )
                )

                # Always show mode
                static_dict["pwr_eff_mode"]["mode"] = f"{mode}"

                # Only show util and ppt_limit for modes 4 and 5
                if mode in [4, 5]:
                    ppt_limit_watts = updated_ppt_limit / 1000.0  # Convert milliwatts to watts
                    static_dict["pwr_eff_mode"]["util"] = f"{updated_util}%"
                    static_dict["pwr_eff_mode"]["ppt_limit"] = f"{ppt_limit_watts} W"
                else:
                    # For modes 0-3, util and ppt_limit are not displayed
                    pass
                static_dict["pwr_eff_mode"]["response"] = (
                    "Set power efficiency mode operation successful"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["pwr_eff_mode"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set power efficiency mode for cpu %s | %s",
                    cpu_id,
                    e.get_error_info(),
                )

        if args.cpu_gmi3_link_width:
            static_dict["set_gmi3_link_width"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_gmi3_link_width_range(
                    args.cpu, args.cpu_gmi3_link_width[0][0], args.cpu_gmi3_link_width[0][1]
                )
                static_dict["set_gmi3_link_width"]["response"] = (
                    f"{args.cpu_gmi3_link_width[0][0]} - {args.cpu_gmi3_link_width[0][1]}"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_gmi3_link_width"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set gmi3 link width for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_pcie_link_rate:
            static_dict["set_pcie_link_rate"] = {}
            try:
                resp = amdsmi_interface.amdsmi_set_cpu_pcie_link_rate(
                    args.cpu, args.cpu_pcie_link_rate[0][0]
                )
                static_dict["set_pcie_link_rate"]["prev_mode"] = resp
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_pcie_link_rate"]["prev_mode"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set pcie link rate for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_df_pstate_range:
            static_dict["set_df_pstate_range"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_df_pstate_range(
                    args.cpu, args.cpu_df_pstate_range[0][0], args.cpu_df_pstate_range[0][1]
                )
                static_dict["set_df_pstate_range"]["response"] = "Set Operation successful"
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["set_df_pstate_range"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set df pstate range for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_enable_apb:
            static_dict["apbenable"] = {}
            try:
                amdsmi_interface.amdsmi_cpu_apb_enable(args.cpu)
                static_dict["apbenable"]["state"] = (
                    "Enabled DF - Pstate performance boost algorithm"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["apbenable"]["state"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug("Failed to enable APB for cpu %s | %s", cpu_id, e.get_error_info())

        if args.cpu_disable_apb:
            static_dict["apbdisable"] = {}
            try:
                amdsmi_interface.amdsmi_cpu_apb_disable(args.cpu, args.cpu_disable_apb[0][0])
                static_dict["apbdisable"]["state"] = (
                    "Disabled DF - Pstate performance boost algorithm"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["apbdisable"]["state"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug("Failed to enable APB for cpu %s | %s", cpu_id, e.get_error_info())

        if args.soc_boost_limit:
            static_dict["set_soc_boost_limit"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_socket_boostlimit(
                    args.cpu, args.soc_boost_limit[0][0]
                )
                static_dict["set_soc_boost_limit"]["Response"] = "Set Operation successful"
            except amdsmi_exception.AmdSmiLibraryException as e:
                # static_dict["set_soc_boost_limit"]["Response"] = "N/A"
                static_dict["set_soc_boost_limit"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set socket boost limit for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_xgmi_pstate_range:
            static_dict["xgmi_pstate_range"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_xgmi_pstate_range(
                    args.cpu, args.cpu_xgmi_pstate_range[0][0], args.cpu_xgmi_pstate_range[0][1]
                )
                static_dict["xgmi_pstate_range"]["response"] = (
                    f"Set, MIN_PSTATE: {args.cpu_xgmi_pstate_range[0][0]}, MAX_PSTATE: {args.cpu_xgmi_pstate_range[0][1]}, successful"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["xgmi_pstate_range"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set xgmi pstate range for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_railisofreq_policy:
            static_dict["railisofreq_policy"] = {}
            try:
                resp = amdsmi_interface.amdsmi_set_cpu_rail_isofreq_policy(
                    args.cpu, args.cpu_railisofreq_policy[0][0]
                )
                static_dict["railisofreq_policy"]["response"] = f"Set, VALUE: {resp}, successful"
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["railisofreq_policy"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set ISO frequency policy for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_dfcstate_ctrl:
            static_dict["dfcstate_ctrl"] = {}
            try:
                resp = amdsmi_interface.amdsmi_set_cpu_dfc_ctrl(
                    args.cpu, args.cpu_dfcstate_ctrl[0][0]
                )
                static_dict["dfcstate_ctrl"]["response"] = f"Set, VALUE: {resp}, successful"
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["dfcstate_ctrl"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set dfcstate control for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_pc6_enable:
            static_dict["pc6_enable"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_pc6_enable(args.cpu, args.cpu_pc6_enable[0][0])
                static_dict["pc6_enable"]["response"] = (
                    f"Set, VALUE: {args.cpu_pc6_enable[0][0]}, successful"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["pc6_enable"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set PC6 enable for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_cc6_enable:
            static_dict["cc6_enable"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_cc6_enable(args.cpu, args.cpu_cc6_enable[0][0])
                static_dict["cc6_enable"]["response"] = (
                    f"Set, VALUE: {args.cpu_cc6_enable[0][0]}, successful"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["cc6_enable"]["response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set CC6 enable for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_floor_limit:
            static_dict["floor_limit"] = {}
            try:
                cpu_floor_limit = args.cpu_floor_limit[0][0]
                amdsmi_interface.amdsmi_set_cpu_floor_freq_limit(args.cpu, cpu_floor_limit)

                # Verify the cpu floor limit is set
                flimit = amdsmi_interface.amdsmi_get_cpu_floor_freq_limit(args.cpu)
                freq_range = amdsmi_interface.amdsmi_get_cpu_freq_range()
                fmax = freq_range["fmax"]
                fmin = freq_range["fmin"]

                if flimit < cpu_floor_limit:
                    if fmax and flimit != fmax:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful"
                        )
                    else:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful. Max allowed cpu floor limit is {flimit} MHz"
                        )
                elif flimit > cpu_floor_limit:
                    if fmin and flimit != fmin:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful"
                        )
                    else:
                        static_dict["floor_limit"]["Response"] = (
                            f"Set, VALUE: {flimit} MHz, successful. Min allowed cpu floor limit is {flimit} MHz"
                        )
                else:
                    static_dict["floor_limit"]["Response"] = f"Set, VALUE: {flimit} MHz, successful"
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["floor_limit"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set socket floor limit for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_msr_floor_limit:
            static_dict["msr_floor_limit"] = {}
            try:
                msr_floor_limit = args.cpu_msr_floor_limit[0][0]
                amdsmi_interface.amdsmi_set_cpu_msr_floor_freq_limit(args.cpu, msr_floor_limit)

                # Verify the cpu msr floor limit is set
                effflimit = amdsmi_interface.amdsmi_get_cpu_eff_floor_freq_limit(args.cpu)
                if effflimit == 0:
                    effflimit = msr_floor_limit
                freq_range = amdsmi_interface.amdsmi_get_cpu_freq_range()
                fmax = freq_range["fmax"]
                fmin = freq_range["fmin"]

                if effflimit < msr_floor_limit:
                    if fmax and effflimit != fmax:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful"
                        )
                    else:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful. Max allowed msr cpu floor limit is {effflimit} MHz"
                        )
                elif effflimit > msr_floor_limit:
                    if fmin and effflimit != fmin:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful"
                        )
                    else:
                        static_dict["msr_floor_limit"]["Response"] = (
                            f"Set, VALUE: {effflimit} MHz, successful. Min allowed msr cpu floor limit is {effflimit} MHz"
                        )
                else:
                    static_dict["msr_floor_limit"]["Response"] = (
                        f"Set, VALUE: {effflimit} MHz, successful"
                    )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["msr_floor_limit"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set CPU MSR floor limit for cpu %s | %s", cpu_id, e.get_error_info()
                )

        if args.cpu_dimm_sb_reg:
            static_dict["dimm_sb_reg"] = {}
            try:
                dimm_addr = args.cpu_dimm_sb_reg[0][0]
                lid = args.cpu_dimm_sb_reg[0][1]
                reg_offset = args.cpu_dimm_sb_reg[0][2]
                reg_space = args.cpu_dimm_sb_reg[0][3]
                write_data = args.cpu_dimm_sb_reg[0][4]
                amdsmi_interface.amdsmi_set_cpu_dimm_sb_reg(
                    args.cpu, dimm_addr, lid, reg_offset, reg_space, write_data
                )
                static_dict["dimm_sb_reg"]["DimmAddress"] = f"0x{dimm_addr:02X}"
                static_dict["dimm_sb_reg"]["Lid"] = f"0x{lid:02X}"
                static_dict["dimm_sb_reg"]["Offset"] = f"0x{reg_offset:04X}"
                static_dict["dimm_sb_reg"]["RegSpace"] = reg_space
                static_dict["dimm_sb_reg"]["Data"] = f"0x{write_data:08X}"
                static_dict["dimm_sb_reg"]["Response"] = (
                    "Set DIMM sideband register write operation successful"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["dimm_sb_reg"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to write DIMM sideband register for cpu %s | %s",
                    cpu_id,
                    e.get_error_info(),
                )

        if args.cpu_sdps_limit:
            static_dict["sdps_limit"] = {}
            try:
                amdsmi_interface.amdsmi_set_cpu_sdps_limit(args.cpu, args.cpu_sdps_limit[0][0])
                sdps_limit_watts = float(args.cpu_sdps_limit[0][0]) / 1000
                static_dict["sdps_limit"]["Response"] = (
                    f"Set, VALUE: {sdps_limit_watts:.3f} W, successful"
                )
            except amdsmi_exception.AmdSmiLibraryException as e:
                static_dict["sdps_limit"]["Response"] = (
                    f"Error occurred for CPU {cpu_id} - {e.get_error_info()}"
                )
                logging.debug(
                    "Failed to set socket SDPS limit for cpu %s | %s", cpu_id, e.get_error_info()
                )

        multiple_devices_csv_override = False
        self.logger.store_cpu_output(args.cpu, "values", static_dict)
        if multiple_devices:
            self.logger.store_multiple_device_output()
            return  # Skip printing when there are multiple devices
        self.logger.print_output(multiple_device_enabled=multiple_devices_csv_override)

    def set_gpu(
        self,
        args,
        multiple_devices=False,
        gpu=None,
        fan=None,
        perf_level=None,
        profile=None,
        perf_determinism=None,
        compute_partition=None,
        memory_partition=None,
        power_cap=None,
        soc_pstate=None,
        xgmi_plpd=None,
        process_isolation=None,
        clk_limit=None,
        clk_level=None,
        ptl_status=None,
        ptl_format=None,
        mem_carveout=None,
        compute_partition_mem_alloc_mode=None,
    ):
        """Issue reset commands to target gpu(s)

        Args:
            args (Namespace): Namespace containing the parsed CLI args
            multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False.
            gpu (device_handle, optional): device_handle for target device. Defaults to None.
            fan (tuple, optional): Value override for args.fan as (value, is_percentage). Defaults to None.
            perf_level (amdsmi_interface.AmdSmiDevPerfLevel, optional): Value override for args.perf_level. Defaults to None.
            profile (bool, optional): Value override for args.profile. Defaults to None.
            perf_determinism (int, optional): Value override for args.perf_determinism. Defaults to None.
            compute_partition (amdsmi_interface.AmdSmiComputePartitionType, optional): Value override for args.compute_partition. Defaults to None.
            memory_partition (amdsmi_interface.AmdSmiMemoryPartitionType, optional): Value override for args.memory_partition. Defaults to None.
            power_cap (int, optional): Value override for args.power_cap. Defaults to None.
            soc_pstate (int, optional): Value override for args.soc_pstate. Defaults to None.
            xgmi_plpd (int, optional): Value override for args.xgmi_plpd. Defaults to None.
            process_isolation (int, optional): Value override for args.process_isolation. Defaults to None.
            ptl_status (int, optional): Value override for args.ptl_status. Defaults to None.
            ptl_format(string, optional): Value override for args.ptl_format. Defaults to None.
            compute_partition_mem_alloc_mode (str, optional): Value override for args.compute_partition_mem_alloc_mode. Defaults to None.
        Raises:
            ValueError: Value error if no gpu value is provided
            IndexError: Index error if gpu list is empty

        Return:
            Nothing
        """
        # Set args.* to passed in arguments
        if gpu:
            args.gpu = gpu
        if fan is not None:
            args.fan = fan
        if perf_level:
            args.perf_level = perf_level
        if profile:
            args.profile = profile
        if perf_determinism is not None:
            args.perf_determinism = perf_determinism
        if compute_partition:
            args.compute_partition = compute_partition
        if memory_partition:
            args.memory_partition = memory_partition
        if power_cap:
            args.power_cap = power_cap
        if soc_pstate:
            args.soc_pstate = soc_pstate
        if xgmi_plpd:
            args.xgmi_plpd = xgmi_plpd
        if process_isolation:
            args.process_isolation = process_isolation
        if clk_limit:
            args.clk_limit = clk_limit
        if clk_level:
            args.clk_level = clk_level
        if ptl_status:
            args.ptl_status = ptl_status
        if ptl_format:
            args.ptl_format = ptl_format
        if mem_carveout is not None:
            args.mem_carveout = mem_carveout
        if compute_partition_mem_alloc_mode is not None:
            args.compute_partition_mem_alloc_mode = compute_partition_mem_alloc_mode

        # Handle No GPU passed
        if args.gpu == None:
            args.gpu = self.device_handles

        if not self.group_check_printed:
            self.helpers.check_required_groups()
            self.group_check_printed = True

        # Handle multiple GPUs
        handled_multiple_gpus, device_handle = self.helpers.handle_gpus(
            args, self.logger, self.set_gpu
        )
        if handled_multiple_gpus:
            return  # This function is recursive

        args.gpu = device_handle

        # Error if no subcommand args are passed
        if self.helpers.is_baremetal():
            if not any(
                [
                    getattr(args, "fan", None) is not None,
                    getattr(args, "perf_level", None) is not None,
                    getattr(args, "profile", None) is not None,
                    getattr(args, "compute_partition", None) is not None,
                    getattr(args, "memory_partition", None) is not None,
                    getattr(args, "perf_determinism", None) is not None,
                    getattr(args, "power_cap", None) is not None,
                    getattr(args, "soc_pstate", None) is not None,
                    getattr(args, "xgmi_plpd", None) is not None,
                    getattr(args, "clk_level", None) is not None,
                    getattr(args, "clk_limit", None) is not None,
                    getattr(args, "ptl_status", None) is not None,
                    getattr(args, "ptl_format", None) is not None,
                    getattr(args, "process_isolation", None) is not None,
                    getattr(args, "mem_carveout", None) is not None,
                    getattr(args, "compute_partition_mem_alloc_mode", None) is not None,
                ]
            ):
                command = " ".join(sys.argv[1:])
                raise AmdSmiRequiredCommandException(command, self.logger.format)
        else:
            if not any(
                [
                    getattr(args, "power_cap", None) is not None,
                    getattr(args, "clk_limit", None) is not None,
                    getattr(args, "process_isolation", None) is not None,
                ]
            ):
                command = " ".join(sys.argv[1:])
                raise AmdSmiRequiredCommandException(command, self.logger.format)

        # Build GPU string for errors
        try:
            gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(args.gpu)
        except amdsmi_exception.AmdSmiLibraryException:
            gpu_bdf = f"BDF Unavailable for {args.gpu}"
        try:
            gpu_id = self.helpers.get_gpu_id_from_device_handle(args.gpu)
        except IndexError:
            gpu_id = f"ID Unavailable for {args.gpu}"
        gpu_string = f"GPU ID: {gpu_id} BDF:{gpu_bdf}"

        # Handle args
        if self.helpers.is_baremetal():
            if isinstance(args.fan, tuple):
                # Parse input: args.fan is now (value, is_percentage) tuple from parser
                input_value, is_percentage = args.fan

                # Check if gpu_od interface is available
                has_gpu_od, gpu_od_path = self.helpers.detect_gpu_od(gpu_bdf)

                # Helper function for consistent error formatting
                def format_fan_error(message, include_driver_note=False):
                    error_msg = message
                    if include_driver_note:
                        error_msg += (
                            "\nNote: For Navi3x+ GPUs, ensure the amdgpu driver is loaded with"
                            " gpu_od enabled:\n  sudo modprobe amdgpu ppfeaturemask=0xfff7ffff"
                            "\nIf fan operations return 'Device or resource busy', disable"
                            " runtime PM:\n  echo on | sudo tee"
                            " /sys/class/drm/card<N>/device/power/control"
                        )
                    return error_msg

                # Convert based on interface type and input format
                if has_gpu_od:
                    # For gpu_od interface: read OD_RANGE dynamically using shared helper
                    od_min, od_max = self.helpers.parse_gpu_od_fan_range(gpu_od_path)
                    if od_min is None:
                        # Parsing failed - cannot proceed without valid range
                        result = format_fan_error(
                            f"Unable to read gpu_od OD_RANGE from {gpu_od_path}. Cannot set fan speed.",
                            include_driver_note=True,
                        )
                        self.logger.store_output(args.gpu, "fan", result)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return

                    od_range = od_max - od_min
                    if is_percentage:
                        # Convert percentage (0-100%) to hardware range (od_min-od_max)
                        hw_value = od_min + int((input_value / 100) * od_range)
                        fan_percentage = input_value
                    else:
                        # Direct hardware value
                        if od_min <= input_value <= od_max:
                            hw_value = input_value
                            fan_percentage = (
                                int(((input_value - od_min) / od_range) * 100)
                                if od_range > 0
                                else 0
                            )
                        else:
                            result = format_fan_error(
                                f"Invalid fan speed value {input_value} for gpu_od interface. Valid range: {od_min}-{od_max} or use percentage (0-100%)",
                                include_driver_note=True,
                            )
                            self.logger.store_output(args.gpu, "fan", result)
                            self.logger.print_output()
                            self.logger.clear_multiple_devices_output()
                            return
                else:
                    # For legacy hwmon: range 0-255
                    if is_percentage:
                        # Convert percentage (0-100%) to PWM (0-255) using ceiling rounding
                        hw_value = math.ceil((input_value / 100) * 255)
                        fan_percentage = input_value
                    else:
                        # Direct PWM value
                        if 0 <= input_value <= 255:
                            hw_value = input_value
                            fan_percentage = int(
                                (input_value / 255) * 100 // 1
                            )  # round down (aka floor) to nearest whole number
                        else:
                            result = f"Invalid fan speed value {input_value}. Valid range: 0-255 or use percentage (0-100%)"
                            self.logger.store_output(args.gpu, "fan", result)
                            self.logger.print_output()
                            self.logger.clear_multiple_devices_output()
                            return

                try:
                    amdsmi_interface.amdsmi_set_gpu_fan_speed(args.gpu, 0, hw_value)
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    result = format_fan_error(
                        f"[{e.get_error_info(detailed=False)}] Unable to set fan speed to {hw_value} RPM/PWM ({fan_percentage}%)",
                        include_driver_note=has_gpu_od,
                    )
                    self.logger.store_output(args.gpu, "fan", result)
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                self.logger.store_output(
                    args.gpu,
                    "fan",
                    f"Successfully set fan speed to {hw_value} RPM/PWM ({fan_percentage}%)",
                )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if args.perf_level:
                perf_level = amdsmi_interface.AmdSmiDevPerfLevel[args.perf_level]
                try:
                    amdsmi_interface.amdsmi_set_gpu_perf_level(args.gpu, perf_level)
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    self.logger.store_output(
                        args.gpu,
                        "perflevel",
                        f"[{e.get_error_info(detailed=False)}] Unable to set performance level to {args.perf_level}",
                    )
                    perf_options = (
                        str(self.helpers.get_perf_levels()[0][0:-1])
                        .replace("[", "")
                        .replace("]", "")
                        .replace("'", "")
                        .replace(" ", "")
                    )
                    print(f"\nPerformance Level Options:\n\t{perf_options}\n")
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                self.logger.store_output(
                    args.gpu, "perflevel", f"Successfully set performance level {args.perf_level}"
                )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if args.profile:
                try:
                    # Parse profile input (name or number)
                    profile_input = args.profile.upper()
                    name_mapping = self.helpers.get_power_profile_name_mapping()

                    if profile_input in name_mapping:
                        profile_mask = name_mapping[profile_input]
                    else:
                        # Invalid profile - show available ones
                        try:
                            profile_status = amdsmi_interface.amdsmi_get_gpu_power_profile_presets(
                                args.gpu, 0
                            )
                            available = self.helpers.parse_available_profiles(
                                profile_status["available_profiles"]
                            )
                            available_str = ", ".join(available)
                        except amdsmi_exception.AmdSmiLibraryException as e:
                            available_str = "Unable to fetch available profiles"
                            logging.debug(
                                f"Failed to fetch available profiles: {e.get_error_info()}"
                            )

                        self.logger.store_output(
                            args.gpu,
                            "profile",
                            f"Invalid profile: {args.profile}\n\nAvailable profiles: {available_str}",
                        )
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return

                    # Set the profile
                    amdsmi_interface.amdsmi_set_gpu_power_profile(args.gpu, 0, profile_mask)

                    self.logger.store_output(
                        args.gpu, "profile", f"Successfully set power profile to {profile_input}"
                    )
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e

                    # Get available profiles for error message
                    try:
                        profile_status = amdsmi_interface.amdsmi_get_gpu_power_profile_presets(
                            args.gpu, 0
                        )
                        available = self.helpers.parse_available_profiles(
                            profile_status["available_profiles"]
                        )
                        available_str = ", ".join(available)
                    except amdsmi_exception.AmdSmiLibraryException as get_error:
                        available_str = "Unable to fetch available profiles"
                        logging.debug(
                            f"Failed to fetch available profiles: {get_error.get_error_info()}"
                        )

                    error_msg = f"[{e.get_error_info(detailed=False)}] Unable to set power profile to {args.profile}"
                    self.logger.store_output(args.gpu, "profile", error_msg)
                    print(f"\nAvailable Power Profiles:\n\t{available_str}\n")
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if isinstance(args.perf_determinism, int):
                try:
                    amdsmi_interface.amdsmi_set_gpu_perf_determinism_mode(
                        args.gpu, args.perf_determinism
                    )
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    self.logger.store_output(
                        args.gpu,
                        "perfdeterminism",
                        f"[{e.get_error_info(detailed=False)}] Unable to enable performance determinism and set GFX clock frequency to {args.perf_determinism} MHz",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                self.logger.store_output(
                    args.gpu,
                    "perfdeterminism",
                    f"Successfully enabled performance determinism and set GFX clock frequency to {args.perf_determinism} MHz",
                )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if args.compute_partition:
                current_set_count = self.helpers.get_set_count()
                future_set_count = 0
                attempted_to_set = "N/A"
                user_requested_partition_args = "N/A"
                try:
                    (accelerator_set_choices, accelerator_profiles) = (
                        self.helpers.get_accelerator_choices_types_indices()
                    )
                    logging.debug(
                        "args.compute_partition: %s; Accelerator_set_choices: %s",
                        str(args.compute_partition),
                        str(json.dumps(accelerator_set_choices, indent=4)),
                    )
                    if args.compute_partition in accelerator_profiles["profile_types"]:
                        compute_partition = amdsmi_interface.AmdSmiComputePartitionType[
                            args.compute_partition
                        ]
                        index = accelerator_profiles["profile_types"].index(args.compute_partition)
                        attempted_to_set = f"Attempted to set accelerator partition to {args.compute_partition} (profile #{accelerator_profiles['profile_indices'][int(index)]}) on {gpu_string}"
                        user_requested_partition_args = f"{args.compute_partition} (profile #{accelerator_profiles['profile_indices'][int(index)]})"
                        amdsmi_interface.amdsmi_set_gpu_compute_partition(
                            args.gpu, compute_partition
                        )
                    elif args.compute_partition in accelerator_profiles["profile_indices"]:
                        compute_partition = int(args.compute_partition)
                        index = accelerator_profiles["profile_indices"].index(
                            args.compute_partition
                        )
                        attempted_to_set = f"Attempted to set accelerator partition to {accelerator_profiles['profile_types'][int(index)]} (profile #{args.compute_partition}) on {gpu_string}"
                        user_requested_partition_args = f"{accelerator_profiles['profile_types'][int(index)]} (profile #{args.compute_partition})"
                        amdsmi_interface.amdsmi_set_gpu_accelerator_partition_profile(
                            args.gpu, compute_partition
                        )
                    else:
                        raise ValueError(
                            f"Invalid accelerator configuration {args.compute_partition} on {gpu_string}"
                        )
                    self.helpers.increment_set_count()
                    future_set_count = self.helpers.get_set_count()
                    if current_set_count == future_set_count - 1:
                        self.logger.store_output(
                            args.gpu,
                            "accelerator_partition",
                            f"Successfully set accelerator partition to {user_requested_partition_args}",
                        )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    elif (
                        e.get_error_code()
                        == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_SUPPORTED
                    ):
                        self.helpers.increment_set_count()
                        future_set_count = self.helpers.get_set_count()
                        if current_set_count == future_set_count - 1:
                            out = f"[AMDSMI_STATUS_NOT_SUPPORTED] Unable to set compute partition to {user_requested_partition_args}"
                            self.logger.store_output(args.gpu, "accelerator_partition", out)
                    elif (
                        e.get_error_code()
                        == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_SETTING_UNAVAILABLE
                    ):
                        print(
                            f"\n{attempted_to_set}\n"
                            f"\n[AMDSMI_STATUS_SETTING_UNAVAILABLE] Please check amd-smi partition --memory --accelerator for available profiles.\n"
                            "Users may need to switch memory partition to another mode in order to enable the desired accelerator partition.\n"
                        )
                        raise ValueError(
                            f"[AMDSMI_STATUS_SETTING_UNAVAILABLE] Unable to set accelerator partition to {args.compute_partition} on {gpu_string}"
                        ) from e
                    else:
                        raise ValueError(
                            f"Unable to set accelerator partition to {args.compute_partition} on {gpu_string}"
                        ) from e
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
            if args.memory_partition:
                ####################################################################
                # Get current and available memory partition modes                 #
                # Info used if AMDSMI_STATUS_INVAL is caught & to set progress bar #
                ####################################################################
                self.helpers.increment_set_count()
                set_count = self.helpers.get_set_count()
                if set_count == 1:  # only show reload warning on 1st set
                    self.helpers.confirm_changing_memory_partition_gpu_reload_warning()
                try:
                    memory_dict = {"caps": "N/A", "current": "N/A"}
                    memory_partition_config = (
                        amdsmi_interface.amdsmi_get_gpu_memory_partition_config(args.gpu)
                    )
                    memory_dict["caps"] = (
                        str(memory_partition_config["partition_caps"])
                        .replace("]", "")
                        .replace("[", "")
                        .replace("'", "")
                        .replace(" ", "")
                    )
                    memory_dict["current"] = memory_partition_config["mp_mode"]
                except amdsmi_exception.AmdSmiLibraryException as e:
                    logging.debug(
                        "Failed to get current memory partition for GPU %s | %s",
                        gpu_id,
                        e.get_error_info(),
                    )
                try:
                    memory_partition = amdsmi_interface.AmdSmiMemoryPartitionType[
                        args.memory_partition
                    ]
                    amdsmi_interface.amdsmi_set_gpu_memory_partition_mode(
                        args.gpu, memory_partition
                    )
                    out = f"Successfully set memory partition to {args.memory_partition}, use `sudo modprobe -r amdgpu && sudo modprobe amdgpu` to reload driver"
                except amdsmi_exception.AmdSmiLibraryException as e:
                    out = f"[{e.get_error_info(detailed=False)}] Unable to set memory partition to {args.memory_partition}"
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        out = "[AMDSMI_STATUS_NO_PERM] Command requires elevation"
                        self.logger.store_output(args.gpu, "memory_partition", out)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        raise PermissionError("Command requires elevation") from e
                    elif e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_INVAL:
                        print(f"Valid Memory partition Modes: {memory_dict['caps']}\n")
                        self.logger.store_output(args.gpu, "memory_partition", out)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return
                    else:
                        self.logger.store_output(args.gpu, "memory_partition", out)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return
                self.logger.store_output(args.gpu, "memory_partition", out)
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if isinstance(args.soc_pstate, int):
                try:
                    amdsmi_interface.amdsmi_set_soc_pstate(args.gpu, args.soc_pstate)
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_INVAL:
                        soc_pstate_info = amdsmi_interface.amdsmi_get_soc_pstate(args.gpu)
                        policy_string = "N/A"
                        # Check if 'policies' key exists before accessing it
                        if "policies" in soc_pstate_info and soc_pstate_info["policies"]:
                            policy_string = ""
                            for policy in soc_pstate_info["policies"]:
                                policy_string += (
                                    f"{policy['policy_id']}: {policy['policy_description']}, "
                                )
                            policy_string = policy_string.rstrip(
                                ", "
                            )  # Remove trailing comma and space
                        print(f"Valid SOC P-State Policies: [{policy_string}]\n")
                    self.logger.store_output(
                        args.gpu,
                        "socpstate",
                        f"[{e.get_error_info(detailed=False)}] Unable to set soc pstate dpm policy to {args.soc_pstate}",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
                self.logger.store_output(
                    args.gpu,
                    "socpstate",
                    f"Successfully set soc pstate dpm policy to {args.soc_pstate}",
                )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if isinstance(args.xgmi_plpd, int):
                try:
                    amdsmi_interface.amdsmi_set_xgmi_plpd(args.gpu, args.xgmi_plpd)
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_INVAL:
                        xgmi_plpd_info = amdsmi_interface.amdsmi_get_xgmi_plpd(args.gpu)
                        policy_string = "N/A"
                        # Check if 'policies' key exists before accessing it
                        if "policies" in xgmi_plpd_info and xgmi_plpd_info["policies"]:
                            policy_string = ""
                            for policy in xgmi_plpd_info["policies"]:
                                policy_string += (
                                    f"{policy['policy_id']}: {policy['policy_description']}, "
                                )
                            policy_string = policy_string.rstrip(
                                ", "
                            )  # Remove trailing comma and space
                        print(f"Valid XGMI PLPD Policies: [{policy_string}]\n")
                    self.logger.store_output(
                        args.gpu,
                        "xgmiplpd",
                        f"[{e.get_error_info(detailed=False)}] Unable to set XGMI per-link power down policy to {args.xgmi_plpd}",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
                self.logger.store_output(
                    args.gpu,
                    "xgmiplpd",
                    f"Successfully set XGMI per-link power down policy to {args.xgmi_plpd}",
                )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if isinstance(args.clk_level, tuple):
                clk_type = args.clk_level.clk_type
                perf_levels = args.clk_level.perf_levels
                perf_levels_str = str(perf_levels).strip("[]").replace(" ", "")
                smi_clk_type_mapping = {
                    "sclk": amdsmi_interface.AmdSmiClkType.SYS,
                    "mclk": amdsmi_interface.AmdSmiClkType.MEM,
                    "pcie": amdsmi_interface.AmdSmiClkType.PCIE,
                    "fclk": amdsmi_interface.AmdSmiClkType.DF,
                    "socclk": amdsmi_interface.AmdSmiClkType.SOC,
                }
                results_clk_lvl = {
                    "perf_level": "Unable to set performance level to MANUAL",
                    "get_clock_freq": f"Unable to retrieve {clk_type} frequency levels",
                    "set_clock": f"Unable to set {clk_type} perf level(s) to {perf_levels_str}",
                }
                if clk_type not in smi_clk_type_mapping:
                    raise ValueError(
                        f"Invalid clock type {clk_type}. Valid options are: {', '.join(smi_clk_type_mapping.keys())}"
                    )

                # Set perf level to manual if not already set
                try:
                    amdsmi_interface.amdsmi_set_gpu_perf_level(
                        args.gpu, amdsmi_interface.AmdSmiDevPerfLevel.MANUAL
                    )
                    results_clk_lvl["perf_level"] = "Successfully set performance level to MANUAL"
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    results_clk_lvl["perf_level"] = (
                        f"[{e.get_error_info(detailed=False)}] Unable to set performance level to MANUAL"
                    )
                    self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                if clk_type.lower() == "pcie":
                    # Get PCIe bandwidth levels
                    try:
                        pcie_bandwidth_levels = amdsmi_interface.amdsmi_get_gpu_pci_bandwidth(
                            args.gpu
                        )
                        num_supported = pcie_bandwidth_levels["transfer_rate"]["num_supported"]
                        results_clk_lvl["get_clock_freq"] = (
                            f"Successfully retrieved {clk_type} frequency levels"
                        )
                    except amdsmi_exception.AmdSmiLibraryException as e:
                        results_clk_lvl["get_clock_freq"] = (
                            f"[{e.get_error_info(detailed=False)}] Unable to retrieve {clk_type} frequency levels"
                        )
                        self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return
                else:
                    # Get clock frequency levels
                    try:
                        frequencies = amdsmi_interface.amdsmi_get_clk_freq(
                            args.gpu, smi_clk_type_mapping[clk_type]
                        )
                        num_supported = frequencies["num_supported"]
                        results_clk_lvl["get_clock_freq"] = (
                            f"Successfully retrieved {clk_type} frequency levels"
                        )
                    except amdsmi_exception.AmdSmiLibraryException as e:
                        results_clk_lvl["get_clock_freq"] = (
                            f"[{e.get_error_info(detailed=False)}] Unable to retrieve {clk_type} frequency levels"
                        )
                        self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return

                # Validate bandwidth bitmask
                freq_bitmask = 0
                invalid_levels = []
                for level in perf_levels:
                    if level < num_supported:
                        freq_bitmask |= 1 << level
                    else:
                        invalid_levels.append(level)

                if invalid_levels:
                    # Handle/report invalid levels
                    invalid_levels_str = str(invalid_levels).strip("[]").replace(" ", "")
                    valid_levels_str = f"Valid levels for {clk_type}: 0"
                    if num_supported > 1:
                        valid_levels_str = f"Valid levels for {clk_type}: 0-{num_supported - 1}"
                    print(f"\n{valid_levels_str}\n")
                    results_clk_lvl["set_clock"] = (
                        f"Invalid level(s) {invalid_levels_str} are not within the range of supported levels for {clk_type}"
                    )
                    self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
                else:
                    # Proceed with freq_bitmask
                    pass

                if clk_type.lower() == "pcie":
                    try:
                        amdsmi_interface.amdsmi_set_gpu_pci_bandwidth(args.gpu, freq_bitmask)
                        results_clk_lvl["set_clock"] = (
                            f"Successfully set {clk_type} perf level(s) to {perf_levels_str}"
                        )
                    except amdsmi_exception.AmdSmiLibraryException as e:
                        if (
                            e.get_error_code()
                            == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM
                        ):
                            raise PermissionError("Command requires elevation") from e

                        results_clk_lvl["set_clock"] = (
                            f"[{e.get_error_info(detailed=False)}] Unable to set {clk_type} perf level(s) to {perf_levels_str}"
                        )
                        self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return
                else:
                    # For non-pcie clocks
                    if clk_type in self.helpers.convert_clock_type:
                        clk_type_conversion = self.helpers.convert_clock_type[clk_type]
                    else:
                        clk_type_conversion = "N/A"

                    try:
                        amdsmi_interface.amdsmi_set_clk_freq(args.gpu, clk_type, freq_bitmask)
                        results_clk_lvl["set_clock"] = (
                            f"Successfully set {clk_type} perf level(s) to {perf_levels_str}"
                        )
                    except amdsmi_exception.AmdSmiLibraryException as e:
                        if (
                            e.get_error_code()
                            == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM
                        ):
                            raise PermissionError("Command requires elevation") from e
                        results_clk_lvl["set_clock"] = (
                            f"[{e.get_error_info(detailed=False)}] Unable to set {clk_type} perf level(s) to {perf_levels_str}"
                        )
                        self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return
                self.logger.store_output(args.gpu, "clk_level", results_clk_lvl)
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if isinstance(args.ptl_status, int):
                status_string = "Enabled" if args.ptl_status else "Disabled"
                result = f"Requested PTL status to {status_string}"  # This should not print out
                try:  # Due to driver requirements, do NOT check current state. Set state regardless of current state.
                    amdsmi_interface.amdsmi_set_gpu_ptl_state(args.gpu, args.ptl_status)
                    result = f"Successfully set PTL state to {status_string}"
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    self.logger.store_output(
                        args.gpu,
                        "ptlstatus",
                        f"[{e.get_error_info(detailed=False)}] Unable to set ptl status to {args.ptl_status}",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
                self.logger.store_output(args.gpu, "ptlstatus", result)
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            if isinstance(args.ptl_format, tuple):
                requested_fmt1_enum, requested_fmt2_enum = args.ptl_format
                requested_str = f"{requested_fmt1_enum.name},{requested_fmt2_enum.name}"

                result = f"Requested PTL status to {requested_str}"  # This should not print out
                try:
                    # Get current formats as ints
                    cur1_code, cur2_code = amdsmi_interface.amdsmi_get_gpu_ptl_formats(args.gpu)
                    cur1_enum = amdsmi_interface.AmdSmiPtlData(cur1_code)
                    cur2_enum = amdsmi_interface.AmdSmiPtlData(cur2_code)
                    current_str = f"{cur1_enum.name},{cur2_enum.name}"
                    if (cur1_enum, cur2_enum) == (requested_fmt1_enum, requested_fmt2_enum):
                        result = f"PTL format is already {current_str}"
                    else:
                        amdsmi_interface.amdsmi_set_gpu_ptl_formats(
                            args.gpu, requested_fmt1_enum, requested_fmt2_enum
                        )
                        result = f"Successfully set PTL format to {requested_str}"
                except amdsmi_exception.AmdSmiLibraryException as e:
                    if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                        raise PermissionError("Command requires elevation") from e
                    self.logger.store_output(
                        args.gpu,
                        "ptlformat",
                        f"[{e.get_error_info(detailed=False)}] Unable to set PTL format to {requested_str}",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
                self.logger.store_output(args.gpu, "ptlformat", result)
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return

        # Universal args
        if isinstance(args.power_cap, tuple):
            pwr_type = args.power_cap.pwr_type
            requested_power_cap = args.power_cap.watts

            # If pwr_type is None, default to ppt0 (legacy behavior)
            if pwr_type is None:
                pwr_type = "ppt0"
                pwr_type_as_int = 0
            else:
                pwr_type_as_int = 0 if pwr_type == "ppt0" else 1

            # Set the power cap for the specified sensor
            pwr_type_upper = pwr_type.upper()
            result = self.helpers.validate_and_set_power_cap(
                args.gpu, pwr_type_as_int, pwr_type_upper, requested_power_cap, self.logger
            )
            self.logger.store_output(args.gpu, "powercap", result)
            if multiple_devices:
                self.logger.store_multiple_device_output()
                return  # Skip printing when there are multiple devices
            self.logger.print_output()
            self.logger.clear_multiple_devices_output()
            return
        if isinstance(args.clk_limit, tuple):
            clk_type = args.clk_limit.clk_type
            lim_type = args.clk_limit.lim_type
            val = args.clk_limit.val
            val_changed = True  # Assume Clock limit value is changed

            # Validate the value against the extremum
            try:
                # Parser only allows three options sclk, mclk or fclk
                if clk_type == "sclk":
                    amdsmi_clk_type = amdsmi_interface.AmdSmiClkType.GFX
                elif clk_type == "mclk":
                    amdsmi_clk_type = amdsmi_interface.AmdSmiClkType.MEM
                elif clk_type == "fclk":
                    amdsmi_clk_type = amdsmi_interface.AmdSmiClkType.DF
                else:
                    print("Valid clock types are: sclk, mclk, fclk\n")
                    self.logger.store_output(
                        args.gpu, "clk_limit", f"Invalid clock type {args.clk_limit.clk_type}"
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
                clk_tuple = amdsmi_interface.amdsmi_get_clock_info(args.gpu, amdsmi_clk_type)

                if lim_type == "min":
                    amdsmi_lim_type = amdsmi_interface.AmdSmiClkLimitType.MIN
                    if isinstance(clk_tuple["max_clk"], int) and val > clk_tuple["max_clk"]:
                        self.logger.store_output(
                            args.gpu,
                            "clk_limit",
                            f"Cannot set {args.clk_limit.clk_type} min value greater than max ({clk_tuple['max_clk']}MHz)",
                        )
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return

                    if val == clk_tuple["min_clk"]:
                        val_changed = False  # Clock limit value did not changed
                elif lim_type == "max":
                    amdsmi_lim_type = amdsmi_interface.AmdSmiClkLimitType.MAX
                    if isinstance(clk_tuple["min_clk"], int) and val < clk_tuple["min_clk"]:
                        self.logger.store_output(
                            args.gpu,
                            "clk_limit",
                            f"Cannot set {args.clk_limit.clk_type} max value less than min ({clk_tuple['min_clk']}MHz)",
                        )
                        self.logger.print_output()
                        self.logger.clear_multiple_devices_output()
                        return
                    if val == clk_tuple["max_clk"]:
                        val_changed = False  # Clock limit value did not changed
            except amdsmi_exception.AmdSmiLibraryException as e:
                if (
                    e.get_error_code()
                    == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_SUPPORTED
                    and lim_type == "min"
                    and clk_type == "mclk"
                ):
                    logging.debug("Setting mclk min is not supported")
                    self.logger.store_output(
                        args.gpu, "clk_limit", "Setting mclk min is not supported"
                    )
                else:
                    logging.debug(
                        "Failed to get clock extremum info for gpu %s | %s",
                        gpu_id,
                        e.get_error_info(),
                    )
                    self.logger.store_output(
                        args.gpu,
                        "clk_limit",
                        f"[{e.get_error_info(detailed=False)}] Unable to change {args.clk_limit.lim_type} of {args.clk_limit.clk_type} to {args.clk_limit.val}MHz",
                    )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return

            # Set the value
            try:
                if val_changed:
                    amdsmi_interface.amdsmi_set_gpu_clk_limit(args.gpu, clk_type, lim_type, val)
            except amdsmi_exception.AmdSmiLibraryException as e:
                if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                    raise PermissionError("Command requires elevation") from e
                elif (
                    e.get_error_code()
                    == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_SUPPORTED
                    and lim_type == "min"
                    and clk_type == "mclk"
                ):
                    logging.debug("Setting mclk min is not supported")
                    self.logger.store_output(
                        args.gpu, "clk_limit", "Setting mclk min is not supported"
                    )
                else:
                    self.logger.store_output(
                        args.gpu,
                        "clk_limit",
                        f"[{e.get_error_info(detailed=False)}] Unable to set {args.clk_limit.lim_type} of {args.clk_limit.clk_type} to {args.clk_limit.val}MHz",
                    )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return

            if val_changed:
                self.logger.store_output(
                    args.gpu,
                    "clk_limit",
                    f"Successfully changed {args.clk_limit.lim_type} of {args.clk_limit.clk_type} to {args.clk_limit.val}MHz",
                )
            else:
                self.logger.store_output(
                    args.gpu, "clk_limit", f"Clock limit is already set to {args.clk_limit.val}MHz"
                )
            self.logger.print_output()
            self.logger.clear_multiple_devices_output()
            return
        if isinstance(args.process_isolation, int):
            status_string = "Enabled" if args.process_isolation else "Disabled"
            result = f"Requested process isolation to {status_string}"  # This should not print out
            try:
                current_status = amdsmi_interface.amdsmi_get_gpu_process_isolation(args.gpu)
                if current_status == args.process_isolation:
                    result = f"Process isolation is already {status_string}"
                else:
                    amdsmi_interface.amdsmi_set_gpu_process_isolation(
                        args.gpu, args.process_isolation
                    )
                    result = f"Successfully set process isolation to {status_string}"
            except amdsmi_exception.AmdSmiLibraryException as e:
                if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                    raise PermissionError("Command requires elevation") from e
                self.logger.store_output(
                    args.gpu,
                    "process_isolation",
                    f"[{e.get_error_info(detailed=False)}] Unable to set process isolation to {status_string}",
                )
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return

            self.logger.store_output(args.gpu, "process_isolation", result)
            self.logger.print_output()
            self.logger.clear_multiple_devices_output()
            return

        if args.mem_carveout is not None:
            # Validate single GPU (VRAM is per-GPU)
            if isinstance(args.gpu, list) and len(args.gpu) > 1:
                raise ValueError(
                    "VRAM carveout can only be set for a single GPU. Please specify --gpu <id>"
                )

            try:
                uma_info = amdsmi_interface.amdsmi_get_gpu_uma_carveout_info(args.gpu)
                options = uma_info.get("options", [])
                current_index = uma_info.get("current_index", -1)

                # Validate index
                if args.mem_carveout >= len(options):
                    self.logger.store_output(
                        args.gpu,
                        "mem_carveout",
                        f"Invalid index {args.mem_carveout}. Valid range: 0-{len(options) - 1}",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                # Check if already set
                if args.mem_carveout == current_index:
                    description = options[args.mem_carveout].get("description", "N/A")
                    self.logger.store_output(
                        args.gpu,
                        "mem_carveout",
                        f"VRAM carveout is already set to [{args.mem_carveout}] {description}",
                    )
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return

                # Set the value
                amdsmi_interface.amdsmi_set_gpu_uma_carveout(args.gpu, args.mem_carveout)
                description = options[args.mem_carveout].get("description", "N/A")
                self.logger.store_output(
                    args.gpu,
                    "mem_carveout",
                    f"Successfully set VRAM carveout to [{args.mem_carveout}] {description}. "
                    "Takes effect after the next reboot — "
                    "current VRAM size still reflects the previous boot.",
                )
                self.logger.print_output()
                self.helpers.prompt_reboot()

            except amdsmi_exception.AmdSmiLibraryException as e:
                if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                    raise PermissionError("Command requires elevation") from e
                if (
                    e.get_error_code()
                    == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_SUPPORTED
                ):
                    # Surface an actionable message instead of a raw error code.
                    # Avoid naming specific products here so the message does not
                    # age as new ASICs add or drop UMA carveout support.
                    self.logger.store_output(
                        args.gpu,
                        "mem_carveout",
                        "Not supported: UMA carveout is only available on APUs whose"
                        ' VBIOS exposes the ATCS "Set UMA Allocation Size" function.',
                    )
                else:
                    self.logger.store_output(
                        args.gpu,
                        "mem_carveout",
                        f"[{e.get_error_info(detailed=False)}] Unable to set VRAM carveout to index {args.mem_carveout}",
                    )
                self.logger.print_output()

            self.logger.clear_multiple_devices_output()
            return

        if getattr(args, "compute_partition_mem_alloc_mode", None):
            try:
                mode = amdsmi_interface.AmdSmiAcceleratorPartitionMemAllocModeType[
                    args.compute_partition_mem_alloc_mode
                ]
                amdsmi_interface.amdsmi_set_gpu_accelerator_partition_mem_alloc_mode(args.gpu, mode)
                out = f"Successfully set accelerator partition memory allocation mode to {args.compute_partition_mem_alloc_mode}"
            except KeyError:
                out = (
                    "Invalid accelerator partition memory allocation mode "
                    f"{args.compute_partition_mem_alloc_mode}"
                )
                self.logger.store_output(args.gpu, "accelerator_partition_mem_alloc_mode", out)
                self.logger.print_output()
                self.logger.clear_multiple_devices_output()
                return
            except amdsmi_exception.AmdSmiLibraryException as e:
                out = f"[{e.get_error_info(detailed=False)}] Unable to set accelerator partition memory allocation mode to {args.compute_partition_mem_alloc_mode}"
                if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                    out = "[AMDSMI_STATUS_NO_PERM] Command requires elevation"
                    self.logger.store_output(args.gpu, "accelerator_partition_mem_alloc_mode", out)
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    raise PermissionError("Command requires elevation") from e
                else:
                    self.logger.store_output(args.gpu, "accelerator_partition_mem_alloc_mode", out)
                    self.logger.print_output()
                    self.logger.clear_multiple_devices_output()
                    return
            self.logger.store_output(args.gpu, "accelerator_partition_mem_alloc_mode", out)
            self.logger.print_output()
            self.logger.clear_multiple_devices_output()
            return

    def set_value(
        self,
        args,
        multiple_devices=False,
        gpu=None,
        fan=None,
        perf_level=None,
        profile=None,
        perf_determinism=None,
        compute_partition=None,
        memory_partition=None,
        power_cap=None,
        cpu=None,
        cpu_pwr_limit=None,
        cpu_xgmi_link_width=None,
        cpu_lclk_dpm_level=None,
        cpu_pwr_eff_mode=None,
        cpu_gmi3_link_width=None,
        cpu_pcie_link_rate=None,
        cpu_df_pstate_range=None,
        cpu_enable_apb=None,
        cpu_disable_apb=None,
        soc_boost_limit=None,
        core=None,
        core_boost_limit=None,
        soc_pstate=None,
        xgmi_plpd=None,
        process_isolation=None,
        clk_limit=None,
        clk_level=None,
        ptl_status=None,
        ptl_format=None,
        cpu_xgmi_pstate_range=None,
        cpu_railisofreq_policy=None,
        cpu_dfcstate_ctrl=None,
        cpu_pc6_enable=None,
        cpu_cc6_enable=None,
        cpu_floor_limit=None,
        cpu_msr_floor_limit=None,
        cpu_dimm_sb_reg=None,
        cpu_sdps_limit=None,
        core_floor_limit=None,
        core_msr_floor_limit=None,
        mem_carveout=None,
        gtt=None,
    ):
        """Issue reset commands to target gpu(s)

        Args:
            args (Namespace): Namespace containing the parsed CLI args
            multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False.
            gpu (device_handle, optional): device_handle for target device. Defaults to None.
            fan (tuple, optional): Value override for args.fan as (value, is_percentage). Defaults to None.
            perf_level (amdsmi_interface.AmdSmiDevPerfLevel, optional): Value override for args.perf_level. Defaults to None.
            profile (bool, optional): Value override for args.profile. Defaults to None.
            perf_determinism (int, optional): Value override for args.perf_determinism. Defaults to None.
            compute_partition (amdsmi_interface.AmdSmiComputePartitionType, optional): Value override for args.compute_partition. Defaults to None.
            memory_partition (amdsmi_interface.AmdSmiMemoryPartitionType, optional): Value override for args.memory_partition. Defaults to None.
            power_cap (int, optional): Value override for args.power_cap. Defaults to None.

            cpu (cpu_handle, optional): device_handle for target device. Defaults to None.
            cpu_pwr_limit (int, optional): Value override for args.cpu_pwr_limit. Defaults to None.
            cpu_xgmi_link_width (List[int], optional): Value override for args.cpu_xgmi_link_width. Defaults to None.
            cpu_lclk_dpm_level (List[int], optional): Value override for args.cpu_lclk_dpm_level. Defaults to None.
            cpu_pwr_eff_mode (List[int], optional): Value override for args.cpu_pwr_eff_mode [mode, util, ppt_limit]. Defaults to None.
            cpu_gmi3_link_width (List[int], optional): Value override for args.cpu_gmi3_link_width. Defaults to None.
            cpu_pcie_link_rate (int, optional): Value override for args.cpu_pcie_link_rate. Defaults to None.
            cpu_df_pstate_range (List[int], optional): Value override for args.cpu_df_pstate_range. Defaults to None.
            cpu_enable_apb (bool, optional): Value override for args.cpu_enable_apb. Defaults to None.
            cpu_disable_apb (int, optional): Value override for args.cpu_disable_apb. Defaults to None.
            soc_boost_limit (int, optional): Value override for args.soc_boost_limit. Defaults to None.
            cpu_dfcstate_ctrl (int, optional): Value override for args.cpu_dfcstate_ctrl. Defaults to None.
            cpu_railisofreq_policy (int, optional): Value override for args.cpu_railisofreq_policy. Defaults to None.
            cpu_xgmi_pstate_range (List[int], optional): Value override for args.cpu_xgmi_pstate_range. Defaults to None.
            cpu_pc6_enable (int, optional): Value override for args.cpu_pc6_enable. Defaults to None.
            cpu_cc6_enable (int, optional): Value override for args.cpu_cc6_enable. Defaults to None.
            cpu_dimm_sb_reg (list, optional): DIMM sideband register write parameters [dimm_addr, lid, reg_offset, reg_space, write_data] for write operation. Value override for args.cpu_dimm_sb_reg. Defaults to None.
            cpu_sdps_limit (int, optional): Value override for args.cpu_sdps_limit. Defaults to None.
            cpu_floor_limit (int, optional): Value override for args.cpu_floor_limit. Defaults to None.
            cpu_msr_floor_limit (int, optional): Value override for args.cpu_msr_floor_limit. Defaults to None.

            core (device_handle, optional): device_handle for target core. Defaults to None.
            core_boost_limit (int, optional): Value override for args.core_boost_limit. Defaults to None
            core_floor_limit (int, optional): Value override for args.core_floor_limit. Defaults to None.
            core_msr_floor_limit (int, optional): Value override for args.core_msr_floor_limit. Defaults to None.
            soc_pstate (int, optional): Value override for args.soc_pstate. Defaults to None.
            xgmi_plpd (int, optional): Value override for args.xgmi_plpd. Defaults to None.
            process_isolation (int, optional): Value override for args.process_isolation. Defaults to None.
        Raises:
            ValueError: Value error if no gpu value is provided
            IndexError: Index error if gpu list is empty

        Return:
            Nothing
        """
        # These are the only args checked at this point, the other args will be passed
        #   in through the applicable function set_gpu, set_cpu, or set_core function
        if gpu:
            args.gpu = gpu
        if cpu:
            args.cpu = cpu
        if core:
            args.core = core

        # Special GTT handling (system-wide, not per-GPU) — handle before device dispatch
        if hasattr(args, "gtt") and args.gtt is not None:
            if hasattr(args, "gpu") and args.gpu is not None:
                print(
                    "amd-smi set: error: argument --gtt/-G: not allowed with argument --gpu/-g "
                    "(--gtt is a system-wide setting, not per-GPU)",
                    file=sys.stderr,
                )
                sys.exit(2)
            gb_value = args.gtt
            pages = self.helpers.gb_to_pages(gb_value)
            try:
                amdsmi_interface.amdsmi_set_ttm_pages_limit(pages)
                self.logger.output["set_gtt"] = (
                    f"Successfully set GTT to {gb_value:.2f} GB ({pages} pages). "
                    "Takes effect after the next reboot — "
                    "current values shown by 'amd-smi node --gtt' still reflect the previous boot."
                )
                self.logger.print_output()
                self.helpers.prompt_reboot()
                return
            except amdsmi_exception.AmdSmiLibraryException as e:
                if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM:
                    raise PermissionError("Command requires elevation") from e
                self.logger.output["set_gtt"] = (
                    f"[{e.get_error_info(detailed=False)}] Unable to set GTT to {gb_value:.2f} GB"
                )
                self.logger.print_output()
                return

        # Check if a GPU argument has been set
        gpu_args_enabled = False
        gpu_attributes = [
            "fan",
            "perf_level",
            "profile",
            "perf_determinism",
            "compute_partition",
            "memory_partition",
            "power_cap",
            "soc_pstate",
            "xgmi_plpd",
            "process_isolation",
            "clk_limit",
            "clk_level",
            "ptl_status",
            "ptl_format",
            "mem_carveout",
            "compute_partition_mem_alloc_mode",
        ]
        for attr in gpu_attributes:
            if hasattr(args, attr):
                if getattr(args, attr) is not None:
                    gpu_args_enabled = True
                    break
        # Check if a CPU argument has been set
        cpu_args_enabled = False
        cpu_attributes = [
            "cpu_pwr_limit",
            "cpu_xgmi_link_width",
            "cpu_lclk_dpm_level",
            "cpu_pwr_eff_mode",
            "cpu_gmi3_link_width",
            "cpu_pcie_link_rate",
            "cpu_df_pstate_range",
            "cpu_enable_apb",
            "cpu_disable_apb",
            "soc_boost_limit",
            "cpu_xgmi_pstate_range",
            "cpu_railisofreq_policy",
            "cpu_dfcstate_ctrl",
            "cpu_pc6_enable",
            "cpu_cc6_enable",
            "cpu_floor_limit",
            "cpu_msr_floor_limit",
            "cpu_dimm_sb_reg",
            "cpu_sdps_limit",
        ]
        for attr in cpu_attributes:
            if hasattr(args, attr):
                if getattr(args, attr) not in [None, False]:
                    cpu_args_enabled = True
                    break

        # Check if a Core argument has been set
        core_args_enabled = False
        core_attributes = ["core_boost_limit", "core_floor_limit", "core_msr_floor_limit"]
        for attr in core_attributes:
            if hasattr(args, attr):
                if getattr(args, attr) is not None:
                    core_args_enabled = True
                    break

        # Error if no subcommand args are passed
        if self.helpers.is_baremetal():
            is_gpu_set = False
            is_cpu_set = False
            is_core_set = False
            try:
                is_gpu_set = any(
                    [
                        args.gpu is not None,
                        args.fan is not None,
                        args.perf_level is not None,
                        args.profile is not None,
                        args.perf_determinism is not None,
                        args.compute_partition is not None,
                        args.memory_partition is not None,
                        args.power_cap is not None,
                        args.soc_pstate is not None,
                        args.xgmi_plpd is not None,
                        args.clk_limit is not None,
                        args.clk_level is not None,
                        args.ptl_status is not None,
                        args.ptl_format is not None,
                        args.process_isolation is not None,
                        args.mem_carveout is not None,
                        args.compute_partition_mem_alloc_mode is not None,
                    ]
                )
            except AttributeError:
                # If attribute error for gpu, then we could be another subcommand
                pass

            try:
                is_cpu_set = any(
                    [
                        args.cpu is not None,
                        args.cpu_pwr_limit is not None,
                        args.cpu_xgmi_link_width is not None,
                        args.cpu_lclk_dpm_level is not None,
                        args.cpu_pwr_eff_mode is not None,
                        args.cpu_gmi3_link_width is not None,
                        args.cpu_pcie_link_rate is not None,
                        args.cpu_df_pstate_range is not None,
                        args.cpu_enable_apb,
                        args.cpu_disable_apb is not None,
                        args.soc_boost_limit is not None,
                        args.cpu_xgmi_pstate_range is not None,
                        args.cpu_railisofreq_policy is not None,
                        args.cpu_dfcstate_ctrl is not None,
                        args.cpu_pc6_enable is not None,
                        args.cpu_cc6_enable is not None,
                        args.cpu_floor_limit is not None,
                        args.cpu_msr_floor_limit is not None,
                        args.cpu_dimm_sb_reg is not None,
                        args.cpu_sdps_limit is not None,
                    ]
                )
            except AttributeError:
                # If attribute error for cpu, then we could be another subcommand
                pass
            try:
                if args.core_boost_limit:
                    is_core_set = True
                if args.core_floor_limit:
                    is_core_set = True
                if args.core_msr_floor_limit:
                    is_core_set = True
            except AttributeError:
                # If attribute error for core, then we could be another subcommand
                pass

            if not (is_gpu_set or is_cpu_set or is_core_set):
                # if neither GPU / CPU / or Core args are provided, then raise error message
                command = " ".join(sys.argv[1:])
                raise AmdSmiRequiredCommandException(command, self.logger.format)
        else:
            if not any(
                [
                    args.process_isolation is not None,
                    args.clk_limit is not None,
                    args.power_cap is not None,
                ]
            ):
                command = " ".join(sys.argv[1:])
                raise AmdSmiRequiredCommandException(command, self.logger.format)

        # Only allow one device's arguments to be set at a time
        if not any([gpu_args_enabled, cpu_args_enabled, core_args_enabled]):
            raise ValueError(
                "No GPU, CPU, or CORE arguments provided, specific arguments are needed"
            )
        elif all([gpu_args_enabled, cpu_args_enabled, core_args_enabled]):
            raise ValueError("Cannot set GPU, CPU, and CORE arguments at the same time")
        elif not (gpu_args_enabled ^ cpu_args_enabled ^ core_args_enabled):
            raise ValueError("Cannot set GPU, CPU, or CORE arguments at the same time")

        if self.helpers.is_amdgpu_initialized() and gpu_args_enabled:
            if args.gpu == None:
                args.gpu = self.device_handles

        if self.helpers.is_amd_hsmp_initialized() and cpu_args_enabled:
            if args.cpu == None:
                args.cpu = self.cpu_handles

        if self.helpers.is_amd_hsmp_initialized() and core_args_enabled:
            if args.core == None:
                args.core = self.core_handles

        # Handle CPU and GPU initialization cases
        if self.helpers.is_amd_hsmp_initialized() and self.helpers.is_amdgpu_initialized():
            # Print out all CPU and all GPU static info only if no device was specified.
            # If a GPU or CPU argument is provided only print out the specified device.
            if args.cpu == None and args.gpu == None and args.core == None:
                raise ValueError("No GPU, CPU, or CORE provided, specific target(s) are needed")

            if args.cpu:
                self.set_cpu(
                    args,
                    multiple_devices,
                    cpu,
                    cpu_pwr_limit,
                    cpu_xgmi_link_width,
                    cpu_lclk_dpm_level,
                    cpu_pwr_eff_mode,
                    cpu_gmi3_link_width,
                    cpu_pcie_link_rate,
                    cpu_df_pstate_range,
                    cpu_enable_apb,
                    cpu_disable_apb,
                    soc_boost_limit,
                    cpu_xgmi_pstate_range,
                    cpu_railisofreq_policy,
                    cpu_dfcstate_ctrl,
                    cpu_pc6_enable,
                    cpu_cc6_enable,
                    cpu_floor_limit,
                    cpu_msr_floor_limit,
                    cpu_dimm_sb_reg,
                    cpu_sdps_limit,
                )
            if args.core:
                self.logger.output = {}
                self.logger.clear_multiple_devices_output()
                self.set_core(
                    args,
                    multiple_devices,
                    core,
                    core_boost_limit,
                    core_floor_limit,
                    core_msr_floor_limit,
                )
            if args.gpu:
                self.logger.output = {}
                self.logger.clear_multiple_devices_output()
                self.set_gpu(
                    args,
                    multiple_devices,
                    gpu,
                    fan,
                    perf_level,
                    profile,
                    perf_determinism,
                    compute_partition,
                    memory_partition,
                    power_cap,
                    soc_pstate,
                    xgmi_plpd,
                    process_isolation,
                    clk_limit,
                    clk_level,
                    ptl_status,
                    ptl_format,
                    mem_carveout,
                )
        elif self.helpers.is_amd_hsmp_initialized():  # Only CPU is initialized
            if args.cpu == None and args.core == None:
                raise ValueError("No CPU or CORE provided, specific target(s) are needed")
            if args.cpu:
                self.set_cpu(
                    args,
                    multiple_devices,
                    cpu,
                    cpu_pwr_limit,
                    cpu_xgmi_link_width,
                    cpu_lclk_dpm_level,
                    cpu_pwr_eff_mode,
                    cpu_gmi3_link_width,
                    cpu_pcie_link_rate,
                    cpu_df_pstate_range,
                    cpu_enable_apb,
                    cpu_disable_apb,
                    soc_boost_limit,
                    cpu_xgmi_pstate_range,
                    cpu_railisofreq_policy,
                    cpu_dfcstate_ctrl,
                    cpu_pc6_enable,
                    cpu_cc6_enable,
                    cpu_floor_limit,
                    cpu_msr_floor_limit,
                    cpu_dimm_sb_reg,
                    cpu_sdps_limit,
                )
            if args.core:
                self.logger.output = {}
                self.logger.clear_multiple_devices_output()
                self.set_core(
                    args,
                    multiple_devices,
                    core,
                    core_boost_limit,
                    core_floor_limit,
                    core_msr_floor_limit,
                )
        elif self.helpers.is_amdgpu_initialized():  # Only GPU is initialized
            if args.gpu == None:
                args.gpu = self.device_handles
            self.logger.clear_multiple_devices_output()
            self.set_gpu(
                args,
                multiple_devices,
                gpu,
                fan,
                perf_level,
                profile,
                perf_determinism,
                compute_partition,
                memory_partition,
                power_cap,
                soc_pstate,
                xgmi_plpd,
                process_isolation,
                clk_limit,
                clk_level,
                ptl_status,
                ptl_format,
                mem_carveout,
            )
