Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/radeon/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 173 kB image not shown  

Quelle  ci_dpm.c   Sprache: C

 
/*
 * Copyright 2013 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 */


#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/seq_file.h>

#include "atom.h"
#include "ci_dpm.h"
#include "cik.h"
#include "cikd.h"
#include "r600_dpm.h"
#include "radeon.h"
#include "radeon_asic.h"
#include "radeon_ucode.h"
#include "si_dpm.h"

#define MC_CG_ARB_FREQ_F0           0x0a
#define MC_CG_ARB_FREQ_F1           0x0b
#define MC_CG_ARB_FREQ_F2           0x0c
#define MC_CG_ARB_FREQ_F3           0x0d

#define SMC_RAM_END 0x40000

#define VOLTAGE_SCALE               4
#define VOLTAGE_VID_OFFSET_SCALE1    625
#define VOLTAGE_VID_OFFSET_SCALE2    100

static const struct ci_pt_defaults defaults_hawaii_xt = {
 1, 0xF, 0xFD, 0x19, 5, 0x14, 0, 0xB0000,
 { 0x2E,  0x00,  0x00,  0x88,  0x00,  0x00,  0x72,  0x60,  0x51,  0xA7,  0x79,  0x6B,  0x90,  0xBD,  0x79  },
 { 0x217, 0x217, 0x217, 0x242, 0x242, 0x242, 0x269, 0x269, 0x269, 0x2A1, 0x2A1, 0x2A1, 0x2C9, 0x2C9, 0x2C9 }
};

static const struct ci_pt_defaults defaults_hawaii_pro = {
 1, 0xF, 0xFD, 0x19, 5, 0x14, 0, 0x65062,
 { 0x2E,  0x00,  0x00,  0x88,  0x00,  0x00,  0x72,  0x60,  0x51,  0xA7,  0x79,  0x6B,  0x90,  0xBD,  0x79  },
 { 0x217, 0x217, 0x217, 0x242, 0x242, 0x242, 0x269, 0x269, 0x269, 0x2A1, 0x2A1, 0x2A1, 0x2C9, 0x2C9, 0x2C9 }
};

static const struct ci_pt_defaults defaults_bonaire_xt = {
 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
 { 0x79,  0x253, 0x25D, 0xAE,  0x72,  0x80,  0x83,  0x86,  0x6F,  0xC8,  0xC9,  0xC9,  0x2F,  0x4D,  0x61  },
 { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 }
};

static const struct ci_pt_defaults defaults_saturn_xt = {
 1, 0xF, 0xFD, 0x19, 5, 55, 0, 0x70000,
 { 0x8C,  0x247, 0x249, 0xA6,  0x80,  0x81,  0x8B,  0x89,  0x86,  0xC9,  0xCA,  0xC9,  0x4D,  0x4D,  0x4D  },
 { 0x187, 0x187, 0x187, 0x1C7, 0x1C7, 0x1C7, 0x210, 0x210, 0x210, 0x266, 0x266, 0x266, 0x2C9, 0x2C9, 0x2C9 }
};

static const struct ci_pt_config_reg didt_config_ci[] = {
 { 0x10, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x10, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x10, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x10, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x11, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x11, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x11, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x11, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x12, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x12, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x12, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x12, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x2, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x2, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x2, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x1, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x1, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x0, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x30, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x30, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x30, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x30, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x31, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x31, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x31, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x31, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x32, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x32, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x32, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x32, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x22, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x22, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x22, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x21, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x21, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x20, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x50, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x50, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x50, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x50, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x51, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x51, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x51, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x51, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x52, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x52, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x52, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x52, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x42, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x42, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x42, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x41, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x41, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x40, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x70, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x70, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x70, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x70, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x71, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x71, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x71, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x71, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x72, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x72, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x72, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x72, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x62, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x62, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x62, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x61, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x61, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
 { 0x60, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
 { 0xFFFFFFFF }
};

extern u8 rv770_get_memory_module_index(struct radeon_device *rdev);
extern int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
           u32 arb_freq_src, u32 arb_freq_dest);
static int ci_get_std_voltage_value_sidd(struct radeon_device *rdev,
      struct atom_voltage_table_entry *voltage_table,
      u16 *std_voltage_hi_sidd, u16 *std_voltage_lo_sidd);
static int ci_set_power_limit(struct radeon_device *rdev, u32 n);
static int ci_set_overdrive_target_tdp(struct radeon_device *rdev,
           u32 target_tdp);
static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate);

static PPSMC_Result ci_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg);
static PPSMC_Result ci_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
            PPSMC_Msg msg, u32 parameter);

static void ci_thermal_start_smc_fan_control(struct radeon_device *rdev);
static void ci_fan_ctrl_set_default_mode(struct radeon_device *rdev);

static struct ci_power_info *ci_get_pi(struct radeon_device *rdev)
{
 struct ci_power_info *pi = rdev->pm.dpm.priv;

 return pi;
}

static struct ci_ps *ci_get_ps(struct radeon_ps *rps)
{
 struct ci_ps *ps = rps->ps_priv;

 return ps;
}

static void ci_initialize_powertune_defaults(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 switch (rdev->pdev->device) {
 case 0x6649:
 case 0x6650:
 case 0x6651:
 case 0x6658:
 case 0x665C:
 case 0x665D:
 default:
  pi->powertune_defaults = &defaults_bonaire_xt;
  break;
 case 0x6640:
 case 0x6641:
 case 0x6646:
 case 0x6647:
  pi->powertune_defaults = &defaults_saturn_xt;
  break;
 case 0x67B8:
 case 0x67B0:
  pi->powertune_defaults = &defaults_hawaii_xt;
  break;
 case 0x67BA:
 case 0x67B1:
  pi->powertune_defaults = &defaults_hawaii_pro;
  break;
 case 0x67A0:
 case 0x67A1:
 case 0x67A2:
 case 0x67A8:
 case 0x67A9:
 case 0x67AA:
 case 0x67B9:
 case 0x67BE:
  pi->powertune_defaults = &defaults_bonaire_xt;
  break;
 }

 pi->dte_tj_offset = 0;

 pi->caps_power_containment = true;
 pi->caps_cac = false;
 pi->caps_sq_ramping = false;
 pi->caps_db_ramping = false;
 pi->caps_td_ramping = false;
 pi->caps_tcp_ramping = false;

 if (pi->caps_power_containment) {
  pi->caps_cac = true;
  if (rdev->family == CHIP_HAWAII)
   pi->enable_bapm_feature = false;
  else
   pi->enable_bapm_feature = true;
  pi->enable_tdc_limit_feature = true;
  pi->enable_pkg_pwr_tracking_feature = true;
 }
}

static u8 ci_convert_to_vid(u16 vddc)
{
 return (6200 - (vddc * VOLTAGE_SCALE)) / 25;
}

static int ci_populate_bapm_vddc_vid_sidd(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u8 *hi_vid = pi->smc_powertune_table.BapmVddCVidHiSidd;
 u8 *lo_vid = pi->smc_powertune_table.BapmVddCVidLoSidd;
 u8 *hi2_vid = pi->smc_powertune_table.BapmVddCVidHiSidd2;
 u32 i;

 if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries == NULL)
  return -EINVAL;
 if (rdev->pm.dpm.dyn_state.cac_leakage_table.count > 8)
  return -EINVAL;
 if (rdev->pm.dpm.dyn_state.cac_leakage_table.count !=
     rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count)
  return -EINVAL;

 for (i = 0; i < rdev->pm.dpm.dyn_state.cac_leakage_table.count; i++) {
  if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) {
   lo_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc1);
   hi_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc2);
   hi2_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc3);
  } else {
   lo_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc);
   hi_vid[i] = ci_convert_to_vid((u16)rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage);
  }
 }
 return 0;
}

static int ci_populate_vddc_vid(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u8 *vid = pi->smc_powertune_table.VddCVid;
 u32 i;

 if (pi->vddc_voltage_table.count > 8)
  return -EINVAL;

 for (i = 0; i < pi->vddc_voltage_table.count; i++)
  vid[i] = ci_convert_to_vid(pi->vddc_voltage_table.entries[i].value);

 return 0;
}

static int ci_populate_svi_load_line(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;

 pi->smc_powertune_table.SviLoadLineEn = pt_defaults->svi_load_line_en;
 pi->smc_powertune_table.SviLoadLineVddC = pt_defaults->svi_load_line_vddc;
 pi->smc_powertune_table.SviLoadLineTrimVddC = 3;
 pi->smc_powertune_table.SviLoadLineOffsetVddC = 0;

 return 0;
}

static int ci_populate_tdc_limit(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
 u16 tdc_limit;

 tdc_limit = rdev->pm.dpm.dyn_state.cac_tdp_table->tdc * 256;
 pi->smc_powertune_table.TDC_VDDC_PkgLimit = cpu_to_be16(tdc_limit);
 pi->smc_powertune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
  pt_defaults->tdc_vddc_throttle_release_limit_perc;
 pi->smc_powertune_table.TDC_MAWt = pt_defaults->tdc_mawt;

 return 0;
}

static int ci_populate_dw8(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
 int ret;

 ret = ci_read_smc_sram_dword(rdev,
         SMU7_FIRMWARE_HEADER_LOCATION +
         offsetof(SMU7_Firmware_Header, PmFuseTable) +
         offsetof(SMU7_Discrete_PmFuses, TdcWaterfallCtl),
         (u32 *)&pi->smc_powertune_table.TdcWaterfallCtl,
         pi->sram_end);
 if (ret)
  return -EINVAL;
 else
  pi->smc_powertune_table.TdcWaterfallCtl = pt_defaults->tdc_waterfall_ctl;

 return 0;
}

static int ci_populate_fuzzy_fan(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if ((rdev->pm.dpm.fan.fan_output_sensitivity & (1 << 15)) ||
     (rdev->pm.dpm.fan.fan_output_sensitivity == 0))
  rdev->pm.dpm.fan.fan_output_sensitivity =
   rdev->pm.dpm.fan.default_fan_output_sensitivity;

 pi->smc_powertune_table.FuzzyFan_PwmSetDelta =
  cpu_to_be16(rdev->pm.dpm.fan.fan_output_sensitivity);

 return 0;
}

static int ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u8 *hi_vid = pi->smc_powertune_table.BapmVddCVidHiSidd;
 u8 *lo_vid = pi->smc_powertune_table.BapmVddCVidLoSidd;
 int i, min, max;

 min = max = hi_vid[0];
 for (i = 0; i < 8; i++) {
  if (0 != hi_vid[i]) {
   if (min > hi_vid[i])
    min = hi_vid[i];
   if (max < hi_vid[i])
    max = hi_vid[i];
  }

  if (0 != lo_vid[i]) {
   if (min > lo_vid[i])
    min = lo_vid[i];
   if (max < lo_vid[i])
    max = lo_vid[i];
  }
 }

 if ((min == 0) || (max == 0))
  return -EINVAL;
 pi->smc_powertune_table.GnbLPMLMaxVid = (u8)max;
 pi->smc_powertune_table.GnbLPMLMinVid = (u8)min;

 return 0;
}

static int ci_populate_bapm_vddc_base_leakage_sidd(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u16 hi_sidd, lo_sidd;
 struct radeon_cac_tdp_table *cac_tdp_table =
  rdev->pm.dpm.dyn_state.cac_tdp_table;

 hi_sidd = cac_tdp_table->high_cac_leakage / 100 * 256;
 lo_sidd = cac_tdp_table->low_cac_leakage / 100 * 256;

 pi->smc_powertune_table.BapmVddCBaseLeakageHiSidd = cpu_to_be16(hi_sidd);
 pi->smc_powertune_table.BapmVddCBaseLeakageLoSidd = cpu_to_be16(lo_sidd);

 return 0;
}

static int ci_populate_bapm_parameters_in_dpm_table(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
 SMU7_Discrete_DpmTable  *dpm_table = &pi->smc_state_table;
 struct radeon_cac_tdp_table *cac_tdp_table =
  rdev->pm.dpm.dyn_state.cac_tdp_table;
 struct radeon_ppm_table *ppm = rdev->pm.dpm.dyn_state.ppm_table;
 int i, j, k;
 const u16 *def1;
 const u16 *def2;

 dpm_table->DefaultTdp = cac_tdp_table->tdp * 256;
 dpm_table->TargetTdp = cac_tdp_table->configurable_tdp * 256;

 dpm_table->DTETjOffset = (u8)pi->dte_tj_offset;
 dpm_table->GpuTjMax =
  (u8)(pi->thermal_temp_setting.temperature_high / 1000);
 dpm_table->GpuTjHyst = 8;

 dpm_table->DTEAmbientTempBase = pt_defaults->dte_ambient_temp_base;

 if (ppm) {
  dpm_table->PPM_PkgPwrLimit = cpu_to_be16((u16)ppm->dgpu_tdp * 256 / 1000);
  dpm_table->PPM_TemperatureLimit = cpu_to_be16((u16)ppm->tj_max * 256);
 } else {
  dpm_table->PPM_PkgPwrLimit = cpu_to_be16(0);
  dpm_table->PPM_TemperatureLimit = cpu_to_be16(0);
 }

 dpm_table->BAPM_TEMP_GRADIENT = cpu_to_be32(pt_defaults->bapm_temp_gradient);
 def1 = pt_defaults->bapmti_r;
 def2 = pt_defaults->bapmti_rc;

 for (i = 0; i < SMU7_DTE_ITERATIONS; i++) {
  for (j = 0; j < SMU7_DTE_SOURCES; j++) {
   for (k = 0; k < SMU7_DTE_SINKS; k++) {
    dpm_table->BAPMTI_R[i][j][k] = cpu_to_be16(*def1);
    dpm_table->BAPMTI_RC[i][j][k] = cpu_to_be16(*def2);
    def1++;
    def2++;
   }
  }
 }

 return 0;
}

static int ci_populate_pm_base(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 pm_fuse_table_offset;
 int ret;

 if (pi->caps_power_containment) {
  ret = ci_read_smc_sram_dword(rdev,
          SMU7_FIRMWARE_HEADER_LOCATION +
          offsetof(SMU7_Firmware_Header, PmFuseTable),
          &pm_fuse_table_offset, pi->sram_end);
  if (ret)
   return ret;
  ret = ci_populate_bapm_vddc_vid_sidd(rdev);
  if (ret)
   return ret;
  ret = ci_populate_vddc_vid(rdev);
  if (ret)
   return ret;
  ret = ci_populate_svi_load_line(rdev);
  if (ret)
   return ret;
  ret = ci_populate_tdc_limit(rdev);
  if (ret)
   return ret;
  ret = ci_populate_dw8(rdev);
  if (ret)
   return ret;
  ret = ci_populate_fuzzy_fan(rdev);
  if (ret)
   return ret;
  ret = ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(rdev);
  if (ret)
   return ret;
  ret = ci_populate_bapm_vddc_base_leakage_sidd(rdev);
  if (ret)
   return ret;
  ret = ci_copy_bytes_to_smc(rdev, pm_fuse_table_offset,
        (u8 *)&pi->smc_powertune_table,
        sizeof(SMU7_Discrete_PmFuses), pi->sram_end);
  if (ret)
   return ret;
 }

 return 0;
}

static void ci_do_enable_didt(struct radeon_device *rdev, const bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 data;

 if (pi->caps_sq_ramping) {
  data = RREG32_DIDT(DIDT_SQ_CTRL0);
  if (enable)
   data |= DIDT_CTRL_EN;
  else
   data &= ~DIDT_CTRL_EN;
  WREG32_DIDT(DIDT_SQ_CTRL0, data);
 }

 if (pi->caps_db_ramping) {
  data = RREG32_DIDT(DIDT_DB_CTRL0);
  if (enable)
   data |= DIDT_CTRL_EN;
  else
   data &= ~DIDT_CTRL_EN;
  WREG32_DIDT(DIDT_DB_CTRL0, data);
 }

 if (pi->caps_td_ramping) {
  data = RREG32_DIDT(DIDT_TD_CTRL0);
  if (enable)
   data |= DIDT_CTRL_EN;
  else
   data &= ~DIDT_CTRL_EN;
  WREG32_DIDT(DIDT_TD_CTRL0, data);
 }

 if (pi->caps_tcp_ramping) {
  data = RREG32_DIDT(DIDT_TCP_CTRL0);
  if (enable)
   data |= DIDT_CTRL_EN;
  else
   data &= ~DIDT_CTRL_EN;
  WREG32_DIDT(DIDT_TCP_CTRL0, data);
 }
}

static int ci_program_pt_config_registers(struct radeon_device *rdev,
       const struct ci_pt_config_reg *cac_config_regs)
{
 const struct ci_pt_config_reg *config_regs = cac_config_regs;
 u32 data;
 u32 cache = 0;

 if (config_regs == NULL)
  return -EINVAL;

 while (config_regs->offset != 0xFFFFFFFF) {
  if (config_regs->type == CISLANDS_CONFIGREG_CACHE) {
   cache |= ((config_regs->value << config_regs->shift) & config_regs->mask);
  } else {
   switch (config_regs->type) {
   case CISLANDS_CONFIGREG_SMC_IND:
    data = RREG32_SMC(config_regs->offset);
    break;
   case CISLANDS_CONFIGREG_DIDT_IND:
    data = RREG32_DIDT(config_regs->offset);
    break;
   default:
    data = RREG32(config_regs->offset << 2);
    break;
   }

   data &= ~config_regs->mask;
   data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
   data |= cache;

   switch (config_regs->type) {
   case CISLANDS_CONFIGREG_SMC_IND:
    WREG32_SMC(config_regs->offset, data);
    break;
   case CISLANDS_CONFIGREG_DIDT_IND:
    WREG32_DIDT(config_regs->offset, data);
    break;
   default:
    WREG32(config_regs->offset << 2, data);
    break;
   }
   cache = 0;
  }
  config_regs++;
 }
 return 0;
}

static int ci_enable_didt(struct radeon_device *rdev, bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 int ret;

 if (pi->caps_sq_ramping || pi->caps_db_ramping ||
     pi->caps_td_ramping || pi->caps_tcp_ramping) {
  cik_enter_rlc_safe_mode(rdev);

  if (enable) {
   ret = ci_program_pt_config_registers(rdev, didt_config_ci);
   if (ret) {
    cik_exit_rlc_safe_mode(rdev);
    return ret;
   }
  }

  ci_do_enable_didt(rdev, enable);

  cik_exit_rlc_safe_mode(rdev);
 }

 return 0;
}

static int ci_enable_power_containment(struct radeon_device *rdev, bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;
 int ret = 0;

 if (enable) {
  pi->power_containment_features = 0;
  if (pi->caps_power_containment) {
   if (pi->enable_bapm_feature) {
    smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableDTE);
    if (smc_result != PPSMC_Result_OK)
     ret = -EINVAL;
    else
     pi->power_containment_features |= POWERCONTAINMENT_FEATURE_BAPM;
   }

   if (pi->enable_tdc_limit_feature) {
    smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_TDCLimitEnable);
    if (smc_result != PPSMC_Result_OK)
     ret = -EINVAL;
    else
     pi->power_containment_features |= POWERCONTAINMENT_FEATURE_TDCLimit;
   }

   if (pi->enable_pkg_pwr_tracking_feature) {
    smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PkgPwrLimitEnable);
    if (smc_result != PPSMC_Result_OK) {
     ret = -EINVAL;
    } else {
     struct radeon_cac_tdp_table *cac_tdp_table =
      rdev->pm.dpm.dyn_state.cac_tdp_table;
     u32 default_pwr_limit =
      (u32)(cac_tdp_table->maximum_power_delivery_limit * 256);

     pi->power_containment_features |= POWERCONTAINMENT_FEATURE_PkgPwrLimit;

     ci_set_power_limit(rdev, default_pwr_limit);
    }
   }
  }
 } else {
  if (pi->caps_power_containment && pi->power_containment_features) {
   if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_TDCLimit)
    ci_send_msg_to_smc(rdev, PPSMC_MSG_TDCLimitDisable);

   if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_BAPM)
    ci_send_msg_to_smc(rdev, PPSMC_MSG_DisableDTE);

   if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_PkgPwrLimit)
    ci_send_msg_to_smc(rdev, PPSMC_MSG_PkgPwrLimitDisable);
   pi->power_containment_features = 0;
  }
 }

 return ret;
}

static int ci_enable_smc_cac(struct radeon_device *rdev, bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;
 int ret = 0;

 if (pi->caps_cac) {
  if (enable) {
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac);
   if (smc_result != PPSMC_Result_OK) {
    ret = -EINVAL;
    pi->cac_enabled = false;
   } else {
    pi->cac_enabled = true;
   }
  } else if (pi->cac_enabled) {
   ci_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac);
   pi->cac_enabled = false;
  }
 }

 return ret;
}

static int ci_enable_thermal_based_sclk_dpm(struct radeon_device *rdev,
         bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result = PPSMC_Result_OK;

 if (pi->thermal_sclk_dpm_enabled) {
  if (enable)
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_ENABLE_THERMAL_DPM);
  else
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_DISABLE_THERMAL_DPM);
 }

 if (smc_result == PPSMC_Result_OK)
  return 0;
 else
  return -EINVAL;
}

static int ci_power_control_set_level(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 struct radeon_cac_tdp_table *cac_tdp_table =
  rdev->pm.dpm.dyn_state.cac_tdp_table;
 s32 adjust_percent;
 s32 target_tdp;
 int ret = 0;
 bool adjust_polarity = false/* ??? */

 if (pi->caps_power_containment) {
  adjust_percent = adjust_polarity ?
   rdev->pm.dpm.tdp_adjustment : (-1 * rdev->pm.dpm.tdp_adjustment);
  target_tdp = ((100 + adjust_percent) *
         (s32)cac_tdp_table->configurable_tdp) / 100;

  ret = ci_set_overdrive_target_tdp(rdev, (u32)target_tdp);
 }

 return ret;
}

void ci_dpm_powergate_uvd(struct radeon_device *rdev, bool gate)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (pi->uvd_power_gated == gate)
  return;

 pi->uvd_power_gated = gate;

 ci_update_uvd_dpm(rdev, gate);
}

bool ci_dpm_vblank_too_short(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 vblank_time = r600_dpm_get_vblank_time(rdev);
 u32 switch_limit = pi->mem_gddr5 ? 450 : 300;

 /* disable mclk switching if the refresh is >120Hz, even if the
        * blanking period would allow it
        */

 if (r600_dpm_get_vrefresh(rdev) > 120)
  return true;

 if (vblank_time < switch_limit)
  return true;
 else
  return false;

}

static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
     struct radeon_ps *rps)
{
 struct ci_ps *ps = ci_get_ps(rps);
 struct ci_power_info *pi = ci_get_pi(rdev);
 struct radeon_clock_and_voltage_limits *max_limits;
 bool disable_mclk_switching;
 u32 sclk, mclk;
 int i;

 if (rps->vce_active) {
  rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
  rps->ecclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].ecclk;
 } else {
  rps->evclk = 0;
  rps->ecclk = 0;
 }

 if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
     ci_dpm_vblank_too_short(rdev))
  disable_mclk_switching = true;
 else
  disable_mclk_switching = false;

 if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
  pi->battery_state = true;
 else
  pi->battery_state = false;

 if (rdev->pm.dpm.ac_power)
  max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
 else
  max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;

 if (rdev->pm.dpm.ac_power == false) {
  for (i = 0; i < ps->performance_level_count; i++) {
   if (ps->performance_levels[i].mclk > max_limits->mclk)
    ps->performance_levels[i].mclk = max_limits->mclk;
   if (ps->performance_levels[i].sclk > max_limits->sclk)
    ps->performance_levels[i].sclk = max_limits->sclk;
  }
 }

 /* XXX validate the min clocks required for display */

 if (disable_mclk_switching) {
  mclk  = ps->performance_levels[ps->performance_level_count - 1].mclk;
  sclk = ps->performance_levels[0].sclk;
 } else {
  mclk = ps->performance_levels[0].mclk;
  sclk = ps->performance_levels[0].sclk;
 }

 if (rps->vce_active) {
  if (sclk < rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].sclk)
   sclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].sclk;
  if (mclk < rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].mclk)
   mclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].mclk;
 }

 ps->performance_levels[0].sclk = sclk;
 ps->performance_levels[0].mclk = mclk;

 if (ps->performance_levels[1].sclk < ps->performance_levels[0].sclk)
  ps->performance_levels[1].sclk = ps->performance_levels[0].sclk;

 if (disable_mclk_switching) {
  if (ps->performance_levels[0].mclk < ps->performance_levels[1].mclk)
   ps->performance_levels[0].mclk = ps->performance_levels[1].mclk;
 } else {
  if (ps->performance_levels[1].mclk < ps->performance_levels[0].mclk)
   ps->performance_levels[1].mclk = ps->performance_levels[0].mclk;
 }
}

static int ci_thermal_set_temperature_range(struct radeon_device *rdev,
         int min_temp, int max_temp)
{
 int low_temp = 0 * 1000;
 int high_temp = 255 * 1000;
 u32 tmp;

 if (low_temp < min_temp)
  low_temp = min_temp;
 if (high_temp > max_temp)
  high_temp = max_temp;
 if (high_temp < low_temp) {
  DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
  return -EINVAL;
 }

 tmp = RREG32_SMC(CG_THERMAL_INT);
 tmp &= ~(CI_DIG_THERM_INTH_MASK | CI_DIG_THERM_INTL_MASK);
 tmp |= CI_DIG_THERM_INTH(high_temp / 1000) |
  CI_DIG_THERM_INTL(low_temp / 1000);
 WREG32_SMC(CG_THERMAL_INT, tmp);

#if 0
 /* XXX: need to figure out how to handle this properly */
 tmp = RREG32_SMC(CG_THERMAL_CTRL);
 tmp &= DIG_THERM_DPM_MASK;
 tmp |= DIG_THERM_DPM(high_temp / 1000);
 WREG32_SMC(CG_THERMAL_CTRL, tmp);
#endif

 rdev->pm.dpm.thermal.min_temp = low_temp;
 rdev->pm.dpm.thermal.max_temp = high_temp;

 return 0;
}

static int ci_thermal_enable_alert(struct radeon_device *rdev,
       bool enable)
{
 u32 thermal_int = RREG32_SMC(CG_THERMAL_INT);
 PPSMC_Result result;

 if (enable) {
  thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
  WREG32_SMC(CG_THERMAL_INT, thermal_int);
  rdev->irq.dpm_thermal = false;
  result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Thermal_Cntl_Enable);
  if (result != PPSMC_Result_OK) {
   DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
   return -EINVAL;
  }
 } else {
  thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
  WREG32_SMC(CG_THERMAL_INT, thermal_int);
  rdev->irq.dpm_thermal = true;
  result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Thermal_Cntl_Disable);
  if (result != PPSMC_Result_OK) {
   DRM_DEBUG_KMS("Could not disable thermal interrupts.\n");
   return -EINVAL;
  }
 }

 return 0;
}

static void ci_fan_ctrl_set_static_mode(struct radeon_device *rdev, u32 mode)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 tmp;

 if (pi->fan_ctrl_is_in_default_mode) {
  tmp = (RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK) >> FDO_PWM_MODE_SHIFT;
  pi->fan_ctrl_default_mode = tmp;
  tmp = (RREG32_SMC(CG_FDO_CTRL2) & TMIN_MASK) >> TMIN_SHIFT;
  pi->t_min = tmp;
  pi->fan_ctrl_is_in_default_mode = false;
 }

 tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TMIN_MASK;
 tmp |= TMIN(0);
 WREG32_SMC(CG_FDO_CTRL2, tmp);

 tmp = RREG32_SMC(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
 tmp |= FDO_PWM_MODE(mode);
 WREG32_SMC(CG_FDO_CTRL2, tmp);
}

static int ci_thermal_setup_fan_table(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 SMU7_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
 u32 duty100;
 u32 t_diff1, t_diff2, pwm_diff1, pwm_diff2;
 u16 fdo_min, slope1, slope2;
 u32 reference_clock, tmp;
 int ret;
 u64 tmp64;

 if (!pi->fan_table_start) {
  rdev->pm.dpm.fan.ucode_fan_control = false;
  return 0;
 }

 duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;

 if (duty100 == 0) {
  rdev->pm.dpm.fan.ucode_fan_control = false;
  return 0;
 }

 tmp64 = (u64)rdev->pm.dpm.fan.pwm_min * duty100;
 do_div(tmp64, 10000);
 fdo_min = (u16)tmp64;

 t_diff1 = rdev->pm.dpm.fan.t_med - rdev->pm.dpm.fan.t_min;
 t_diff2 = rdev->pm.dpm.fan.t_high - rdev->pm.dpm.fan.t_med;

 pwm_diff1 = rdev->pm.dpm.fan.pwm_med - rdev->pm.dpm.fan.pwm_min;
 pwm_diff2 = rdev->pm.dpm.fan.pwm_high - rdev->pm.dpm.fan.pwm_med;

 slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
 slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);

 fan_table.TempMin = cpu_to_be16((50 + rdev->pm.dpm.fan.t_min) / 100);
 fan_table.TempMed = cpu_to_be16((50 + rdev->pm.dpm.fan.t_med) / 100);
 fan_table.TempMax = cpu_to_be16((50 + rdev->pm.dpm.fan.t_max) / 100);

 fan_table.Slope1 = cpu_to_be16(slope1);
 fan_table.Slope2 = cpu_to_be16(slope2);

 fan_table.FdoMin = cpu_to_be16(fdo_min);

 fan_table.HystDown = cpu_to_be16(rdev->pm.dpm.fan.t_hyst);

 fan_table.HystUp = cpu_to_be16(1);

 fan_table.HystSlope = cpu_to_be16(1);

 fan_table.TempRespLim = cpu_to_be16(5);

 reference_clock = radeon_get_xclk(rdev);

 fan_table.RefreshPeriod = cpu_to_be32((rdev->pm.dpm.fan.cycle_delay *
            reference_clock) / 1600);

 fan_table.FdoMax = cpu_to_be16((u16)duty100);

 tmp = (RREG32_SMC(CG_MULT_THERMAL_CTRL) & TEMP_SEL_MASK) >> TEMP_SEL_SHIFT;
 fan_table.TempSrc = (uint8_t)tmp;

 ret = ci_copy_bytes_to_smc(rdev,
       pi->fan_table_start,
       (u8 *)(&fan_table),
       sizeof(fan_table),
       pi->sram_end);

 if (ret) {
  DRM_ERROR("Failed to load fan table to the SMC.");
  rdev->pm.dpm.fan.ucode_fan_control = false;
 }

 return 0;
}

static int ci_fan_ctrl_start_smc_fan_control(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result ret;

 if (pi->caps_od_fuzzy_fan_control_support) {
  ret = ci_send_msg_to_smc_with_parameter(rdev,
       PPSMC_StartFanControl,
       FAN_CONTROL_FUZZY);
  if (ret != PPSMC_Result_OK)
   return -EINVAL;
  ret = ci_send_msg_to_smc_with_parameter(rdev,
       PPSMC_MSG_SetFanPwmMax,
       rdev->pm.dpm.fan.default_max_fan_pwm);
  if (ret != PPSMC_Result_OK)
   return -EINVAL;
 } else {
  ret = ci_send_msg_to_smc_with_parameter(rdev,
       PPSMC_StartFanControl,
       FAN_CONTROL_TABLE);
  if (ret != PPSMC_Result_OK)
   return -EINVAL;
 }

 pi->fan_is_controlled_by_smc = true;
 return 0;
}

static int ci_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev)
{
 PPSMC_Result ret;
 struct ci_power_info *pi = ci_get_pi(rdev);

 ret = ci_send_msg_to_smc(rdev, PPSMC_StopFanControl);
 if (ret == PPSMC_Result_OK) {
  pi->fan_is_controlled_by_smc = false;
  return 0;
 } else
  return -EINVAL;
}

int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev,
          u32 *speed)
{
 u32 duty, duty100;
 u64 tmp64;

 if (rdev->pm.no_fan)
  return -ENOENT;

 duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
 duty = (RREG32_SMC(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT;

 if (duty100 == 0)
  return -EINVAL;

 tmp64 = (u64)duty * 100;
 do_div(tmp64, duty100);
 *speed = (u32)tmp64;

 if (*speed > 100)
  *speed = 100;

 return 0;
}

int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev,
          u32 speed)
{
 u32 tmp;
 u32 duty, duty100;
 u64 tmp64;
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (rdev->pm.no_fan)
  return -ENOENT;

 if (pi->fan_is_controlled_by_smc)
  return -EINVAL;

 if (speed > 100)
  return -EINVAL;

 duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;

 if (duty100 == 0)
  return -EINVAL;

 tmp64 = (u64)speed * duty100;
 do_div(tmp64, 100);
 duty = (u32)tmp64;

 tmp = RREG32_SMC(CG_FDO_CTRL0) & ~FDO_STATIC_DUTY_MASK;
 tmp |= FDO_STATIC_DUTY(duty);
 WREG32_SMC(CG_FDO_CTRL0, tmp);

 return 0;
}

void ci_fan_ctrl_set_mode(struct radeon_device *rdev, u32 mode)
{
 if (mode) {
  /* stop auto-manage */
  if (rdev->pm.dpm.fan.ucode_fan_control)
   ci_fan_ctrl_stop_smc_fan_control(rdev);
  ci_fan_ctrl_set_static_mode(rdev, mode);
 } else {
  /* restart auto-manage */
  if (rdev->pm.dpm.fan.ucode_fan_control)
   ci_thermal_start_smc_fan_control(rdev);
  else
   ci_fan_ctrl_set_default_mode(rdev);
 }
}

u32 ci_fan_ctrl_get_mode(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 tmp;

 if (pi->fan_is_controlled_by_smc)
  return 0;

 tmp = RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK;
 return (tmp >> FDO_PWM_MODE_SHIFT);
}

#if 0
static int ci_fan_ctrl_get_fan_speed_rpm(struct radeon_device *rdev,
      u32 *speed)
{
 u32 tach_period;
 u32 xclk = radeon_get_xclk(rdev);

 if (rdev->pm.no_fan)
  return -ENOENT;

 if (rdev->pm.fan_pulses_per_revolution == 0)
  return -ENOENT;

 tach_period = (RREG32_SMC(CG_TACH_STATUS) & TACH_PERIOD_MASK) >> TACH_PERIOD_SHIFT;
 if (tach_period == 0)
  return -ENOENT;

 *speed = 60 * xclk * 10000 / tach_period;

 return 0;
}

static int ci_fan_ctrl_set_fan_speed_rpm(struct radeon_device *rdev,
      u32 speed)
{
 u32 tach_period, tmp;
 u32 xclk = radeon_get_xclk(rdev);

 if (rdev->pm.no_fan)
  return -ENOENT;

 if (rdev->pm.fan_pulses_per_revolution == 0)
  return -ENOENT;

 if ((speed < rdev->pm.fan_min_rpm) ||
     (speed > rdev->pm.fan_max_rpm))
  return -EINVAL;

 if (rdev->pm.dpm.fan.ucode_fan_control)
  ci_fan_ctrl_stop_smc_fan_control(rdev);

 tach_period = 60 * xclk * 10000 / (8 * speed);
 tmp = RREG32_SMC(CG_TACH_CTRL) & ~TARGET_PERIOD_MASK;
 tmp |= TARGET_PERIOD(tach_period);
 WREG32_SMC(CG_TACH_CTRL, tmp);

 ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC_RPM);

 return 0;
}
#endif

static void ci_fan_ctrl_set_default_mode(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 tmp;

 if (!pi->fan_ctrl_is_in_default_mode) {
  tmp = RREG32_SMC(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
  tmp |= FDO_PWM_MODE(pi->fan_ctrl_default_mode);
  WREG32_SMC(CG_FDO_CTRL2, tmp);

  tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TMIN_MASK;
  tmp |= TMIN(pi->t_min);
  WREG32_SMC(CG_FDO_CTRL2, tmp);
  pi->fan_ctrl_is_in_default_mode = true;
 }
}

static void ci_thermal_start_smc_fan_control(struct radeon_device *rdev)
{
 if (rdev->pm.dpm.fan.ucode_fan_control) {
  ci_fan_ctrl_start_smc_fan_control(rdev);
  ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC);
 }
}

static void ci_thermal_initialize(struct radeon_device *rdev)
{
 u32 tmp;

 if (rdev->pm.fan_pulses_per_revolution) {
  tmp = RREG32_SMC(CG_TACH_CTRL) & ~EDGE_PER_REV_MASK;
  tmp |= EDGE_PER_REV(rdev->pm.fan_pulses_per_revolution - 1);
  WREG32_SMC(CG_TACH_CTRL, tmp);
 }

 tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TACH_PWM_RESP_RATE_MASK;
 tmp |= TACH_PWM_RESP_RATE(0x28);
 WREG32_SMC(CG_FDO_CTRL2, tmp);
}

static int ci_thermal_start_thermal_controller(struct radeon_device *rdev)
{
 int ret;

 ci_thermal_initialize(rdev);
 ret = ci_thermal_set_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
 if (ret)
  return ret;
 ret = ci_thermal_enable_alert(rdev, true);
 if (ret)
  return ret;
 if (rdev->pm.dpm.fan.ucode_fan_control) {
  ret = ci_thermal_setup_fan_table(rdev);
  if (ret)
   return ret;
  ci_thermal_start_smc_fan_control(rdev);
 }

 return 0;
}

static void ci_thermal_stop_thermal_controller(struct radeon_device *rdev)
{
 if (!rdev->pm.no_fan)
  ci_fan_ctrl_set_default_mode(rdev);
}

#if 0
static int ci_read_smc_soft_register(struct radeon_device *rdev,
         u16 reg_offset, u32 *value)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 return ci_read_smc_sram_dword(rdev,
          pi->soft_regs_start + reg_offset,
          value, pi->sram_end);
}
#endif

static int ci_write_smc_soft_register(struct radeon_device *rdev,
          u16 reg_offset, u32 value)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 return ci_write_smc_sram_dword(rdev,
           pi->soft_regs_start + reg_offset,
           value, pi->sram_end);
}

static void ci_init_fps_limits(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 SMU7_Discrete_DpmTable *table = &pi->smc_state_table;

 if (pi->caps_fps) {
  u16 tmp;

  tmp = 45;
  table->FpsHighT = cpu_to_be16(tmp);

  tmp = 30;
  table->FpsLowT = cpu_to_be16(tmp);
 }
}

static int ci_update_sclk_t(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 int ret = 0;
 u32 low_sclk_interrupt_t = 0;

 if (pi->caps_sclk_throttle_low_notification) {
  low_sclk_interrupt_t = cpu_to_be32(pi->low_sclk_interrupt_t);

  ret = ci_copy_bytes_to_smc(rdev,
        pi->dpm_table_start +
        offsetof(SMU7_Discrete_DpmTable, LowSclkInterruptT),
        (u8 *)&low_sclk_interrupt_t,
        sizeof(u32), pi->sram_end);

 }

 return ret;
}

static void ci_get_leakage_voltages(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u16 leakage_id, virtual_voltage_id;
 u16 vddc, vddci;
 int i;

 pi->vddc_leakage.count = 0;
 pi->vddci_leakage.count = 0;

 if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) {
  for (i = 0; i < CISLANDS_MAX_LEAKAGE_COUNT; i++) {
   virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
   if (radeon_atom_get_voltage_evv(rdev, virtual_voltage_id, &vddc) != 0)
    continue;
   if (vddc != 0 && vddc != virtual_voltage_id) {
    pi->vddc_leakage.actual_voltage[pi->vddc_leakage.count] = vddc;
    pi->vddc_leakage.leakage_id[pi->vddc_leakage.count] = virtual_voltage_id;
    pi->vddc_leakage.count++;
   }
  }
 } else if (radeon_atom_get_leakage_id_from_vbios(rdev, &leakage_id) == 0) {
  for (i = 0; i < CISLANDS_MAX_LEAKAGE_COUNT; i++) {
   virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
   if (radeon_atom_get_leakage_vddc_based_on_leakage_params(rdev, &vddc, &vddci,
           virtual_voltage_id,
           leakage_id) == 0) {
    if (vddc != 0 && vddc != virtual_voltage_id) {
     pi->vddc_leakage.actual_voltage[pi->vddc_leakage.count] = vddc;
     pi->vddc_leakage.leakage_id[pi->vddc_leakage.count] = virtual_voltage_id;
     pi->vddc_leakage.count++;
    }
    if (vddci != 0 && vddci != virtual_voltage_id) {
     pi->vddci_leakage.actual_voltage[pi->vddci_leakage.count] = vddci;
     pi->vddci_leakage.leakage_id[pi->vddci_leakage.count] = virtual_voltage_id;
     pi->vddci_leakage.count++;
    }
   }
  }
 }
}

static void ci_set_dpm_event_sources(struct radeon_device *rdev, u32 sources)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 bool want_thermal_protection;
 u32 tmp;

 switch (sources) {
 case 0:
 default:
  want_thermal_protection = false;
  break;
 case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
  want_thermal_protection = true;
  break;
 case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
  want_thermal_protection = true;
  break;
 case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
       (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
  want_thermal_protection = true;
  break;
 }

 if (want_thermal_protection) {
  tmp = RREG32_SMC(GENERAL_PWRMGT);
  if (pi->thermal_protection)
   tmp &= ~THERMAL_PROTECTION_DIS;
  else
   tmp |= THERMAL_PROTECTION_DIS;
  WREG32_SMC(GENERAL_PWRMGT, tmp);
 } else {
  tmp = RREG32_SMC(GENERAL_PWRMGT);
  tmp |= THERMAL_PROTECTION_DIS;
  WREG32_SMC(GENERAL_PWRMGT, tmp);
 }
}

static void ci_enable_auto_throttle_source(struct radeon_device *rdev,
        enum radeon_dpm_auto_throttle_src source,
        bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (enable) {
  if (!(pi->active_auto_throttle_sources & (1 << source))) {
   pi->active_auto_throttle_sources |= 1 << source;
   ci_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
  }
 } else {
  if (pi->active_auto_throttle_sources & (1 << source)) {
   pi->active_auto_throttle_sources &= ~(1 << source);
   ci_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
  }
 }
}

static void ci_enable_vr_hot_gpio_interrupt(struct radeon_device *rdev)
{
 if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT)
  ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableVRHotGPIOInterrupt);
}

static int ci_unfreeze_sclk_mclk_dpm(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;

 if (!pi->need_update_smu7_dpm_table)
  return 0;

 if ((!pi->sclk_dpm_key_disabled) &&
     (pi->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_UPDATE_SCLK))) {
  smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_SCLKDPM_UnfreezeLevel);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 if ((!pi->mclk_dpm_key_disabled) &&
     (pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
  smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_UnfreezeLevel);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 pi->need_update_smu7_dpm_table = 0;
 return 0;
}

static int ci_enable_sclk_mclk_dpm(struct radeon_device *rdev, bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;

 if (enable) {
  if (!pi->sclk_dpm_key_disabled) {
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_DPM_Enable);
   if (smc_result != PPSMC_Result_OK)
    return -EINVAL;
  }

  if (!pi->mclk_dpm_key_disabled) {
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_Enable);
   if (smc_result != PPSMC_Result_OK)
    return -EINVAL;

   WREG32_P(MC_SEQ_CNTL_3, CAC_EN, ~CAC_EN);

   WREG32_SMC(LCAC_MC0_CNTL, 0x05);
   WREG32_SMC(LCAC_MC1_CNTL, 0x05);
   WREG32_SMC(LCAC_CPL_CNTL, 0x100005);

   udelay(10);

   WREG32_SMC(LCAC_MC0_CNTL, 0x400005);
   WREG32_SMC(LCAC_MC1_CNTL, 0x400005);
   WREG32_SMC(LCAC_CPL_CNTL, 0x500005);
  }
 } else {
  if (!pi->sclk_dpm_key_disabled) {
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_DPM_Disable);
   if (smc_result != PPSMC_Result_OK)
    return -EINVAL;
  }

  if (!pi->mclk_dpm_key_disabled) {
   smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_Disable);
   if (smc_result != PPSMC_Result_OK)
    return -EINVAL;
  }
 }

 return 0;
}

static int ci_start_dpm(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;
 int ret;
 u32 tmp;

 tmp = RREG32_SMC(GENERAL_PWRMGT);
 tmp |= GLOBAL_PWRMGT_EN;
 WREG32_SMC(GENERAL_PWRMGT, tmp);

 tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
 tmp |= DYNAMIC_PM_EN;
 WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);

 ci_write_smc_soft_register(rdev, offsetof(SMU7_SoftRegisters, VoltageChangeTimeout), 0x1000);

 WREG32_P(BIF_LNCNT_RESET, 0, ~RESET_LNCNT_EN);

 smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Voltage_Cntl_Enable);
 if (smc_result != PPSMC_Result_OK)
  return -EINVAL;

 ret = ci_enable_sclk_mclk_dpm(rdev, true);
 if (ret)
  return ret;

 if (!pi->pcie_dpm_key_disabled) {
  smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PCIeDPM_Enable);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 return 0;
}

static int ci_freeze_sclk_mclk_dpm(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;

 if (!pi->need_update_smu7_dpm_table)
  return 0;

 if ((!pi->sclk_dpm_key_disabled) &&
     (pi->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_UPDATE_SCLK))) {
  smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_SCLKDPM_FreezeLevel);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 if ((!pi->mclk_dpm_key_disabled) &&
     (pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
  smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_FreezeLevel);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 return 0;
}

static int ci_stop_dpm(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 PPSMC_Result smc_result;
 int ret;
 u32 tmp;

 tmp = RREG32_SMC(GENERAL_PWRMGT);
 tmp &= ~GLOBAL_PWRMGT_EN;
 WREG32_SMC(GENERAL_PWRMGT, tmp);

 tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
 tmp &= ~DYNAMIC_PM_EN;
 WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);

 if (!pi->pcie_dpm_key_disabled) {
  smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PCIeDPM_Disable);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 ret = ci_enable_sclk_mclk_dpm(rdev, false);
 if (ret)
  return ret;

 smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Voltage_Cntl_Disable);
 if (smc_result != PPSMC_Result_OK)
  return -EINVAL;

 return 0;
}

static void ci_enable_sclk_control(struct radeon_device *rdev, bool enable)
{
 u32 tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);

 if (enable)
  tmp &= ~SCLK_PWRMGT_OFF;
 else
  tmp |= SCLK_PWRMGT_OFF;
 WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);
}

#if 0
static int ci_notify_hw_of_power_source(struct radeon_device *rdev,
     bool ac_power)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 struct radeon_cac_tdp_table *cac_tdp_table =
  rdev->pm.dpm.dyn_state.cac_tdp_table;
 u32 power_limit;

 if (ac_power)
  power_limit = (u32)(cac_tdp_table->maximum_power_delivery_limit * 256);
 else
  power_limit = (u32)(cac_tdp_table->battery_power_limit * 256);

 ci_set_power_limit(rdev, power_limit);

 if (pi->caps_automatic_dc_transition) {
  if (ac_power)
   ci_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC);
  else
   ci_send_msg_to_smc(rdev, PPSMC_MSG_Remove_DC_Clamp);
 }

 return 0;
}
#endif

static PPSMC_Result ci_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
{
 u32 tmp;
 int i;

 if (!ci_is_smc_running(rdev))
  return PPSMC_Result_Failed;

 WREG32(SMC_MESSAGE_0, msg);

 for (i = 0; i < rdev->usec_timeout; i++) {
  tmp = RREG32(SMC_RESP_0);
  if (tmp != 0)
   break;
  udelay(1);
 }
 tmp = RREG32(SMC_RESP_0);

 return (PPSMC_Result)tmp;
}

static PPSMC_Result ci_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
            PPSMC_Msg msg, u32 parameter)
{
 WREG32(SMC_MSG_ARG_0, parameter);
 return ci_send_msg_to_smc(rdev, msg);
}

static PPSMC_Result ci_send_msg_to_smc_return_parameter(struct radeon_device *rdev,
       PPSMC_Msg msg, u32 *parameter)
{
 PPSMC_Result smc_result;

 smc_result = ci_send_msg_to_smc(rdev, msg);

 if ((smc_result == PPSMC_Result_OK) && parameter)
  *parameter = RREG32(SMC_MSG_ARG_0);

 return smc_result;
}

static int ci_dpm_force_state_sclk(struct radeon_device *rdev, u32 n)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (!pi->sclk_dpm_key_disabled) {
  PPSMC_Result smc_result =
   ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SCLKDPM_SetEnabledMask, 1 << n);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 return 0;
}

static int ci_dpm_force_state_mclk(struct radeon_device *rdev, u32 n)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (!pi->mclk_dpm_key_disabled) {
  PPSMC_Result smc_result =
   ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_MCLKDPM_SetEnabledMask, 1 << n);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 return 0;
}

static int ci_dpm_force_state_pcie(struct radeon_device *rdev, u32 n)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (!pi->pcie_dpm_key_disabled) {
  PPSMC_Result smc_result =
   ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_PCIeDPM_ForceLevel, n);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 return 0;
}

static int ci_set_power_limit(struct radeon_device *rdev, u32 n)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
  PPSMC_Result smc_result =
   ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_PkgPwrSetLimit, n);
  if (smc_result != PPSMC_Result_OK)
   return -EINVAL;
 }

 return 0;
}

static int ci_set_overdrive_target_tdp(struct radeon_device *rdev,
           u32 target_tdp)
{
 PPSMC_Result smc_result =
  ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_OverDriveSetTargetTdp, target_tdp);
 if (smc_result != PPSMC_Result_OK)
  return -EINVAL;
 return 0;
}

#if 0
static int ci_set_boot_state(struct radeon_device *rdev)
{
 return ci_enable_sclk_mclk_dpm(rdev, false);
}
#endif

static u32 ci_get_average_sclk_freq(struct radeon_device *rdev)
{
 u32 sclk_freq;
 PPSMC_Result smc_result =
  ci_send_msg_to_smc_return_parameter(rdev,
          PPSMC_MSG_API_GetSclkFrequency,
          &sclk_freq);
 if (smc_result != PPSMC_Result_OK)
  sclk_freq = 0;

 return sclk_freq;
}

static u32 ci_get_average_mclk_freq(struct radeon_device *rdev)
{
 u32 mclk_freq;
 PPSMC_Result smc_result =
  ci_send_msg_to_smc_return_parameter(rdev,
          PPSMC_MSG_API_GetMclkFrequency,
          &mclk_freq);
 if (smc_result != PPSMC_Result_OK)
  mclk_freq = 0;

 return mclk_freq;
}

static void ci_dpm_start_smc(struct radeon_device *rdev)
{
 int i;

 ci_program_jump_on_start(rdev);
 ci_start_smc_clock(rdev);
 ci_start_smc(rdev);
 for (i = 0; i < rdev->usec_timeout; i++) {
  if (RREG32_SMC(FIRMWARE_FLAGS) & INTERRUPTS_ENABLED)
   break;
 }
}

static void ci_dpm_stop_smc(struct radeon_device *rdev)
{
 ci_reset_smc(rdev);
 ci_stop_smc_clock(rdev);
}

static int ci_process_firmware_header(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 tmp;
 int ret;

 ret = ci_read_smc_sram_dword(rdev,
         SMU7_FIRMWARE_HEADER_LOCATION +
         offsetof(SMU7_Firmware_Header, DpmTable),
         &tmp, pi->sram_end);
 if (ret)
  return ret;

 pi->dpm_table_start = tmp;

 ret = ci_read_smc_sram_dword(rdev,
         SMU7_FIRMWARE_HEADER_LOCATION +
         offsetof(SMU7_Firmware_Header, SoftRegisters),
         &tmp, pi->sram_end);
 if (ret)
  return ret;

 pi->soft_regs_start = tmp;

 ret = ci_read_smc_sram_dword(rdev,
         SMU7_FIRMWARE_HEADER_LOCATION +
         offsetof(SMU7_Firmware_Header, mcRegisterTable),
         &tmp, pi->sram_end);
 if (ret)
  return ret;

 pi->mc_reg_table_start = tmp;

 ret = ci_read_smc_sram_dword(rdev,
         SMU7_FIRMWARE_HEADER_LOCATION +
         offsetof(SMU7_Firmware_Header, FanTable),
         &tmp, pi->sram_end);
 if (ret)
  return ret;

 pi->fan_table_start = tmp;

 ret = ci_read_smc_sram_dword(rdev,
         SMU7_FIRMWARE_HEADER_LOCATION +
         offsetof(SMU7_Firmware_Header, mcArbDramTimingTable),
         &tmp, pi->sram_end);
 if (ret)
  return ret;

 pi->arb_table_start = tmp;

 return 0;
}

static void ci_read_clock_registers(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 pi->clock_registers.cg_spll_func_cntl =
  RREG32_SMC(CG_SPLL_FUNC_CNTL);
 pi->clock_registers.cg_spll_func_cntl_2 =
  RREG32_SMC(CG_SPLL_FUNC_CNTL_2);
 pi->clock_registers.cg_spll_func_cntl_3 =
  RREG32_SMC(CG_SPLL_FUNC_CNTL_3);
 pi->clock_registers.cg_spll_func_cntl_4 =
  RREG32_SMC(CG_SPLL_FUNC_CNTL_4);
 pi->clock_registers.cg_spll_spread_spectrum =
  RREG32_SMC(CG_SPLL_SPREAD_SPECTRUM);
 pi->clock_registers.cg_spll_spread_spectrum_2 =
  RREG32_SMC(CG_SPLL_SPREAD_SPECTRUM_2);
 pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
 pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
 pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
 pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
 pi->clock_registers.mpll_func_cntl = RREG32(MPLL_FUNC_CNTL);
 pi->clock_registers.mpll_func_cntl_1 = RREG32(MPLL_FUNC_CNTL_1);
 pi->clock_registers.mpll_func_cntl_2 = RREG32(MPLL_FUNC_CNTL_2);
 pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
 pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
}

static void ci_init_sclk_t(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 pi->low_sclk_interrupt_t = 0;
}

static void ci_enable_thermal_protection(struct radeon_device *rdev,
      bool enable)
{
 u32 tmp = RREG32_SMC(GENERAL_PWRMGT);

 if (enable)
  tmp &= ~THERMAL_PROTECTION_DIS;
 else
  tmp |= THERMAL_PROTECTION_DIS;
 WREG32_SMC(GENERAL_PWRMGT, tmp);
}

static void ci_enable_acpi_power_management(struct radeon_device *rdev)
{
 u32 tmp = RREG32_SMC(GENERAL_PWRMGT);

 tmp |= STATIC_PM_EN;

 WREG32_SMC(GENERAL_PWRMGT, tmp);
}

#if 0
static int ci_enter_ulp_state(struct radeon_device *rdev)
{

 WREG32(SMC_MESSAGE_0, PPSMC_MSG_SwitchToMinimumPower);

 udelay(25000);

 return 0;
}

static int ci_exit_ulp_state(struct radeon_device *rdev)
{
 int i;

 WREG32(SMC_MESSAGE_0, PPSMC_MSG_ResumeFromMinimumPower);

 udelay(7000);

 for (i = 0; i < rdev->usec_timeout; i++) {
  if (RREG32(SMC_RESP_0) == 1)
   break;
  udelay(1000);
 }

 return 0;
}
#endif

static int ci_notify_smc_display_change(struct radeon_device *rdev,
     bool has_display)
{
 PPSMC_Msg msg = has_display ? PPSMC_MSG_HasDisplay : PPSMC_MSG_NoDisplay;

 return (ci_send_msg_to_smc(rdev, msg) == PPSMC_Result_OK) ?  0 : -EINVAL;
}

static int ci_enable_ds_master_switch(struct radeon_device *rdev,
          bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);

 if (enable) {
  if (pi->caps_sclk_ds) {
   if (ci_send_msg_to_smc(rdev, PPSMC_MSG_MASTER_DeepSleep_ON) != PPSMC_Result_OK)
    return -EINVAL;
  } else {
   if (ci_send_msg_to_smc(rdev, PPSMC_MSG_MASTER_DeepSleep_OFF) != PPSMC_Result_OK)
    return -EINVAL;
  }
 } else {
  if (pi->caps_sclk_ds) {
   if (ci_send_msg_to_smc(rdev, PPSMC_MSG_MASTER_DeepSleep_OFF) != PPSMC_Result_OK)
    return -EINVAL;
  }
 }

 return 0;
}

static void ci_program_display_gap(struct radeon_device *rdev)
{
 u32 tmp = RREG32_SMC(CG_DISPLAY_GAP_CNTL);
 u32 pre_vbi_time_in_us;
 u32 frame_time_in_us;
 u32 ref_clock = rdev->clock.spll.reference_freq;
 u32 refresh_rate = r600_dpm_get_vrefresh(rdev);
 u32 vblank_time = r600_dpm_get_vblank_time(rdev);

 tmp &= ~DISP_GAP_MASK;
 if (rdev->pm.dpm.new_active_crtc_count > 0)
  tmp |= DISP_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
 else
  tmp |= DISP_GAP(R600_PM_DISPLAY_GAP_IGNORE);
 WREG32_SMC(CG_DISPLAY_GAP_CNTL, tmp);

 if (refresh_rate == 0)
  refresh_rate = 60;
 if (vblank_time == 0xffffffff)
  vblank_time = 500;
 frame_time_in_us = 1000000 / refresh_rate;
 pre_vbi_time_in_us =
  frame_time_in_us - 200 - vblank_time;
 tmp = pre_vbi_time_in_us * (ref_clock / 100);

 WREG32_SMC(CG_DISPLAY_GAP_CNTL2, tmp);
 ci_write_smc_soft_register(rdev, offsetof(SMU7_SoftRegisters, PreVBlankGap), 0x64);
 ci_write_smc_soft_register(rdev, offsetof(SMU7_SoftRegisters, VBlankTimeout), (frame_time_in_us - pre_vbi_time_in_us));


 ci_notify_smc_display_change(rdev, (rdev->pm.dpm.new_active_crtc_count == 1));

}

static void ci_enable_spread_spectrum(struct radeon_device *rdev, bool enable)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 tmp;

 if (enable) {
  if (pi->caps_sclk_ss_support) {
   tmp = RREG32_SMC(GENERAL_PWRMGT);
   tmp |= DYN_SPREAD_SPECTRUM_EN;
   WREG32_SMC(GENERAL_PWRMGT, tmp);
  }
 } else {
  tmp = RREG32_SMC(CG_SPLL_SPREAD_SPECTRUM);
  tmp &= ~SSEN;
  WREG32_SMC(CG_SPLL_SPREAD_SPECTRUM, tmp);

  tmp = RREG32_SMC(GENERAL_PWRMGT);
  tmp &= ~DYN_SPREAD_SPECTRUM_EN;
  WREG32_SMC(GENERAL_PWRMGT, tmp);
 }
}

static void ci_program_sstp(struct radeon_device *rdev)
{
 WREG32_SMC(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
}

static void ci_enable_display_gap(struct radeon_device *rdev)
{
 u32 tmp = RREG32_SMC(CG_DISPLAY_GAP_CNTL);

 tmp &= ~(DISP_GAP_MASK | DISP_GAP_MCHG_MASK);
 tmp |= (DISP_GAP(R600_PM_DISPLAY_GAP_IGNORE) |
  DISP_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK));

 WREG32_SMC(CG_DISPLAY_GAP_CNTL, tmp);
}

static void ci_program_vc(struct radeon_device *rdev)
{
 u32 tmp;

 tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
 tmp &= ~(RESET_SCLK_CNT | RESET_BUSY_CNT);
 WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);

 WREG32_SMC(CG_FTV_0, CISLANDS_VRC_DFLT0);
 WREG32_SMC(CG_FTV_1, CISLANDS_VRC_DFLT1);
 WREG32_SMC(CG_FTV_2, CISLANDS_VRC_DFLT2);
 WREG32_SMC(CG_FTV_3, CISLANDS_VRC_DFLT3);
 WREG32_SMC(CG_FTV_4, CISLANDS_VRC_DFLT4);
 WREG32_SMC(CG_FTV_5, CISLANDS_VRC_DFLT5);
 WREG32_SMC(CG_FTV_6, CISLANDS_VRC_DFLT6);
 WREG32_SMC(CG_FTV_7, CISLANDS_VRC_DFLT7);
}

static void ci_clear_vc(struct radeon_device *rdev)
{
 u32 tmp;

 tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
 tmp |= (RESET_SCLK_CNT | RESET_BUSY_CNT);
 WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);

 WREG32_SMC(CG_FTV_0, 0);
 WREG32_SMC(CG_FTV_1, 0);
 WREG32_SMC(CG_FTV_2, 0);
 WREG32_SMC(CG_FTV_3, 0);
 WREG32_SMC(CG_FTV_4, 0);
 WREG32_SMC(CG_FTV_5, 0);
 WREG32_SMC(CG_FTV_6, 0);
 WREG32_SMC(CG_FTV_7, 0);
}

static int ci_upload_firmware(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 int i;

 for (i = 0; i < rdev->usec_timeout; i++) {
  if (RREG32_SMC(RCU_UC_EVENTS) & BOOT_SEQ_DONE)
   break;
 }
 WREG32_SMC(SMC_SYSCON_MISC_CNTL, 1);

 ci_stop_smc_clock(rdev);
 ci_reset_smc(rdev);

 return ci_load_smc_ucode(rdev, pi->sram_end);

}

static int ci_get_svi2_voltage_table(struct radeon_device *rdev,
         struct radeon_clock_voltage_dependency_table *voltage_dependency_table,
         struct atom_voltage_table *voltage_table)
{
 u32 i;

 if (voltage_dependency_table == NULL)
  return -EINVAL;

 voltage_table->mask_low = 0;
 voltage_table->phase_delay = 0;

 voltage_table->count = voltage_dependency_table->count;
 for (i = 0; i < voltage_table->count; i++) {
  voltage_table->entries[i].value = voltage_dependency_table->entries[i].v;
  voltage_table->entries[i].smio_low = 0;
 }

 return 0;
}

static int ci_construct_voltage_tables(struct radeon_device *rdev)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 int ret;

 if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO) {
  ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
          VOLTAGE_OBJ_GPIO_LUT,
          &pi->vddc_voltage_table);
  if (ret)
   return ret;
 } else if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
  ret = ci_get_svi2_voltage_table(rdev,
      &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
      &pi->vddc_voltage_table);
  if (ret)
   return ret;
 }

 if (pi->vddc_voltage_table.count > SMU7_MAX_LEVELS_VDDC)
  si_trim_voltage_table_to_fit_state_table(rdev, SMU7_MAX_LEVELS_VDDC,
        &pi->vddc_voltage_table);

 if (pi->vddci_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO) {
  ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDCI,
          VOLTAGE_OBJ_GPIO_LUT,
          &pi->vddci_voltage_table);
  if (ret)
   return ret;
 } else if (pi->vddci_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
  ret = ci_get_svi2_voltage_table(rdev,
      &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
      &pi->vddci_voltage_table);
  if (ret)
   return ret;
 }

 if (pi->vddci_voltage_table.count > SMU7_MAX_LEVELS_VDDCI)
  si_trim_voltage_table_to_fit_state_table(rdev, SMU7_MAX_LEVELS_VDDCI,
        &pi->vddci_voltage_table);

 if (pi->mvdd_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO) {
  ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_MVDDC,
          VOLTAGE_OBJ_GPIO_LUT,
          &pi->mvdd_voltage_table);
  if (ret)
   return ret;
 } else if (pi->mvdd_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
  ret = ci_get_svi2_voltage_table(rdev,
      &rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk,
      &pi->mvdd_voltage_table);
  if (ret)
   return ret;
 }

 if (pi->mvdd_voltage_table.count > SMU7_MAX_LEVELS_MVDD)
  si_trim_voltage_table_to_fit_state_table(rdev, SMU7_MAX_LEVELS_MVDD,
        &pi->mvdd_voltage_table);

 return 0;
}

static void ci_populate_smc_voltage_table(struct radeon_device *rdev,
       struct atom_voltage_table_entry *voltage_table,
       SMU7_Discrete_VoltageLevel *smc_voltage_table)
{
 int ret;

 ret = ci_get_std_voltage_value_sidd(rdev, voltage_table,
         &smc_voltage_table->StdVoltageHiSidd,
         &smc_voltage_table->StdVoltageLoSidd);

 if (ret) {
  smc_voltage_table->StdVoltageHiSidd = voltage_table->value * VOLTAGE_SCALE;
  smc_voltage_table->StdVoltageLoSidd = voltage_table->value * VOLTAGE_SCALE;
 }

 smc_voltage_table->Voltage = cpu_to_be16(voltage_table->value * VOLTAGE_SCALE);
 smc_voltage_table->StdVoltageHiSidd =
  cpu_to_be16(smc_voltage_table->StdVoltageHiSidd);
 smc_voltage_table->StdVoltageLoSidd =
  cpu_to_be16(smc_voltage_table->StdVoltageLoSidd);
}

static int ci_populate_smc_vddc_table(struct radeon_device *rdev,
          SMU7_Discrete_DpmTable *table)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 unsigned int count;

 table->VddcLevelCount = pi->vddc_voltage_table.count;
 for (count = 0; count < table->VddcLevelCount; count++) {
  ci_populate_smc_voltage_table(rdev,
           &pi->vddc_voltage_table.entries[count],
           &table->VddcLevel[count]);

  if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO)
   table->VddcLevel[count].Smio |=
    pi->vddc_voltage_table.entries[count].smio_low;
  else
   table->VddcLevel[count].Smio = 0;
 }
 table->VddcLevelCount = cpu_to_be32(table->VddcLevelCount);

 return 0;
}

static int ci_populate_smc_vddci_table(struct radeon_device *rdev,
           SMU7_Discrete_DpmTable *table)
{
 unsigned int count;
 struct ci_power_info *pi = ci_get_pi(rdev);

 table->VddciLevelCount = pi->vddci_voltage_table.count;
 for (count = 0; count < table->VddciLevelCount; count++) {
  ci_populate_smc_voltage_table(rdev,
           &pi->vddci_voltage_table.entries[count],
           &table->VddciLevel[count]);

  if (pi->vddci_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO)
   table->VddciLevel[count].Smio |=
    pi->vddci_voltage_table.entries[count].smio_low;
  else
   table->VddciLevel[count].Smio = 0;
 }
 table->VddciLevelCount = cpu_to_be32(table->VddciLevelCount);

 return 0;
}

static int ci_populate_smc_mvdd_table(struct radeon_device *rdev,
          SMU7_Discrete_DpmTable *table)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 unsigned int count;

 table->MvddLevelCount = pi->mvdd_voltage_table.count;
 for (count = 0; count < table->MvddLevelCount; count++) {
  ci_populate_smc_voltage_table(rdev,
           &pi->mvdd_voltage_table.entries[count],
           &table->MvddLevel[count]);

  if (pi->mvdd_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO)
   table->MvddLevel[count].Smio |=
    pi->mvdd_voltage_table.entries[count].smio_low;
  else
   table->MvddLevel[count].Smio = 0;
 }
 table->MvddLevelCount = cpu_to_be32(table->MvddLevelCount);

 return 0;
}

static int ci_populate_smc_voltage_tables(struct radeon_device *rdev,
       SMU7_Discrete_DpmTable *table)
{
 int ret;

 ret = ci_populate_smc_vddc_table(rdev, table);
 if (ret)
  return ret;

 ret = ci_populate_smc_vddci_table(rdev, table);
 if (ret)
  return ret;

 ret = ci_populate_smc_mvdd_table(rdev, table);
 if (ret)
  return ret;

 return 0;
}

static int ci_populate_mvdd_value(struct radeon_device *rdev, u32 mclk,
      SMU7_Discrete_VoltageLevel *voltage)
{
 struct ci_power_info *pi = ci_get_pi(rdev);
 u32 i = 0;

 if (pi->mvdd_control != CISLANDS_VOLTAGE_CONTROL_NONE) {
  for (i = 0; i < rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.count; i++) {
   if (mclk <= rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.entries[i].clk) {
    voltage->Voltage = pi->mvdd_voltage_table.entries[i].value;
    break;
   }
  }

  if (i >= rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.count)
   return -EINVAL;
 }

 return -EINVAL;
}

static int ci_get_std_voltage_value_sidd(struct radeon_device *rdev,
      struct atom_voltage_table_entry *voltage_table,
      u16 *std_voltage_hi_sidd, u16 *std_voltage_lo_sidd)
{
 u16 v_index, idx;
 bool voltage_found = false;
 *std_voltage_hi_sidd = voltage_table->value * VOLTAGE_SCALE;
 *std_voltage_lo_sidd = voltage_table->value * VOLTAGE_SCALE;

 if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries == NULL)
  return -EINVAL;

 if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries) {
  for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
   if (voltage_table->value ==
       rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
    voltage_found = true;
    if ((u32)v_index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
     idx = v_index;
    else
     idx = rdev->pm.dpm.dyn_state.cac_leakage_table.count - 1;
    *std_voltage_lo_sidd =
     rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].vddc * VOLTAGE_SCALE;
    *std_voltage_hi_sidd =
     rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].leakage * VOLTAGE_SCALE;
    break;
   }
  }

  if (!voltage_found) {
   for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
    if (voltage_table->value <=
        rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
     voltage_found = true;
     if ((u32)v_index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
      idx = v_index;
     else
      idx = rdev->pm.dpm.dyn_state.cac_leakage_table.count - 1;
     *std_voltage_lo_sidd =
      rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].vddc * VOLTAGE_SCALE;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.