drm/radeon/kms: add dpm support for SI (v7)
authorAlex Deucher <alexander.deucher@amd.com>
Tue, 25 Jun 2013 21:56:16 +0000 (17:56 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 27 Jun 2013 23:40:05 +0000 (19:40 -0400)
This adds dpm support for SI asics.  This includes:
- dynamic engine clock scaling
- dynamic memory clock scaling
- dynamic voltage scaling
- dynamic pcie gen1/gen2/gen3 switching
- power containment
- shader power scaling

Set radeon.dpm=1 to enable.

v2: enable hainan support, rebase
v3: guard acpi stuff
v4: fix 64 bit math
v5: fix 64 bit div harder
v6: fix thermal interrupt check noticed by Jerome
v7: attempt fix state enable

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
18 files changed:
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atombios.h
drivers/gpu/drm/radeon/ni_dpm.c
drivers/gpu/drm/radeon/ni_dpm.h
drivers/gpu/drm/radeon/ppsmc.h
drivers/gpu/drm/radeon/r600_dpm.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_ucode.h
drivers/gpu/drm/radeon/rv770_dpm.h
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/si_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/si_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/si_smc.c [new file with mode: 0644]
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/radeon/sislands_smc.h [new file with mode: 0644]

index 32d1c7f..c3df52c 100644 (file)
@@ -79,7 +79,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
        si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
        r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
        rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
-       trinity_smc.o ni_dpm.o
+       trinity_smc.o ni_dpm.o si_smc.o si_dpm.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
 radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
index 7ba9588..16b120c 100644 (file)
@@ -7763,6 +7763,9 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER
 #define ATOM_PP_PLATFORM_CAP_REGULATOR_HOT 0x00010000               // Enable the 'regulator hot' feature.
 #define ATOM_PP_PLATFORM_CAP_BACO          0x00020000               // Does the driver supports BACO state.
 #define ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE   0x00040000           // Does the driver supports new CAC voltage table.
+#define ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY   0x00080000     // Does the driver supports revert GPIO5 polarity.
+#define ATOM_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17   0x00100000     // Does the driver supports thermal2GPIO17.
+#define ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE   0x00200000   // Does the driver supports VR HOT GPIO Configurable.
 
 typedef struct _ATOM_PPLIB_POWERPLAYTABLE
 {
index 8e6b23a..649d949 100644 (file)
@@ -719,7 +719,7 @@ static const u32 cayman_sysls_enable[] =
 struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
 struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
 
-static struct ni_power_info *ni_get_pi(struct radeon_device *rdev)
+struct ni_power_info *ni_get_pi(struct radeon_device *rdev)
 {
         struct ni_power_info *pi = rdev->pm.dpm.priv;
 
@@ -1471,8 +1471,8 @@ static int ni_populate_smc_tdp_limits(struct radeon_device *rdev,
        return 0;
 }
 
-static int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
-                                      u32 arb_freq_src, u32 arb_freq_dest)
+int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
+                               u32 arb_freq_src, u32 arb_freq_dest)
 {
        u32 mc_arb_dram_timing;
        u32 mc_arb_dram_timing2;
@@ -3488,8 +3488,8 @@ void ni_dpm_setup_asic(struct radeon_device *rdev)
        rv770_enable_acpi_pm(rdev);
 }
 
-static void ni_update_current_ps(struct radeon_device *rdev,
-                                struct radeon_ps *rps)
+void ni_update_current_ps(struct radeon_device *rdev,
+                         struct radeon_ps *rps)
 {
        struct ni_ps *new_ps = ni_get_ps(rps);
        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
@@ -3500,8 +3500,8 @@ static void ni_update_current_ps(struct radeon_device *rdev,
        eg_pi->current_rps.ps_priv = &ni_pi->current_ps;
 }
 
-static void ni_update_requested_ps(struct radeon_device *rdev,
-                                  struct radeon_ps *rps)
+void ni_update_requested_ps(struct radeon_device *rdev,
+                           struct radeon_ps *rps)
 {
        struct ni_ps *new_ps = ni_get_ps(rps);
        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
@@ -4192,8 +4192,12 @@ void ni_dpm_print_power_state(struct radeon_device *rdev,
        printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
        for (i = 0; i < ps->performance_level_count; i++) {
                pl = &ps->performance_levels[i];
-               printk("\t\tpower level 0    sclk: %u mclk: %u vddc: %u vddci: %u\n",
-                      pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+               if (rdev->family >= CHIP_TAHITI)
+                       printk("\t\tpower level 0    sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+                              pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+               else
+                       printk("\t\tpower level 0    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                              pl->sclk, pl->mclk, pl->vddc, pl->vddci);
        }
        r600_dpm_print_ps_status(rdev, rps);
 }
index 59c1692..8874424 100644 (file)
@@ -231,4 +231,11 @@ struct ni_power_info {
 #define NISLANDS_DPM2_SQ_RAMP_STI_SIZE                  0x1E
 #define NISLANDS_DPM2_SQ_RAMP_LTI_RATIO                 0xF
 
+int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
+                               u32 arb_freq_src, u32 arb_freq_dest);
+void ni_update_current_ps(struct radeon_device *rdev,
+                         struct radeon_ps *rps);
+void ni_update_requested_ps(struct radeon_device *rdev,
+                           struct radeon_ps *rps);
+
 #endif
index 0f6ccce..8fb1113 100644 (file)
@@ -26,6 +26,9 @@
 #pragma pack(push, 1)
 
 #define PPSMC_SWSTATE_FLAG_DC                           0x01
+#define PPSMC_SWSTATE_FLAG_UVD                          0x02
+#define PPSMC_SWSTATE_FLAG_VCE                          0x04
+#define PPSMC_SWSTATE_FLAG_PCIE_X1                      0x08
 
 #define PPSMC_THERMAL_PROTECT_TYPE_INTERNAL             0x00
 #define PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL             0x01
 #define PPSMC_SYSTEMFLAG_GDDR5                          0x04
 #define PPSMC_SYSTEMFLAG_DISABLE_BABYSTEP               0x08
 #define PPSMC_SYSTEMFLAG_REGULATOR_HOT                  0x10
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_ANALOG           0x20
+#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO        0x40
 
 #define PPSMC_EXTRAFLAGS_AC2DC_ACTION_MASK              0x07
 #define PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK     0x08
 #define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTODPMLOWSTATE   0x00
 #define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE  0x01
+#define PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH      0x02
 
 #define PPSMC_DISPLAY_WATERMARK_LOW                     0
 #define PPSMC_DISPLAY_WATERMARK_HIGH                    1
 
 #define PPSMC_STATEFLAG_AUTO_PULSE_SKIP    0x01
 #define PPSMC_STATEFLAG_POWERBOOST         0x02
+#define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20
+#define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS   0x40
 
 #define PPSMC_Result_OK             ((uint8_t)0x01)
 #define PPSMC_Result_Failed         ((uint8_t)0xFF)
@@ -80,9 +88,14 @@ typedef uint8_t PPSMC_Result;
 #define PPSMC_CACLongTermAvgEnable          ((uint8_t)0x6E)
 #define PPSMC_CACLongTermAvgDisable         ((uint8_t)0x6F)
 #define PPSMC_MSG_CollectCAC_PowerCorreln   ((uint8_t)0x7A)
+#define PPSMC_FlushDataCache                ((uint8_t)0x80)
 #define PPSMC_MSG_SetEnabledLevels          ((uint8_t)0x82)
 #define PPSMC_MSG_SetForcedLevels           ((uint8_t)0x83)
 #define PPSMC_MSG_ResetToDefaults           ((uint8_t)0x84)
+#define PPSMC_MSG_EnableDTE                 ((uint8_t)0x87)
+#define PPSMC_MSG_DisableDTE                ((uint8_t)0x88)
+#define PPSMC_MSG_ThrottleOVRDSCLKDS        ((uint8_t)0x96)
+#define PPSMC_MSG_CancelThrottleOVRDSCLKDS  ((uint8_t)0x97)
 
 /* TN */
 #define PPSMC_MSG_DPM_Config                ((uint32_t) 0x102)
index e7684c6..76368c0 100644 (file)
@@ -906,6 +906,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
            sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) {
                rdev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit);
                rdev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit);
+               rdev->pm.dpm.near_tdp_limit_adjusted = rdev->pm.dpm.near_tdp_limit;
                rdev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit);
                if (rdev->pm.dpm.tdp_od_limit)
                        rdev->pm.dpm.power_control = true;
index 9de8ae2..a424949 100644 (file)
@@ -1359,6 +1359,7 @@ struct radeon_dpm {
        struct radeon_dpm_fan fan;
        u32 tdp_limit;
        u32 near_tdp_limit;
+       u32 near_tdp_limit_adjusted;
        u32 sq_ramping_threshold;
        u32 cac_leakage;
        u16 tdp_od_limit;
index c20ec37..c3df589 100644 (file)
@@ -2262,6 +2262,20 @@ static struct radeon_asic si_asic = {
                .set_uvd_clocks = &si_set_uvd_clocks,
                .get_temperature = &si_get_temp,
        },
+       .dpm = {
+               .init = &si_dpm_init,
+               .setup_asic = &si_dpm_setup_asic,
+               .enable = &si_dpm_enable,
+               .disable = &si_dpm_disable,
+               .pre_set_power_state = &si_dpm_pre_set_power_state,
+               .set_power_state = &si_dpm_set_power_state,
+               .post_set_power_state = &si_dpm_post_set_power_state,
+               .display_configuration_changed = &si_dpm_display_configuration_changed,
+               .fini = &si_dpm_fini,
+               .get_sclk = &ni_dpm_get_sclk,
+               .get_mclk = &ni_dpm_get_mclk,
+               .print_power_state = &ni_dpm_print_power_state,
+       },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
index 2b4a922..2497d0a 100644 (file)
@@ -658,6 +658,15 @@ u32 si_get_xclk(struct radeon_device *rdev);
 uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev);
 int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
 int si_get_temp(struct radeon_device *rdev);
+int si_dpm_init(struct radeon_device *rdev);
+void si_dpm_setup_asic(struct radeon_device *rdev);
+int si_dpm_enable(struct radeon_device *rdev);
+void si_dpm_disable(struct radeon_device *rdev);
+int si_dpm_pre_set_power_state(struct radeon_device *rdev);
+int si_dpm_set_power_state(struct radeon_device *rdev);
+void si_dpm_post_set_power_state(struct radeon_device *rdev);
+void si_dpm_fini(struct radeon_device *rdev);
+void si_dpm_display_configuration_changed(struct radeon_device *rdev);
 
 /* DCE8 - CIK */
 void dce8_bandwidth_update(struct radeon_device *rdev);
index 2f70c1b..9737bae 100644 (file)
@@ -1096,6 +1096,11 @@ int radeon_pm_init(struct radeon_device *rdev)
        case CHIP_CAICOS:
        case CHIP_CAYMAN:
        case CHIP_ARUBA:
+       case CHIP_TAHITI:
+       case CHIP_PITCAIRN:
+       case CHIP_VERDE:
+       case CHIP_OLAND:
+       case CHIP_HAINAN:
                if (radeon_dpm == 1)
                        rdev->pm.pm_method = PM_METHOD_DPM;
                else
index 4fe9237..d8b05f7 100644 (file)
 #define CAYMAN_SMC_INT_VECTOR_START  0xffc0
 #define CAYMAN_SMC_INT_VECTOR_SIZE   0x0040
 
+#define TAHITI_SMC_UCODE_START       0x10000
+#define TAHITI_SMC_UCODE_SIZE        0xf458
+
+#define PITCAIRN_SMC_UCODE_START     0x10000
+#define PITCAIRN_SMC_UCODE_SIZE      0xe9f4
+
+#define VERDE_SMC_UCODE_START        0x10000
+#define VERDE_SMC_UCODE_SIZE         0xebe4
+
+#define OLAND_SMC_UCODE_START        0x10000
+#define OLAND_SMC_UCODE_SIZE         0xe7b4
+
+#define HAINAN_SMC_UCODE_START       0x10000
+#define HAINAN_SMC_UCODE_SIZE        0xe67C
+
 #endif
index 7fa14b9..f1e1fcf 100644 (file)
@@ -144,6 +144,7 @@ struct rv7xx_pl {
        u16 vddc;
        u16 vddci; /* eg+ only */
        u32 flags;
+       enum radeon_pcie_gen pcie_gen; /* si+ only */
 };
 
 struct rv7xx_ps {
index 7d797f4..6c8caaf 100644 (file)
@@ -41,26 +41,31 @@ MODULE_FIRMWARE("radeon/TAHITI_me.bin");
 MODULE_FIRMWARE("radeon/TAHITI_ce.bin");
 MODULE_FIRMWARE("radeon/TAHITI_mc.bin");
 MODULE_FIRMWARE("radeon/TAHITI_rlc.bin");
+MODULE_FIRMWARE("radeon/TAHITI_smc.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_pfp.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_me.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_ce.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_mc.bin");
 MODULE_FIRMWARE("radeon/PITCAIRN_rlc.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_smc.bin");
 MODULE_FIRMWARE("radeon/VERDE_pfp.bin");
 MODULE_FIRMWARE("radeon/VERDE_me.bin");
 MODULE_FIRMWARE("radeon/VERDE_ce.bin");
 MODULE_FIRMWARE("radeon/VERDE_mc.bin");
 MODULE_FIRMWARE("radeon/VERDE_rlc.bin");
+MODULE_FIRMWARE("radeon/VERDE_smc.bin");
 MODULE_FIRMWARE("radeon/OLAND_pfp.bin");
 MODULE_FIRMWARE("radeon/OLAND_me.bin");
 MODULE_FIRMWARE("radeon/OLAND_ce.bin");
 MODULE_FIRMWARE("radeon/OLAND_mc.bin");
 MODULE_FIRMWARE("radeon/OLAND_rlc.bin");
+MODULE_FIRMWARE("radeon/OLAND_smc.bin");
 MODULE_FIRMWARE("radeon/HAINAN_pfp.bin");
 MODULE_FIRMWARE("radeon/HAINAN_me.bin");
 MODULE_FIRMWARE("radeon/HAINAN_ce.bin");
 MODULE_FIRMWARE("radeon/HAINAN_mc.bin");
 MODULE_FIRMWARE("radeon/HAINAN_rlc.bin");
+MODULE_FIRMWARE("radeon/HAINAN_smc.bin");
 
 static void si_pcie_gen3_enable(struct radeon_device *rdev);
 static void si_program_aspm(struct radeon_device *rdev);
@@ -1540,6 +1545,7 @@ static int si_init_microcode(struct radeon_device *rdev)
        const char *chip_name;
        const char *rlc_chip_name;
        size_t pfp_req_size, me_req_size, ce_req_size, rlc_req_size, mc_req_size;
+       size_t smc_req_size;
        char fw_name[30];
        int err;
 
@@ -1561,6 +1567,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = SI_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(TAHITI_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_PITCAIRN:
                chip_name = "PITCAIRN";
@@ -1570,6 +1577,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = SI_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(PITCAIRN_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_VERDE:
                chip_name = "VERDE";
@@ -1579,6 +1587,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = SI_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(VERDE_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_OLAND:
                chip_name = "OLAND";
@@ -1588,6 +1597,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = OLAND_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(OLAND_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_HAINAN:
                chip_name = "HAINAN";
@@ -1597,6 +1607,7 @@ static int si_init_microcode(struct radeon_device *rdev)
                ce_req_size = SI_CE_UCODE_SIZE * 4;
                rlc_req_size = SI_RLC_UCODE_SIZE * 4;
                mc_req_size = OLAND_MC_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(HAINAN_SMC_UCODE_SIZE, 4);
                break;
        default: BUG();
        }
@@ -1659,6 +1670,17 @@ static int si_init_microcode(struct radeon_device *rdev)
                err = -EINVAL;
        }
 
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+       err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->smc_fw->size != smc_req_size) {
+               printk(KERN_ERR
+                      "si_smc: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->smc_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
 out:
        platform_device_unregister(pdev);
 
@@ -1677,6 +1699,8 @@ out:
                rdev->rlc_fw = NULL;
                release_firmware(rdev->mc_fw);
                rdev->mc_fw = NULL;
+               release_firmware(rdev->smc_fw);
+               rdev->smc_fw = NULL;
        }
        return err;
 }
@@ -5420,6 +5444,7 @@ int si_irq_set(struct radeon_device *rdev)
        u32 grbm_int_cntl = 0;
        u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0;
        u32 dma_cntl, dma_cntl1;
+       u32 thermal_int = 0;
 
        if (!rdev->irq.installed) {
                WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
@@ -5445,6 +5470,9 @@ int si_irq_set(struct radeon_device *rdev)
        dma_cntl = RREG32(DMA_CNTL + DMA0_REGISTER_OFFSET) & ~TRAP_ENABLE;
        dma_cntl1 = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET) & ~TRAP_ENABLE;
 
+       thermal_int = RREG32(CG_THERMAL_INT) &
+               ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+
        /* enable CP interrupts on all rings */
        if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) {
                DRM_DEBUG("si_irq_set: sw int gfx\n");
@@ -5531,6 +5559,11 @@ int si_irq_set(struct radeon_device *rdev)
 
        WREG32(GRBM_INT_CNTL, grbm_int_cntl);
 
+       if (rdev->irq.dpm_thermal) {
+               DRM_DEBUG("dpm thermal\n");
+               thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+       }
+
        if (rdev->num_crtc >= 2) {
                WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1);
                WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
@@ -5566,6 +5599,8 @@ int si_irq_set(struct radeon_device *rdev)
                WREG32(DC_HPD6_INT_CONTROL, hpd6);
        }
 
+       WREG32(CG_THERMAL_INT, thermal_int);
+
        return 0;
 }
 
@@ -5730,6 +5765,7 @@ int si_irq_process(struct radeon_device *rdev)
        u32 src_id, src_data, ring_id;
        u32 ring_index;
        bool queue_hotplug = false;
+       bool queue_thermal = false;
 
        if (!rdev->ih.enabled || rdev->shutdown)
                return IRQ_NONE;
@@ -6000,6 +6036,16 @@ restart_ih:
                        DRM_DEBUG("IH: DMA trap\n");
                        radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX);
                        break;
+               case 230: /* thermal low to high */
+                       DRM_DEBUG("IH: thermal low to high\n");
+                       rdev->pm.dpm.thermal.high_to_low = false;
+                       queue_thermal = true;
+                       break;
+               case 231: /* thermal high to low */
+                       DRM_DEBUG("IH: thermal high to low\n");
+                       rdev->pm.dpm.thermal.high_to_low = true;
+                       queue_thermal = true;
+                       break;
                case 233: /* GUI IDLE */
                        DRM_DEBUG("IH: GUI idle\n");
                        break;
@@ -6018,6 +6064,8 @@ restart_ih:
        }
        if (queue_hotplug)
                schedule_work(&rdev->hotplug_work);
+       if (queue_thermal && rdev->pm.dpm_enabled)
+               schedule_work(&rdev->pm.dpm.thermal.work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        atomic_set(&rdev->ih.lock, 0);
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
new file mode 100644 (file)
index 0000000..32d03f1
--- /dev/null
@@ -0,0 +1,6329 @@
+/*
+ * 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 "drmP.h"
+#include "radeon.h"
+#include "sid.h"
+#include "r600_dpm.h"
+#include "si_dpm.h"
+#include "atom.h"
+#include <linux/math64.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                 0x20000
+
+#define DDR3_DRAM_ROWS              0x2000
+
+#define SCLK_MIN_DEEPSLEEP_FREQ     1350
+
+static const struct si_cac_config_reg cac_weights_tahiti[] =
+{
+       { 0x0, 0x0000ffff, 0, 0xc, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x101, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0xc, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x8fc, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x95, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x34e, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x1a1, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0xda, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x46, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x208, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0xe7, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x948, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x167, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x31, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x18e, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_tahiti[] =
+{
+       { 0x143, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+
+};
+
+static const struct si_cac_config_reg cac_override_tahiti[] =
+{
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_tahiti =
+{
+       ((1 << 16) | 27027),
+       6,
+       0,
+       4,
+       95,
+       {
+               0UL,
+               0UL,
+               4521550UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               40
+       },
+       595000000UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_tahiti =
+{
+       { 1159409, 0, 0, 0, 0 },
+       { 777, 0, 0, 0, 0 },
+       2,
+       54000,
+       127000,
+       25,
+       2,
+       10,
+       13,
+       { 27, 31, 35, 39, 43, 47, 54, 61, 67, 74, 81, 88, 95, 0, 0, 0 },
+       { 240888759, 221057860, 235370597, 162287531, 158510299, 131423027, 116673180, 103067515, 87941937, 76209048, 68209175, 64090048, 58301890, 0, 0, 0 },
+       { 12024, 11189, 11451, 8411, 7939, 6666, 5681, 4905, 4241, 3720, 3354, 3122, 2890, 0, 0, 0 },
+       85,
+       false
+};
+
+static const struct si_dte_data dte_data_tahiti_le =
+{
+       { 0x1E8480, 0x7A1200, 0x2160EC0, 0x3938700, 0 },
+       { 0x7D, 0x7D, 0x4E4, 0xB00, 0 },
+       0x5,
+       0xAFC8,
+       0x64,
+       0x32,
+       1,
+       0,
+       0x10,
+       { 0x78, 0x7C, 0x82, 0x88, 0x8E, 0x94, 0x9A, 0xA0, 0xA6, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4 },
+       { 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700 },
+       { 0x2AF8, 0x2AF8, 0x29BB, 0x27F9, 0x2637, 0x2475, 0x22B3, 0x20F1, 0x1F2F, 0x1D6D, 0x1734, 0x1414, 0x10F4, 0xDD4, 0xAB4, 0x794 },
+       85,
+       true
+};
+
+static const struct si_dte_data dte_data_tahiti_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x7D0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_new_zealand =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0 },
+       { 0x29B, 0x3E9, 0x537, 0x7D2, 0 },
+       0x5,
+       0xAFC8,
+       0x69,
+       0x32,
+       1,
+       0,
+       0x10,
+       { 0x82, 0xA0, 0xB4, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xDAC, 0x1388, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685 },
+       85,
+       true
+};
+
+static const struct si_dte_data dte_data_aruba_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_malta =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+struct si_cac_config_reg cac_weights_pitcairn[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x8a, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x24d, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x19, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0xc11, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x7f3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x403, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x367, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x4c9, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x45d, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x36d, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x534, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x5da, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x880, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0x201, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x1f, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5de, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x7b, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x13, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0xf9, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x66, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x13, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x186, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_pitcairn[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_pitcairn[] =
+{
+    { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_pitcairn =
+{
+       ((1 << 16) | 27027),
+       5,
+       0,
+       6,
+       100,
+       {
+               51600000UL,
+               1800000UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_pitcairn =
+{
+       { 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0 },
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       0,
+       false
+};
+
+static const struct si_dte_data dte_data_curacao_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_curacao_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_neptune_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x3A2F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x2BD, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x30A, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_heathrow[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x362, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x315, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_cape_verde[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_cape_verde[] =
+{
+    { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_cape_verde =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_cape_verde =
+{
+       { 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0 },
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       0,
+       false
+};
+
+static const struct si_dte_data dte_data_venus_xtx =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x71C, 0xAAB, 0xE39, 0x11C7, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0xD6D8, 0x88B8, 0x1555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_venus_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0xBDA, 0x11C7, 0x17B4, 0x1DA1, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0xAFC8, 0x88B8, 0x238E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_venus_pro =
+{
+       {  0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x11C7, 0x1AAB, 0x238E, 0x2C72, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x88B8, 0x88B8, 0x3555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+struct si_cac_config_reg cac_weights_oland[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x60, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x90, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x120, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_oland[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_mars_pro[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_oland[] =
+{
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_oland =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_powertune_data powertune_data_mars_pro =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_oland =
+{
+       { 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0 },
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       0,
+       false
+};
+
+static const struct si_dte_data dte_data_mars_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       55000,
+       105,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xF627, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_sun_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       55000,
+       105,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xD555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+
+static const struct si_cac_config_reg cac_weights_hainan[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x2d9, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x22b, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x21c, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x1dc, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x24e, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x35e, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1143, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0xe17, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x441, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x28b, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0xabe, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xf11, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x907, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0xb45, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0xd1e, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xa2c, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x62, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x1f3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x42, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x709, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x3a, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x357, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x314, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x6d, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x1b9, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_hainan =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       9,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
+struct ni_power_info *ni_get_pi(struct radeon_device *rdev);
+struct ni_ps *ni_get_ps(struct radeon_ps *rps);
+
+static int si_populate_voltage_value(struct radeon_device *rdev,
+                                    const struct atom_voltage_table *table,
+                                    u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage);
+static int si_get_std_voltage_value(struct radeon_device *rdev,
+                                   SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage);
+static int si_write_smc_soft_register(struct radeon_device *rdev,
+                                     u16 reg_offset, u32 value);
+static int si_convert_power_level_to_smc(struct radeon_device *rdev,
+                                        struct rv7xx_pl *pl,
+                                        SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level);
+static int si_calculate_sclk_params(struct radeon_device *rdev,
+                                   u32 engine_clock,
+                                   SISLANDS_SMC_SCLK_VALUE *sclk);
+
+static struct si_power_info *si_get_pi(struct radeon_device *rdev)
+{
+        struct si_power_info *pi = rdev->pm.dpm.priv;
+
+        return pi;
+}
+
+static void si_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff,
+                                                    u16 v, s32 t, u32 ileakage, u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc;
+       s64 temperature, t_slope, t_intercept, av, bv, t_ref;
+
+       i_leakage = drm_int2fixp(ileakage / 100);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+       temperature = div64_s64(drm_int2fixp(t), 1000);
+
+       t_slope = div64_s64(drm_int2fixp(coeff->t_slope), 100000000);
+       t_intercept = div64_s64(drm_int2fixp(coeff->t_intercept), 100000000);
+       av = div64_s64(drm_int2fixp(coeff->av), 100000000);
+       bv = div64_s64(drm_int2fixp(coeff->bv), 100000000);
+       t_ref = drm_int2fixp(coeff->t_ref);
+
+       kt = drm_fixp_div(drm_fixp_exp(drm_fixp_mul(drm_fixp_mul(t_slope, vddc) + t_intercept, temperature)),
+                         drm_fixp_exp(drm_fixp_mul(drm_fixp_mul(t_slope, vddc) + t_intercept, t_ref)));
+       kv = drm_fixp_mul(av, drm_fixp_exp(drm_fixp_mul(bv, vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v_and_t(struct radeon_device *rdev,
+                                            const struct ni_leakage_coeffients *coeff,
+                                            u16 v,
+                                            s32 t,
+                                            u32 i_leakage,
+                                            u32 *leakage)
+{
+       si_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
+}
+
+static void si_calculate_leakage_for_v_formula(const struct ni_leakage_coeffients *coeff,
+                                              const u32 fixed_kt, u16 v,
+                                              u32 ileakage, u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc;
+
+       i_leakage = div64_s64(drm_int2fixp(ileakage), 100);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+
+       kt = div64_s64(drm_int2fixp(fixed_kt), 100000000);
+       kv = drm_fixp_mul(div64_s64(drm_int2fixp(coeff->av), 100000000),
+                         drm_fixp_exp(drm_fixp_mul(div64_s64(drm_int2fixp(coeff->bv), 100000000), vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v(struct radeon_device *rdev,
+                                      const struct ni_leakage_coeffients *coeff,
+                                      const u32 fixed_kt,
+                                      u16 v,
+                                      u32 i_leakage,
+                                      u32 *leakage)
+{
+       si_calculate_leakage_for_v_formula(coeff, fixed_kt, v, i_leakage, leakage);
+}
+
+
+static void si_update_dte_from_pl2(struct radeon_device *rdev,
+                                  struct si_dte_data *dte_data)
+{
+       u32 p_limit1 = rdev->pm.dpm.tdp_limit;
+       u32 p_limit2 = rdev->pm.dpm.near_tdp_limit;
+       u32 k = dte_data->k;
+       u32 t_max = dte_data->max_t;
+       u32 t_split[5] = { 10, 15, 20, 25, 30 };
+       u32 t_0 = dte_data->t0;
+       u32 i;
+
+       if (p_limit2 != 0 && p_limit2 <= p_limit1) {
+               dte_data->tdep_count = 3;
+
+               for (i = 0; i < k; i++) {
+                       dte_data->r[i] =
+                               (t_split[i] * (t_max - t_0/(u32)1000) * (1 << 14)) /
+                               (p_limit2  * (u32)100);
+               }
+
+               dte_data->tdep_r[1] = dte_data->r[4] * 2;
+
+               for (i = 2; i < SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE; i++) {
+                       dte_data->tdep_r[i] = dte_data->r[4];
+               }
+       } else {
+               DRM_ERROR("Invalid PL2! DTE will not be updated.\n");
+       }
+}
+
+static void si_initialize_powertune_defaults(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       bool update_dte_from_pl2 = false;
+
+       if (rdev->family == CHIP_TAHITI) {
+               si_pi->cac_weights = cac_weights_tahiti;
+               si_pi->lcac_config = lcac_tahiti;
+               si_pi->cac_override = cac_override_tahiti;
+               si_pi->powertune_data = &powertune_data_tahiti;
+               si_pi->dte_data = dte_data_tahiti;
+
+               switch (rdev->pdev->device) {
+               case 0x6798:
+                       si_pi->dte_data.enable_dte_by_default = true;
+                       break;
+               case 0x6799:
+                       si_pi->dte_data = dte_data_new_zealand;
+                       break;
+               case 0x6790:
+               case 0x6791:
+               case 0x6792:
+               case 0x679E:
+                       si_pi->dte_data = dte_data_aruba_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x679B:
+                       si_pi->dte_data = dte_data_malta;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x679A:
+                       si_pi->dte_data = dte_data_tahiti_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       if (si_pi->dte_data.enable_dte_by_default == true)
+                               DRM_ERROR("DTE is not enabled!\n");
+                       break;
+               }
+       } else if (rdev->family == CHIP_PITCAIRN) {
+               switch (rdev->pdev->device) {
+               case 0x6810:
+               case 0x6818:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_curacao_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6819:
+               case 0x6811:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_curacao_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6800:
+               case 0x6806:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_neptune_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_pitcairn;
+                       si_pi->lcac_config = lcac_pitcairn;
+                       si_pi->cac_override = cac_override_pitcairn;
+                       si_pi->powertune_data = &powertune_data_pitcairn;
+                       si_pi->dte_data = dte_data_pitcairn;
+               }
+       } else if (rdev->family == CHIP_VERDE) {
+               si_pi->lcac_config = lcac_cape_verde;
+               si_pi->cac_override = cac_override_cape_verde;
+               si_pi->powertune_data = &powertune_data_cape_verde;
+
+               switch (rdev->pdev->device) {
+               case 0x683B:
+               case 0x683F:
+               case 0x6829:
+                       si_pi->cac_weights = cac_weights_cape_verde_pro;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6825:
+               case 0x6827:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6824:
+               case 0x682D:
+                       si_pi->cac_weights = cac_weights_chelsea_xt;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x682F:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6820:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_venus_xtx;
+                       break;
+               case 0x6821:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_venus_xt;
+                       break;
+               case 0x6823:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_venus_pro;
+                       break;
+               case 0x682B:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_venus_pro;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_cape_verde;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               }
+       } else if (rdev->family == CHIP_OLAND) {
+               switch (rdev->pdev->device) {
+               case 0x6601:
+               case 0x6621:
+               case 0x6603:
+                       si_pi->cac_weights = cac_weights_mars_pro;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6600:
+               case 0x6606:
+               case 0x6620:
+                       si_pi->cac_weights = cac_weights_mars_xt;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6611:
+                       si_pi->cac_weights = cac_weights_oland_pro;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6610:
+                       si_pi->cac_weights = cac_weights_oland_xt;
+                       si_pi->lcac_config = lcac_mars_pro;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_mars_pro;
+                       si_pi->dte_data = dte_data_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_oland;
+                       si_pi->lcac_config = lcac_oland;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_oland;
+                       si_pi->dte_data = dte_data_oland;
+                       break;
+               }
+       } else if (rdev->family == CHIP_HAINAN) {
+               si_pi->cac_weights = cac_weights_hainan;
+               si_pi->lcac_config = lcac_oland;
+               si_pi->cac_override = cac_override_oland;
+               si_pi->powertune_data = &powertune_data_hainan;
+               si_pi->dte_data = dte_data_sun_xt;
+               update_dte_from_pl2 = true;
+       } else {
+               DRM_ERROR("Unknown SI asic revision, failed to initialize PowerTune!\n");
+               return;
+       }
+
+       ni_pi->enable_power_containment = false;
+       ni_pi->enable_cac = false;
+       ni_pi->enable_sq_ramping = false;
+       si_pi->enable_dte = false;
+
+       if (si_pi->powertune_data->enable_powertune_by_default) {
+               ni_pi->enable_power_containment= true;
+               ni_pi->enable_cac = true;
+               if (si_pi->dte_data.enable_dte_by_default) {
+                       si_pi->enable_dte = true;
+                       if (update_dte_from_pl2)
+                               si_update_dte_from_pl2(rdev, &si_pi->dte_data);
+
+               }
+               ni_pi->enable_sq_ramping = true;
+       }
+
+       ni_pi->driver_calculate_cac_leakage = true;
+       ni_pi->cac_configuration_required = true;
+
+       if (ni_pi->cac_configuration_required) {
+               ni_pi->support_cac_long_term_average = true;
+               si_pi->dyn_powertune_data.l2_lta_window_size =
+                       si_pi->powertune_data->l2_lta_window_size_default;
+               si_pi->dyn_powertune_data.lts_truncate =
+                       si_pi->powertune_data->lts_truncate_default;
+       } else {
+               ni_pi->support_cac_long_term_average = false;
+               si_pi->dyn_powertune_data.l2_lta_window_size = 0;
+               si_pi->dyn_powertune_data.lts_truncate = 0;
+       }
+
+       si_pi->dyn_powertune_data.disable_uvd_powertune = false;
+}
+
+static u32 si_get_smc_power_scaling_factor(struct radeon_device *rdev)
+{
+       return 1;
+}
+
+static u32 si_calculate_cac_wintime(struct radeon_device *rdev)
+{
+       u32 xclk;
+       u32 wintime;
+       u32 cac_window;
+       u32 cac_window_size;
+
+       xclk = radeon_get_xclk(rdev);
+
+       if (xclk == 0)
+               return 0;
+
+       cac_window = RREG32(CG_CAC_CTRL) & CAC_WINDOW_MASK;
+       cac_window_size = ((cac_window & 0xFFFF0000) >> 16) * (cac_window & 0x0000FFFF);
+
+       wintime = (cac_window_size * 100) / xclk;
+
+       return wintime;
+}
+
+static u32 si_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor)
+{
+       return power_in_watts;
+}
+
+static int si_calculate_adjusted_tdp_limits(struct radeon_device *rdev,
+                                           bool adjust_polarity,
+                                           u32 tdp_adjustment,
+                                           u32 *tdp_limit,
+                                           u32 *near_tdp_limit)
+{
+       u32 adjustment_delta, max_tdp_limit;
+
+       if (tdp_adjustment > (u32)rdev->pm.dpm.tdp_od_limit)
+               return -EINVAL;
+
+       max_tdp_limit = ((100 + 100) * rdev->pm.dpm.tdp_limit) / 100;
+
+       if (adjust_polarity) {
+               *tdp_limit = ((100 + tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
+               *near_tdp_limit = rdev->pm.dpm.near_tdp_limit_adjusted + (*tdp_limit - rdev->pm.dpm.tdp_limit);
+       } else {
+               *tdp_limit = ((100 - tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
+               adjustment_delta  = rdev->pm.dpm.tdp_limit - *tdp_limit;
+               if (adjustment_delta < rdev->pm.dpm.near_tdp_limit_adjusted)
+                       *near_tdp_limit = rdev->pm.dpm.near_tdp_limit_adjusted - adjustment_delta;
+               else
+                       *near_tdp_limit = 0;
+       }
+
+       if ((*tdp_limit <= 0) || (*tdp_limit > max_tdp_limit))
+               return -EINVAL;
+       if ((*near_tdp_limit <= 0) || (*near_tdp_limit > *tdp_limit))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int si_populate_smc_tdp_limits(struct radeon_device *rdev,
+                                     struct radeon_ps *radeon_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (ni_pi->enable_power_containment) {
+               SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+               PP_SIslands_PAPMParameters *papm_parm;
+               struct radeon_ppm_table *ppm = rdev->pm.dpm.dyn_state.ppm_table;
+               u32 scaling_factor = si_get_smc_power_scaling_factor(rdev);
+               u32 tdp_limit;
+               u32 near_tdp_limit;
+               int ret;
+
+               if (scaling_factor == 0)
+                       return -EINVAL;
+
+               memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+               ret = si_calculate_adjusted_tdp_limits(rdev,
+                                                      false, /* ??? */
+                                                      rdev->pm.dpm.tdp_adjustment,
+                                                      &tdp_limit,
+                                                      &near_tdp_limit);
+               if (ret)
+                       return ret;
+
+               smc_table->dpm2Params.TDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(tdp_limit, scaling_factor) * 1000);
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(near_tdp_limit, scaling_factor) * 1000);
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(si_scale_power_for_smc((near_tdp_limit * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+               ret = si_copy_bytes_to_smc(rdev,
+                                          (si_pi->state_table_start + offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+                                                offsetof(PP_SIslands_DPM2Parameters, TDPLimit)),
+                                          (u8 *)(&(smc_table->dpm2Params.TDPLimit)),
+                                          sizeof(u32) * 3,
+                                          si_pi->sram_end);
+               if (ret)
+                       return ret;
+
+               if (si_pi->enable_ppm) {
+                       papm_parm = &si_pi->papm_parm;
+                       memset(papm_parm, 0, sizeof(PP_SIslands_PAPMParameters));
+                       papm_parm->NearTDPLimitTherm = cpu_to_be32(ppm->dgpu_tdp);
+                       papm_parm->dGPU_T_Limit = cpu_to_be32(ppm->tj_max);
+                       papm_parm->dGPU_T_Warning = cpu_to_be32(95);
+                       papm_parm->dGPU_T_Hysteresis = cpu_to_be32(5);
+                       papm_parm->PlatformPowerLimit = 0xffffffff;
+                       papm_parm->NearTDPLimitPAPM = 0xffffffff;
+
+                       ret = si_copy_bytes_to_smc(rdev, si_pi->papm_cfg_table_start,
+                                                  (u8 *)papm_parm,
+                                                  sizeof(PP_SIslands_PAPMParameters),
+                                                  si_pi->sram_end);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+static int si_populate_smc_tdp_limits_2(struct radeon_device *rdev,
+                                       struct radeon_ps *radeon_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (ni_pi->enable_power_containment) {
+               SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+               u32 scaling_factor = si_get_smc_power_scaling_factor(rdev);
+               int ret;
+
+               memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(rdev->pm.dpm.near_tdp_limit_adjusted, scaling_factor) * 1000);
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(si_scale_power_for_smc((rdev->pm.dpm.near_tdp_limit_adjusted * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+               ret = si_copy_bytes_to_smc(rdev,
+                                          (si_pi->state_table_start +
+                                           offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+                                           offsetof(PP_SIslands_DPM2Parameters, NearTDPLimit)),
+                                          (u8 *)(&(smc_table->dpm2Params.NearTDPLimit)),
+                                          sizeof(u32) * 2,
+                                          si_pi->sram_end);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static u16 si_calculate_power_efficiency_ratio(struct radeon_device *rdev,
+                                              const u16 prev_std_vddc,
+                                              const u16 curr_std_vddc)
+{
+       u64 margin = (u64)SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN;
+       u64 prev_vddc = (u64)prev_std_vddc;
+       u64 curr_vddc = (u64)curr_std_vddc;
+       u64 pwr_efficiency_ratio, n, d;
+
+       if ((prev_vddc == 0) || (curr_vddc == 0))
+               return 0;
+
+       n = div64_u64((u64)1024 * curr_vddc * curr_vddc * ((u64)1000 + margin), (u64)1000);
+       d = prev_vddc * prev_vddc;
+       pwr_efficiency_ratio = div64_u64(n, d);
+
+       if (pwr_efficiency_ratio > (u64)0xFFFF)
+               return 0;
+
+       return (u16)pwr_efficiency_ratio;
+}
+
+static bool si_should_disable_uvd_powertune(struct radeon_device *rdev,
+                                           struct radeon_ps *radeon_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (si_pi->dyn_powertune_data.disable_uvd_powertune &&
+           radeon_state->vclk && radeon_state->dclk)
+               return true;
+
+       return false;
+}
+
+static int si_populate_power_containment_values(struct radeon_device *rdev,
+                                               struct radeon_ps *radeon_state,
+                                               SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       SISLANDS_SMC_VOLTAGE_VALUE vddc;
+       u32 prev_sclk;
+       u32 max_sclk;
+       u32 min_sclk;
+       u16 prev_std_vddc;
+       u16 curr_std_vddc;
+       int i;
+       u16 pwr_efficiency_ratio;
+       u8 max_ps_percent;
+       bool disable_uvd_power_tune;
+       int ret;
+
+       if (ni_pi->enable_power_containment == false)
+               return 0;
+
+       if (state->performance_level_count == 0)
+               return -EINVAL;
+
+       if (smc_state->levelCount != state->performance_level_count)
+               return -EINVAL;
+
+       disable_uvd_power_tune = si_should_disable_uvd_powertune(rdev, radeon_state);
+
+       smc_state->levels[0].dpm2.MaxPS = 0;
+       smc_state->levels[0].dpm2.NearTDPDec = 0;
+       smc_state->levels[0].dpm2.AboveSafeInc = 0;
+       smc_state->levels[0].dpm2.BelowSafeInc = 0;
+       smc_state->levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+       for (i = 1; i < state->performance_level_count; i++) {
+               prev_sclk = state->performance_levels[i-1].sclk;
+               max_sclk  = state->performance_levels[i].sclk;
+               if (i == 1)
+                       max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_M;
+               else
+                       max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_H;
+
+               if (prev_sclk > max_sclk)
+                       return -EINVAL;
+
+               if ((max_ps_percent == 0) ||
+                   (prev_sclk == max_sclk) ||
+                   disable_uvd_power_tune) {
+                       min_sclk = max_sclk;
+               } else if (i == 1) {
+                       min_sclk = prev_sclk;
+               } else {
+                       min_sclk = (prev_sclk * (u32)max_ps_percent) / 100;
+               }
+
+               if (min_sclk < state->performance_levels[0].sclk)
+                       min_sclk = state->performance_levels[0].sclk;
+
+               if (min_sclk == 0)
+                       return -EINVAL;
+
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[i-1].vddc, &vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_get_std_voltage_value(rdev, &vddc, &prev_std_vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[i].vddc, &vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_get_std_voltage_value(rdev, &vddc, &curr_std_vddc);
+               if (ret)
+                       return ret;
+
+               pwr_efficiency_ratio = si_calculate_power_efficiency_ratio(rdev,
+                                                                          prev_std_vddc, curr_std_vddc);
+
+               smc_state->levels[i].dpm2.MaxPS = (u8)((SISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk);
+               smc_state->levels[i].dpm2.NearTDPDec = SISLANDS_DPM2_NEAR_TDP_DEC;
+               smc_state->levels[i].dpm2.AboveSafeInc = SISLANDS_DPM2_ABOVE_SAFE_INC;
+               smc_state->levels[i].dpm2.BelowSafeInc = SISLANDS_DPM2_BELOW_SAFE_INC;
+               smc_state->levels[i].dpm2.PwrEfficiencyRatio = cpu_to_be16(pwr_efficiency_ratio);
+       }
+
+       return 0;
+}
+
+static int si_populate_sq_ramping_values(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_state,
+                                        SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 sq_power_throttle, sq_power_throttle2;
+       bool enable_sq_ramping = ni_pi->enable_sq_ramping;
+       int i;
+
+       if (state->performance_level_count == 0)
+               return -EINVAL;
+
+       if (smc_state->levelCount != state->performance_level_count)
+               return -EINVAL;
+
+       if (rdev->pm.dpm.sq_ramping_threshold == 0)
+               return -EINVAL;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT))
+               enable_sq_ramping = false;
+
+       if (NISLANDS_DPM2_SQ_RAMP_LTI_RATIO <= (LTI_RATIO_MASK >> LTI_RATIO_SHIFT))
+               enable_sq_ramping = false;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               sq_power_throttle = 0;
+               sq_power_throttle2 = 0;
+
+               if ((state->performance_levels[i].sclk >= rdev->pm.dpm.sq_ramping_threshold) &&
+                   enable_sq_ramping) {
+                       sq_power_throttle |= MAX_POWER(SISLANDS_DPM2_SQ_RAMP_MAX_POWER);
+                       sq_power_throttle |= MIN_POWER(SISLANDS_DPM2_SQ_RAMP_MIN_POWER);
+                       sq_power_throttle2 |= MAX_POWER_DELTA(SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA);
+                       sq_power_throttle2 |= STI_SIZE(SISLANDS_DPM2_SQ_RAMP_STI_SIZE);
+                       sq_power_throttle2 |= LTI_RATIO(SISLANDS_DPM2_SQ_RAMP_LTI_RATIO);
+               } else {
+                       sq_power_throttle |= MAX_POWER_MASK | MIN_POWER_MASK;
+                       sq_power_throttle2 |= MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+               }
+
+               smc_state->levels[i].SQPowerThrottle = cpu_to_be32(sq_power_throttle);
+               smc_state->levels[i].SQPowerThrottle_2 = cpu_to_be32(sq_power_throttle2);
+       }
+
+       return 0;
+}
+
+static int si_enable_power_containment(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_new_state,
+                                      bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_power_containment) {
+               if (enable) {
+                       if (!si_should_disable_uvd_powertune(rdev, radeon_new_state)) {
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_TDPClampingActive);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->pc_enabled = false;
+                               } else {
+                                       ni_pi->pc_enabled = true;
+                               }
+                       }
+               } else {
+                       smc_result = si_send_msg_to_smc(rdev, PPSMC_TDPClampingInactive);
+                       if (smc_result != PPSMC_Result_OK)
+                               ret = -EINVAL;
+                       ni_pi->pc_enabled = false;
+               }
+       }
+
+       return ret;
+}
+
+static int si_initialize_smc_dte_tables(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret = 0;
+       struct si_dte_data *dte_data = &si_pi->dte_data;
+       Smc_SIslands_DTE_Configuration *dte_tables = NULL;
+       u32 table_size;
+       u8 tdep_count;
+       u32 i;
+
+       if (dte_data == NULL)
+               si_pi->enable_dte = false;
+
+       if (si_pi->enable_dte == false)
+               return 0;
+
+       if (dte_data->k <= 0)
+               return -EINVAL;
+
+       dte_tables = kzalloc(sizeof(Smc_SIslands_DTE_Configuration), GFP_KERNEL);
+       if (dte_tables == NULL) {
+               si_pi->enable_dte = false;
+               return -ENOMEM;
+       }
+
+       table_size = dte_data->k;
+
+       if (table_size > SMC_SISLANDS_DTE_MAX_FILTER_STAGES)
+               table_size = SMC_SISLANDS_DTE_MAX_FILTER_STAGES;
+
+       tdep_count = dte_data->tdep_count;
+       if (tdep_count > SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE)
+               tdep_count = SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE;
+
+       dte_tables->K = cpu_to_be32(table_size);
+       dte_tables->T0 = cpu_to_be32(dte_data->t0);
+       dte_tables->MaxT = cpu_to_be32(dte_data->max_t);
+       dte_tables->WindowSize = dte_data->window_size;
+       dte_tables->temp_select = dte_data->temp_select;
+       dte_tables->DTE_mode = dte_data->dte_mode;
+       dte_tables->Tthreshold = cpu_to_be32(dte_data->t_threshold);
+
+       if (tdep_count > 0)
+               table_size--;
+
+       for (i = 0; i < table_size; i++) {
+               dte_tables->tau[i] = cpu_to_be32(dte_data->tau[i]);
+               dte_tables->R[i]   = cpu_to_be32(dte_data->r[i]);
+       }
+
+       dte_tables->Tdep_count = tdep_count;
+
+       for (i = 0; i < (u32)tdep_count; i++) {
+               dte_tables->T_limits[i] = dte_data->t_limits[i];
+               dte_tables->Tdep_tau[i] = cpu_to_be32(dte_data->tdep_tau[i]);
+               dte_tables->Tdep_R[i] = cpu_to_be32(dte_data->tdep_r[i]);
+       }
+
+       ret = si_copy_bytes_to_smc(rdev, si_pi->dte_table_start, (u8 *)dte_tables,
+                                  sizeof(Smc_SIslands_DTE_Configuration), si_pi->sram_end);
+       kfree(dte_tables);
+
+       return ret;
+}
+
+static int si_get_cac_std_voltage_max_min(struct radeon_device *rdev,
+                                         u16 *max, u16 *min)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct radeon_cac_leakage_table *table =
+               &rdev->pm.dpm.dyn_state.cac_leakage_table;
+       u32 i;
+       u32 v0_loadline;
+
+
+       if (table == NULL)
+               return -EINVAL;
+
+       *max = 0;
+       *min = 0xFFFF;
+
+       for (i = 0; i < table->count; i++) {
+               if (table->entries[i].vddc > *max)
+                       *max = table->entries[i].vddc;
+               if (table->entries[i].vddc < *min)
+                       *min = table->entries[i].vddc;
+       }
+
+       if (si_pi->powertune_data->lkge_lut_v0_percent > 100)
+               return -EINVAL;
+
+       v0_loadline = (*min) * (100 - si_pi->powertune_data->lkge_lut_v0_percent) / 100;
+
+       if (v0_loadline > 0xFFFFUL)
+               return -EINVAL;
+
+       *min = (u16)v0_loadline;
+
+       if ((*min > *max) || (*max == 0) || (*min == 0))
+               return -EINVAL;
+
+       return 0;
+}
+
+static u16 si_get_cac_std_voltage_step(u16 max, u16 min)
+{
+       return ((max - min) + (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1)) /
+               SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
+}
+
+static int si_init_dte_leakage_table(struct radeon_device *rdev,
+                                    PP_SIslands_CacConfig *cac_tables,
+                                    u16 vddc_max, u16 vddc_min, u16 vddc_step,
+                                    u16 t0, u16 t_step)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 leakage;
+       unsigned int i, j;
+       s32 t;
+       u32 smc_leakage;
+       u32 scaling_factor;
+       u16 voltage;
+
+       scaling_factor = si_get_smc_power_scaling_factor(rdev);
+
+       for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++) {
+               t = (1000 * (i * t_step + t0));
+
+               for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+                       voltage = vddc_max - (vddc_step * j);
+
+                       si_calculate_leakage_for_v_and_t(rdev,
+                                                        &si_pi->powertune_data->leakage_coefficients,
+                                                        voltage,
+                                                        t,
+                                                        si_pi->dyn_powertune_data.cac_leakage,
+                                                        &leakage);
+
+                       smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+                       if (smc_leakage > 0xFFFF)
+                               smc_leakage = 0xFFFF;
+
+                       cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+                               cpu_to_be16((u16)smc_leakage);
+               }
+       }
+       return 0;
+}
+
+static int si_init_simplified_leakage_table(struct radeon_device *rdev,
+                                           PP_SIslands_CacConfig *cac_tables,
+                                           u16 vddc_max, u16 vddc_min, u16 vddc_step)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 leakage;
+       unsigned int i, j;
+       u32 smc_leakage;
+       u32 scaling_factor;
+       u16 voltage;
+
+       scaling_factor = si_get_smc_power_scaling_factor(rdev);
+
+       for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+               voltage = vddc_max - (vddc_step * j);
+
+               si_calculate_leakage_for_v(rdev,
+                                          &si_pi->powertune_data->leakage_coefficients,
+                                          si_pi->powertune_data->fixed_kt,
+                                          voltage,
+                                          si_pi->dyn_powertune_data.cac_leakage,
+                                          &leakage);
+
+               smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+               if (smc_leakage > 0xFFFF)
+                       smc_leakage = 0xFFFF;
+
+               for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++)
+                       cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+                               cpu_to_be16((u16)smc_leakage);
+       }
+       return 0;
+}
+
+static int si_initialize_smc_cac_tables(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       PP_SIslands_CacConfig *cac_tables = NULL;
+       u16 vddc_max, vddc_min, vddc_step;
+       u16 t0, t_step;
+       u32 load_line_slope, reg;
+       int ret = 0;
+       u32 ticks_per_us = radeon_get_xclk(rdev) / 100;
+
+       if (ni_pi->enable_cac == false)
+               return 0;
+
+       cac_tables = kzalloc(sizeof(PP_SIslands_CacConfig), GFP_KERNEL);
+       if (!cac_tables)
+               return -ENOMEM;
+
+       reg = RREG32(CG_CAC_CTRL) & ~CAC_WINDOW_MASK;
+       reg |= CAC_WINDOW(si_pi->powertune_data->cac_window);
+       WREG32(CG_CAC_CTRL, reg);
+
+       si_pi->dyn_powertune_data.cac_leakage = rdev->pm.dpm.cac_leakage;
+       si_pi->dyn_powertune_data.dc_pwr_value =
+               si_pi->powertune_data->dc_cac[NISLANDS_DCCAC_LEVEL_0];
+       si_pi->dyn_powertune_data.wintime = si_calculate_cac_wintime(rdev);
+       si_pi->dyn_powertune_data.shift_n = si_pi->powertune_data->shift_n_default;
+
+       si_pi->dyn_powertune_data.leakage_minimum_temperature = 80 * 1000;
+
+       ret = si_get_cac_std_voltage_max_min(rdev, &vddc_max, &vddc_min);
+       if (ret)
+               goto done_free;
+
+       vddc_step = si_get_cac_std_voltage_step(vddc_max, vddc_min);
+       vddc_min = vddc_max - (vddc_step * (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1));
+       t_step = 4;
+       t0 = 60;
+
+       if (si_pi->enable_dte || ni_pi->driver_calculate_cac_leakage)
+               ret = si_init_dte_leakage_table(rdev, cac_tables,
+                                               vddc_max, vddc_min, vddc_step,
+                                               t0, t_step);
+       else
+               ret = si_init_simplified_leakage_table(rdev, cac_tables,
+                                                      vddc_max, vddc_min, vddc_step);
+       if (ret)
+               goto done_free;
+
+       load_line_slope = ((u32)rdev->pm.dpm.load_line_slope << SMC_SISLANDS_SCALE_R) / 100;
+
+       cac_tables->l2numWin_TDP = cpu_to_be32(si_pi->dyn_powertune_data.l2_lta_window_size);
+       cac_tables->lts_truncate_n = si_pi->dyn_powertune_data.lts_truncate;
+       cac_tables->SHIFT_N = si_pi->dyn_powertune_data.shift_n;
+       cac_tables->lkge_lut_V0 = cpu_to_be32((u32)vddc_min);
+       cac_tables->lkge_lut_Vstep = cpu_to_be32((u32)vddc_step);
+       cac_tables->R_LL = cpu_to_be32(load_line_slope);
+       cac_tables->WinTime = cpu_to_be32(si_pi->dyn_powertune_data.wintime);
+       cac_tables->calculation_repeats = cpu_to_be32(2);
+       cac_tables->dc_cac = cpu_to_be32(0);
+       cac_tables->log2_PG_LKG_SCALE = 12;
+       cac_tables->cac_temp = si_pi->powertune_data->operating_temp;
+       cac_tables->lkge_lut_T0 = cpu_to_be32((u32)t0);
+       cac_tables->lkge_lut_Tstep = cpu_to_be32((u32)t_step);
+
+       ret = si_copy_bytes_to_smc(rdev, si_pi->cac_table_start, (u8 *)cac_tables,
+                                  sizeof(PP_SIslands_CacConfig), si_pi->sram_end);
+
+       if (ret)
+               goto done_free;
+
+       ret = si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_ticks_per_us, ticks_per_us);
+
+done_free:
+       if (ret) {
+               ni_pi->enable_cac = false;
+               ni_pi->enable_power_containment = false;
+       }
+
+       kfree(cac_tables);
+
+       return 0;
+}
+
+static int si_program_cac_config_registers(struct radeon_device *rdev,
+                                          const struct si_cac_config_reg *cac_config_regs)
+{
+       const struct si_cac_config_reg *config_regs = cac_config_regs;
+       u32 data = 0, offset;
+
+       if (!config_regs)
+               return -EINVAL;
+
+       while (config_regs->offset != 0xFFFFFFFF) {
+               switch (config_regs->type) {
+               case SISLANDS_CACCONFIG_CGIND:
+                       offset = SMC_CG_IND_START + config_regs->offset;
+                       if (offset < SMC_CG_IND_END)
+                               data = RREG32_SMC(offset);
+                       break;
+               default:
+                       data = RREG32(config_regs->offset << 2);
+                       break;
+               }
+
+               data &= ~config_regs->mask;
+               data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+
+               switch (config_regs->type) {
+               case SISLANDS_CACCONFIG_CGIND:
+                       offset = SMC_CG_IND_START + config_regs->offset;
+                       if (offset < SMC_CG_IND_END)
+                               WREG32_SMC(offset, data);
+                       break;
+               default:
+                       WREG32(config_regs->offset << 2, data);
+                       break;
+               }
+               config_regs++;
+       }
+       return 0;
+}
+
+static int si_initialize_hardware_cac_manager(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+
+       if ((ni_pi->enable_cac == false) ||
+           (ni_pi->cac_configuration_required == false))
+               return 0;
+
+       ret = si_program_cac_config_registers(rdev, si_pi->lcac_config);
+       if (ret)
+               return ret;
+       ret = si_program_cac_config_registers(rdev, si_pi->cac_override);
+       if (ret)
+               return ret;
+       ret = si_program_cac_config_registers(rdev, si_pi->cac_weights);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int si_enable_smc_cac(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_new_state,
+                            bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_cac) {
+               if (enable) {
+                       if (!si_should_disable_uvd_powertune(rdev, radeon_new_state)) {
+                               if (ni_pi->support_cac_long_term_average) {
+                                       smc_result = si_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgEnable);
+                                       if (smc_result != PPSMC_Result_OK)
+                                               ni_pi->support_cac_long_term_average = false;
+                               }
+
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->cac_enabled = false;
+                               } else {
+                                       ni_pi->cac_enabled = true;
+                               }
+
+                               if (si_pi->enable_dte) {
+                                       smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableDTE);
+                                       if (smc_result != PPSMC_Result_OK)
+                                               ret = -EINVAL;
+                               }
+                       }
+               } else if (ni_pi->cac_enabled) {
+                       if (si_pi->enable_dte)
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_DisableDTE);
+
+                       smc_result = si_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac);
+
+                       ni_pi->cac_enabled = false;
+
+                       if (ni_pi->support_cac_long_term_average)
+                               smc_result = si_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgDisable);
+               }
+       }
+       return ret;
+}
+
+static int si_init_smc_spll_table(struct radeon_device *rdev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       SMC_SISLANDS_SPLL_DIV_TABLE *spll_table;
+       SISLANDS_SMC_SCLK_VALUE sclk_params;
+       u32 fb_div, p_div;
+       u32 clk_s, clk_v;
+       u32 sclk = 0;
+       int ret = 0;
+       u32 tmp;
+       int i;
+
+       if (si_pi->spll_table_start == 0)
+               return -EINVAL;
+
+       spll_table = kzalloc(sizeof(SMC_SISLANDS_SPLL_DIV_TABLE), GFP_KERNEL);
+       if (spll_table == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < 256; i++) {
+               ret = si_calculate_sclk_params(rdev, sclk, &sclk_params);
+               if (ret)
+                       break;
+
+               p_div = (sclk_params.vCG_SPLL_FUNC_CNTL & SPLL_PDIV_A_MASK) >> SPLL_PDIV_A_SHIFT;
+               fb_div = (sclk_params.vCG_SPLL_FUNC_CNTL_3 & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
+               clk_s = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM & CLK_S_MASK) >> CLK_S_SHIFT;
+               clk_v = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM_2 & CLK_V_MASK) >> CLK_V_SHIFT;
+
+               fb_div &= ~0x00001FFF;
+               fb_div >>= 1;
+               clk_v >>= 6;
+
+               if (p_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT))
+                       ret = -EINVAL;
+               if (fb_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT))
+                       ret = -EINVAL;
+               if (clk_s & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
+                       ret = -EINVAL;
+               if (clk_v & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT))
+                       ret = -EINVAL;
+
+               if (ret)
+                       break;
+
+               tmp = ((fb_div << SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) |
+                       ((p_div << SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK);
+               spll_table->freq[i] = cpu_to_be32(tmp);
+
+               tmp = ((clk_v << SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK) |
+                       ((clk_s << SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK);
+               spll_table->ss[i] = cpu_to_be32(tmp);
+
+               sclk += 512;
+       }
+
+
+       if (!ret)
+               ret = si_copy_bytes_to_smc(rdev, si_pi->spll_table_start,
+                                          (u8 *)spll_table, sizeof(SMC_SISLANDS_SPLL_DIV_TABLE),
+                                          si_pi->sram_end);
+
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       kfree(spll_table);
+
+       return ret;
+}
+
+static void si_apply_state_adjust_rules(struct radeon_device *rdev,
+                                       struct radeon_ps *rps)
+{
+       struct ni_ps *ps = ni_get_ps(rps);
+       struct radeon_clock_and_voltage_limits *max_limits;
+       bool disable_mclk_switching;
+       u32 mclk, sclk;
+       u16 vddc, vddci;
+       int i;
+
+       if (rdev->pm.dpm.new_active_crtc_count > 1)
+               disable_mclk_switching = true;
+       else
+               disable_mclk_switching = 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;
+
+       for (i = ps->performance_level_count - 2; i >= 0; i--) {
+               if (ps->performance_levels[i].vddc > ps->performance_levels[i+1].vddc)
+                       ps->performance_levels[i].vddc = ps->performance_levels[i+1].vddc;
+       }
+       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;
+                       if (ps->performance_levels[i].vddc > max_limits->vddc)
+                               ps->performance_levels[i].vddc = max_limits->vddc;
+                       if (ps->performance_levels[i].vddci > max_limits->vddci)
+                               ps->performance_levels[i].vddci = max_limits->vddci;
+               }
+       }
+
+       /* 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;
+               vddc = ps->performance_levels[0].vddc;
+               vddci = ps->performance_levels[ps->performance_level_count - 1].vddci;
+       } else {
+               sclk = ps->performance_levels[0].sclk;
+               mclk = ps->performance_levels[0].mclk;
+               vddc = ps->performance_levels[0].vddc;
+               vddci = ps->performance_levels[0].vddci;
+       }
+
+       /* adjusted low state */
+       ps->performance_levels[0].sclk = sclk;
+       ps->performance_levels[0].mclk = mclk;
+       ps->performance_levels[0].vddc = vddc;
+       ps->performance_levels[0].vddci = vddci;
+
+       for (i = 1; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].sclk < ps->performance_levels[i - 1].sclk)
+                       ps->performance_levels[i].sclk = ps->performance_levels[i - 1].sclk;
+               if (ps->performance_levels[i].vddc < ps->performance_levels[i - 1].vddc)
+                       ps->performance_levels[i].vddc = ps->performance_levels[i - 1].vddc;
+       }
+
+       if (disable_mclk_switching) {
+               mclk = ps->performance_levels[0].mclk;
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (mclk < ps->performance_levels[i].mclk)
+                               mclk = ps->performance_levels[i].mclk;
+               }
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       ps->performance_levels[i].mclk = mclk;
+                       ps->performance_levels[i].vddci = vddci;
+               }
+       } else {
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (ps->performance_levels[i].mclk < ps->performance_levels[i - 1].mclk)
+                               ps->performance_levels[i].mclk = ps->performance_levels[i - 1].mclk;
+                       if (ps->performance_levels[i].vddci < ps->performance_levels[i - 1].vddci)
+                               ps->performance_levels[i].vddci = ps->performance_levels[i - 1].vddci;
+               }
+       }
+
+        for (i = 0; i < ps->performance_level_count; i++)
+                btc_adjust_clock_combinations(rdev, max_limits,
+                                              &ps->performance_levels[i]);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                  ps->performance_levels[i].sclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddci, &ps->performance_levels[i].vddci);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                                  rdev->clock.current_dispclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+       }
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_delta_rules(rdev,
+                                             max_limits->vddc, max_limits->vddci,
+                                             &ps->performance_levels[i].vddc,
+                                             &ps->performance_levels[i].vddci);
+       }
+
+       ps->dc_compatible = true;
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].vddc > rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)
+                       ps->dc_compatible = false;
+       }
+
+}
+
+#if 0
+static int si_read_smc_soft_register(struct radeon_device *rdev,
+                                    u16 reg_offset, u32 *value)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       return si_read_smc_sram_dword(rdev,
+                                     si_pi->soft_regs_start + reg_offset, value,
+                                     si_pi->sram_end);
+}
+#endif
+
+static int si_write_smc_soft_register(struct radeon_device *rdev,
+                                     u16 reg_offset, u32 value)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       return si_write_smc_sram_dword(rdev,
+                                      si_pi->soft_regs_start + reg_offset,
+                                      value, si_pi->sram_end);
+}
+
+static bool si_is_special_1gb_platform(struct radeon_device *rdev)
+{
+       bool ret = false;
+       u32 tmp, width, row, column, bank, density;
+       bool is_memory_gddr5, is_special;
+
+       tmp = RREG32(MC_SEQ_MISC0);
+       is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE == ((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT));
+       is_special = (MC_SEQ_MISC0_REV_ID_VALUE == ((tmp & MC_SEQ_MISC0_REV_ID_MASK) >> MC_SEQ_MISC0_REV_ID_SHIFT))
+               & (MC_SEQ_MISC0_VEN_ID_VALUE == ((tmp & MC_SEQ_MISC0_VEN_ID_MASK) >> MC_SEQ_MISC0_VEN_ID_SHIFT));
+
+       WREG32(MC_SEQ_IO_DEBUG_INDEX, 0xb);
+       width = ((RREG32(MC_SEQ_IO_DEBUG_DATA) >> 1) & 1) ? 16 : 32;
+
+       tmp = RREG32(MC_ARB_RAMCFG);
+       row = ((tmp & NOOFROWS_MASK) >> NOOFROWS_SHIFT) + 10;
+       column = ((tmp & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT) + 8;
+       bank = ((tmp & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + 2;
+
+       density = (1 << (row + column - 20 + bank)) * width;
+
+       if ((rdev->pdev->device == 0x6819) &&
+           is_memory_gddr5 && is_special && (density == 0x400))
+               ret = true;
+
+       return ret;
+}
+
+static void si_get_leakage_vddc(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u16 vddc, count = 0;
+       int i, ret;
+
+       for (i = 0; i < SISLANDS_MAX_LEAKAGE_COUNT; i++) {
+               ret = radeon_atom_get_leakage_vddc_based_on_leakage_idx(rdev, &vddc, SISLANDS_LEAKAGE_INDEX0 + i);
+
+               if (!ret && (vddc > 0) && (vddc != (SISLANDS_LEAKAGE_INDEX0 + i))) {
+                       si_pi->leakage_voltage.entries[count].voltage = vddc;
+                       si_pi->leakage_voltage.entries[count].leakage_index =
+                               SISLANDS_LEAKAGE_INDEX0 + i;
+                       count++;
+               }
+       }
+       si_pi->leakage_voltage.count = count;
+}
+
+static int si_get_leakage_voltage_from_leakage_index(struct radeon_device *rdev,
+                                                    u32 index, u16 *leakage_voltage)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int i;
+
+       if (leakage_voltage == NULL)
+               return -EINVAL;
+
+       if ((index & 0xff00) != 0xff00)
+               return -EINVAL;
+
+       if ((index & 0xff) > SISLANDS_MAX_LEAKAGE_COUNT + 1)
+               return -EINVAL;
+
+       if (index < SISLANDS_LEAKAGE_INDEX0)
+               return -EINVAL;
+
+       for (i = 0; i < si_pi->leakage_voltage.count; i++) {
+               if (si_pi->leakage_voltage.entries[i].leakage_index == index) {
+                       *leakage_voltage = si_pi->leakage_voltage.entries[i].voltage;
+                       return 0;
+               }
+       }
+       return -EAGAIN;
+}
+
+static void si_set_dpm_event_sources(struct radeon_device *rdev, u32 sources)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       bool want_thermal_protection;
+       enum radeon_dpm_event_src dpm_event_src;
+
+       switch (sources) {
+       case 0:
+       default:
+               want_thermal_protection = false;
+                break;
+       case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL;
+               break;
+       case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL;
+               break;
+       case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+             (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
+               break;
+       }
+
+       if (want_thermal_protection) {
+               WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK);
+               if (pi->thermal_protection)
+                       WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       } else {
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+       }
+}
+
+static void si_enable_auto_throttle_source(struct radeon_device *rdev,
+                                          enum radeon_dpm_auto_throttle_src source,
+                                          bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (!(pi->active_auto_throttle_sources & (1 << source))) {
+                       pi->active_auto_throttle_sources |= 1 << source;
+                       si_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);
+                       si_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       }
+}
+
+static void si_start_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_stop_dpm(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_enable_sclk_control(struct radeon_device *rdev, bool enable)
+{
+       if (enable)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF);
+
+}
+
+#if 0
+static int si_notify_hardware_of_thermal_state(struct radeon_device *rdev,
+                                              u32 thermal_level)
+{
+       PPSMC_Result ret;
+
+       if (thermal_level == 0) {
+               ret = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+               if (ret == PPSMC_Result_OK)
+                       return 0;
+               else
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static void si_notify_hardware_vpu_recovery_event(struct radeon_device *rdev)
+{
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen, true);
+}
+#endif
+
+#if 0
+static int si_notify_hw_of_powersource(struct radeon_device *rdev, bool ac_power)
+{
+       if (ac_power)
+               return (si_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+#endif
+
+static PPSMC_Result si_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
+                                                     PPSMC_Msg msg, u32 parameter)
+{
+       WREG32(SMC_SCRATCH0, parameter);
+       return si_send_msg_to_smc(rdev, msg);
+}
+
+static int si_restrict_performance_levels_before_switch(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+#if 0
+static int si_unrestrict_performance_levels_after_switch(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+#endif
+
+static int si_set_boot_state(struct radeon_device *rdev)
+{
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToInitialState) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_set_sw_state(struct radeon_device *rdev)
+{
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToSwState) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_halt_smc(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc(rdev, PPSMC_MSG_Halt) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_wait_for_smc_inactive(rdev) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_resume_smc(struct radeon_device *rdev)
+{
+       if (si_send_msg_to_smc(rdev, PPSMC_FlushDataCache) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_Resume) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static void si_dpm_start_smc(struct radeon_device *rdev)
+{
+       si_program_jump_on_start(rdev);
+       si_start_smc(rdev);
+       si_start_smc_clock(rdev);
+}
+
+static void si_dpm_stop_smc(struct radeon_device *rdev)
+{
+       si_reset_smc(rdev);
+       si_stop_smc_clock(rdev);
+}
+
+static int si_process_firmware_header(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_stateTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+        si_pi->state_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_softRegisters,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->soft_regs_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->mc_reg_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->arb_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->cac_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->dte_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_spllTable,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->spll_table_start = tmp;
+
+       ret = si_read_smc_sram_dword(rdev,
+                                    SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                    SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->papm_cfg_table_start = tmp;
+
+       return ret;
+}
+
+static void si_read_clock_registers(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       si_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL);
+       si_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2);
+       si_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3);
+       si_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4);
+       si_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       si_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+       si_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
+       si_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
+       si_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
+       si_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
+       si_pi->clock_registers.mpll_func_cntl = RREG32(MPLL_FUNC_CNTL);
+       si_pi->clock_registers.mpll_func_cntl_1 = RREG32(MPLL_FUNC_CNTL_1);
+       si_pi->clock_registers.mpll_func_cntl_2 = RREG32(MPLL_FUNC_CNTL_2);
+       si_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
+       si_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+static void si_enable_thermal_protection(struct radeon_device *rdev,
+                                         bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       else
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+}
+
+static void si_enable_acpi_power_management(struct radeon_device *rdev)
+{
+       WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+#if 0
+static int si_enter_ulp_state(struct radeon_device *rdev)
+{
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_SwitchToMinimumPower);
+
+       udelay(25000);
+
+       return 0;
+}
+
+static int si_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 si_notify_smc_display_change(struct radeon_device *rdev,
+                                    bool has_display)
+{
+       PPSMC_Msg msg = has_display ?
+               PPSMC_MSG_HasDisplay : PPSMC_MSG_NoDisplay;
+
+       return (si_send_msg_to_smc(rdev, msg) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static void si_program_response_times(struct radeon_device *rdev)
+{
+       u32 voltage_response_time, backbias_response_time, acpi_delay_time, vbi_time_out;
+       u32 vddc_dly, acpi_dly, vbi_dly;
+       u32 reference_clock;
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
+
+       voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time;
+        backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time;
+
+       if (voltage_response_time == 0)
+               voltage_response_time = 1000;
+
+       acpi_delay_time = 15000;
+       vbi_time_out = 100000;
+
+       reference_clock = radeon_get_xclk(rdev);
+
+       vddc_dly = (voltage_response_time  * reference_clock) / 100;
+       acpi_dly = (acpi_delay_time * reference_clock) / 100;
+       vbi_dly  = (vbi_time_out * reference_clock) / 100;
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_delay_vreg,  vddc_dly);
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_delay_acpi,  acpi_dly);
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
+}
+
+static void si_program_ds_registers(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       u32 tmp = 1; /* XXX: 0x10 on tahiti A0 */
+
+       if (eg_pi->sclk_deep_sleep) {
+               WREG32_P(MISC_CLK_CNTL, DEEP_SLEEP_CLK_SEL(tmp), ~DEEP_SLEEP_CLK_SEL_MASK);
+               WREG32_P(CG_SPLL_AUTOSCALE_CNTL, AUTOSCALE_ON_SS_CLEAR,
+                        ~AUTOSCALE_ON_SS_CLEAR);
+       }
+}
+
+static void si_program_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp, pipe;
+       int i;
+
+       tmp = RREG32(CG_DISPLAY_GAP_CNTL) & ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+       if (rdev->pm.dpm.new_active_crtc_count > 0)
+               tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+       if (rdev->pm.dpm.new_active_crtc_count > 1)
+               tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+
+       tmp = RREG32(DCCG_DISP_SLOW_SELECT_REG);
+       pipe = (tmp & DCCG_DISP1_SLOW_SELECT_MASK) >> DCCG_DISP1_SLOW_SELECT_SHIFT;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 0) &&
+           (!(rdev->pm.dpm.new_active_crtcs & (1 << pipe)))) {
+               /* find the first active crtc */
+               for (i = 0; i < rdev->num_crtc; i++) {
+                       if (rdev->pm.dpm.new_active_crtcs & (1 << i))
+                               break;
+               }
+               if (i == rdev->num_crtc)
+                       pipe = 0;
+               else
+                       pipe = i;
+
+               tmp &= ~DCCG_DISP1_SLOW_SELECT_MASK;
+               tmp |= DCCG_DISP1_SLOW_SELECT(pipe);
+               WREG32(DCCG_DISP_SLOW_SELECT_REG, tmp);
+       }
+
+       si_notify_smc_display_change(rdev, rdev->pm.dpm.new_active_crtc_count > 0);
+}
+
+static void si_enable_spread_spectrum(struct radeon_device *rdev, bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       if (enable) {
+               if (pi->sclk_ss)
+                       WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+       } else {
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+               WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+       }
+}
+
+static void si_setup_bsp(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 xclk = radeon_get_xclk(rdev);
+
+       r600_calculate_u_and_p(pi->asi,
+                              xclk,
+                              16,
+                              &pi->bsp,
+                              &pi->bsu);
+
+       r600_calculate_u_and_p(pi->pasi,
+                              xclk,
+                              16,
+                              &pi->pbsp,
+                              &pi->pbsu);
+
+
+        pi->dsp = BSP(pi->bsp) | BSU(pi->bsu);
+       pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu);
+
+       WREG32(CG_BSP, pi->dsp);
+}
+
+static void si_program_git(struct radeon_device *rdev)
+{
+       WREG32_P(CG_GIT, CG_GICST(R600_GICST_DFLT), ~CG_GICST_MASK);
+}
+
+static void si_program_tp(struct radeon_device *rdev)
+{
+       int i;
+       enum r600_td td = R600_TD_DFLT;
+
+       for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+               WREG32(CG_FFCT_0 + (i * 4), (UTC_0(r600_utc[i]) | DTC_0(r600_dtc[i])));
+
+       if (td == R600_TD_AUTO)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
+       else
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
+
+       if (td == R600_TD_UP)
+               WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
+
+       if (td == R600_TD_DOWN)
+               WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+
+static void si_program_tpp(struct radeon_device *rdev)
+{
+       WREG32(CG_TPC, R600_TPC_DFLT);
+}
+
+static void si_program_sstp(struct radeon_device *rdev)
+{
+       WREG32(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
+}
+
+static void si_enable_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
+
+       tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
+       tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
+               DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE));
+       WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void si_program_vc(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       WREG32(CG_FTV, pi->vrc);
+}
+
+static void si_clear_vc(struct radeon_device *rdev)
+{
+       WREG32(CG_FTV, 0);
+}
+
+static u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
+{
+       u8 mc_para_index;
+
+       if (memory_clock < 10000)
+               mc_para_index = 0;
+       else if (memory_clock >= 80000)
+               mc_para_index = 0x0f;
+       else
+               mc_para_index = (u8)((memory_clock - 10000) / 5000 + 1);
+       return mc_para_index;
+}
+
+static u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode)
+{
+       u8 mc_para_index;
+
+       if (strobe_mode) {
+               if (memory_clock < 12500)
+                       mc_para_index = 0x00;
+               else if (memory_clock > 47500)
+                       mc_para_index = 0x0f;
+               else
+                       mc_para_index = (u8)((memory_clock - 10000) / 2500);
+       } else {
+               if (memory_clock < 65000)
+                       mc_para_index = 0x00;
+               else if (memory_clock > 135000)
+                       mc_para_index = 0x0f;
+               else
+                       mc_para_index = (u8)((memory_clock - 60000) / 5000);
+       }
+       return mc_para_index;
+}
+
+static u8 si_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       bool strobe_mode = false;
+       u8 result = 0;
+
+       if (mclk <= pi->mclk_strobe_mode_threshold)
+               strobe_mode = true;
+
+       if (pi->mem_gddr5)
+               result = si_get_mclk_frequency_ratio(mclk, strobe_mode);
+       else
+               result = si_get_ddr3_mclk_frequency_ratio(mclk);
+
+       if (strobe_mode)
+               result |= SISLANDS_SMC_STROBE_ENABLE;
+
+       return result;
+}
+
+static int si_upload_firmware(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+
+       si_reset_smc(rdev);
+       si_stop_smc_clock(rdev);
+
+       ret = si_load_smc_ucode(rdev, si_pi->sram_end);
+
+       return ret;
+}
+
+static bool si_validate_phase_shedding_tables(struct radeon_device *rdev,
+                                             const struct atom_voltage_table *table,
+                                             const struct radeon_phase_shedding_limits_table *limits)
+{
+       u32 data, num_bits, num_levels;
+
+       if ((table == NULL) || (limits == NULL))
+               return false;
+
+       data = table->mask_low;
+
+       num_bits = hweight32(data);
+
+       if (num_bits == 0)
+               return false;
+
+       num_levels = (1 << num_bits);
+
+       if (table->count != num_levels)
+               return false;
+
+       if (limits->count != (num_levels - 1))
+               return false;
+
+       return true;
+}
+
+static void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
+                                                    struct atom_voltage_table *voltage_table)
+{
+       unsigned int i, diff;
+
+       if (voltage_table->count <= SISLANDS_MAX_NO_VREG_STEPS)
+               return;
+
+       diff = voltage_table->count - SISLANDS_MAX_NO_VREG_STEPS;
+
+       for (i= 0; i < SISLANDS_MAX_NO_VREG_STEPS; i++)
+               voltage_table->entries[i] = voltage_table->entries[i + diff];
+
+       voltage_table->count = SISLANDS_MAX_NO_VREG_STEPS;
+}
+
+static int si_construct_voltage_tables(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+
+       ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
+                                           VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddc_voltage_table);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+               si_trim_voltage_table_to_fit_state_table(rdev, &eg_pi->vddc_voltage_table);
+
+       if (eg_pi->vddci_control) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDCI,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+
+               if (eg_pi->vddci_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(rdev, &eg_pi->vddci_voltage_table);
+       }
+
+       if (pi->mvdd_control) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_MVDDC,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &si_pi->mvdd_voltage_table);
+
+               if (ret) {
+                       pi->mvdd_control = false;
+                       return ret;
+               }
+
+               if (si_pi->mvdd_voltage_table.count == 0) {
+                       pi->mvdd_control = false;
+                       return -EINVAL;
+               }
+
+               if (si_pi->mvdd_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(rdev, &si_pi->mvdd_voltage_table);
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
+                                                   VOLTAGE_OBJ_PHASE_LUT, &si_pi->vddc_phase_shed_table);
+               if (ret)
+                       si_pi->vddc_phase_shed_control = false;
+
+               if ((si_pi->vddc_phase_shed_table.count == 0) ||
+                   (si_pi->vddc_phase_shed_table.count > SISLANDS_MAX_NO_VREG_STEPS))
+                       si_pi->vddc_phase_shed_control = false;
+       }
+
+       return 0;
+}
+
+static void si_populate_smc_voltage_table(struct radeon_device *rdev,
+                                         const struct atom_voltage_table *voltage_table,
+                                         SISLANDS_SMC_STATETABLE *table)
+{
+       unsigned int i;
+
+       for (i = 0; i < voltage_table->count; i++)
+               table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
+}
+
+static int si_populate_smc_voltage_tables(struct radeon_device *rdev,
+                                         SISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u8 i;
+
+       if (eg_pi->vddc_voltage_table.count) {
+               si_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table);
+               table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+                       cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+
+               for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
+                       if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
+                               table->maxVDDCIndexInPPTable = i;
+                               break;
+                       }
+               }
+       }
+
+       if (eg_pi->vddci_voltage_table.count) {
+               si_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table);
+
+               table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDCI] =
+                       cpu_to_be32(eg_pi->vddci_voltage_table.mask_low);
+       }
+
+
+       if (si_pi->mvdd_voltage_table.count) {
+               si_populate_smc_voltage_table(rdev, &si_pi->mvdd_voltage_table, table);
+
+               table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_MVDD] =
+                       cpu_to_be32(si_pi->mvdd_voltage_table.mask_low);
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               if (si_validate_phase_shedding_tables(rdev, &si_pi->vddc_phase_shed_table,
+                                                     &rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
+                       si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
+
+                       table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+                               cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
+
+                       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
+                                                  (u32)si_pi->vddc_phase_shed_table.phase_delay);
+               } else {
+                       si_pi->vddc_phase_shed_control = false;
+               }
+       }
+
+       return 0;
+}
+
+static int si_populate_voltage_value(struct radeon_device *rdev,
+                                    const struct atom_voltage_table *table,
+                                    u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < table->count; i++) {
+               if (value <= table->entries[i].value) {
+                       voltage->index = (u8)i;
+                       voltage->value = cpu_to_be16(table->entries[i].value);
+                       break;
+               }
+       }
+
+       if (i >= table->count)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int si_populate_mvdd_value(struct radeon_device *rdev, u32 mclk,
+                                 SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (pi->mvdd_control) {
+               if (mclk <= pi->mvdd_split_frequency)
+                       voltage->index = 0;
+               else
+                       voltage->index = (u8)(si_pi->mvdd_voltage_table.count) - 1;
+
+               voltage->value = cpu_to_be16(si_pi->mvdd_voltage_table.entries[voltage->index].value);
+       }
+       return 0;
+}
+
+static int si_get_std_voltage_value(struct radeon_device *rdev,
+                                   SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage)
+{
+       u16 v_index;
+       bool voltage_found = false;
+       *std_voltage = be16_to_cpu(voltage->value);
+
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries) {
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE) {
+                       if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries == NULL)
+                               return -EINVAL;
+
+                       for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                               if (be16_to_cpu(voltage->value) ==
+                                   (u16)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)
+                                               *std_voltage =
+                                                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+                                       else
+                                               *std_voltage =
+                                                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries[rdev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+                                       break;
+                               }
+                       }
+
+                       if (!voltage_found) {
+                               for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                                       if (be16_to_cpu(voltage->value) <=
+                                           (u16)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)
+                                                       *std_voltage =
+                                                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+                                               else
+                                                       *std_voltage =
+                                                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[rdev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       if ((u32)voltage->index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
+                               *std_voltage = rdev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc;
+               }
+       }
+
+       return 0;
+}
+
+static int si_populate_std_voltage_value(struct radeon_device *rdev,
+                                        u16 value, u8 index,
+                                        SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       voltage->index = index;
+       voltage->value = cpu_to_be16(value);
+
+       return 0;
+}
+
+static int si_populate_phase_shedding_value(struct radeon_device *rdev,
+                                           const struct radeon_phase_shedding_limits_table *limits,
+                                           u16 voltage, u32 sclk, u32 mclk,
+                                           SISLANDS_SMC_VOLTAGE_VALUE *smc_voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < limits->count; i++) {
+               if ((voltage <= limits->entries[i].voltage) &&
+                   (sclk <= limits->entries[i].sclk) &&
+                   (mclk <= limits->entries[i].mclk))
+                       break;
+       }
+
+       smc_voltage->phase_settings = (u8)i;
+
+       return 0;
+}
+
+static int si_init_arb_table_index(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = si_read_smc_sram_dword(rdev, si_pi->arb_table_start, &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= MC_CG_ARB_FREQ_F1 << 24;
+
+       return si_write_smc_sram_dword(rdev, si_pi->arb_table_start,  tmp, si_pi->sram_end);
+}
+
+static int si_initial_switch_from_arb_f0_to_f1(struct radeon_device *rdev)
+{
+       return ni_copy_and_switch_arb_sets(rdev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int si_reset_to_default(struct radeon_device *rdev)
+{
+       return (si_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_force_switch_to_arb_f0(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = si_read_smc_sram_dword(rdev, si_pi->arb_table_start,
+                                    &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp = (tmp >> 24) & 0xff;
+
+       if (tmp == MC_CG_ARB_FREQ_F0)
+               return 0;
+
+       return ni_copy_and_switch_arb_sets(rdev, tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static u32 si_calculate_memory_refresh_rate(struct radeon_device *rdev,
+                                           u32 engine_clock)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u32 dram_rows;
+       u32 dram_refresh_rate;
+       u32 mc_arb_rfsh_rate;
+       u32 tmp = (RREG32(MC_ARB_RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
+
+       if (pi->mem_gddr5)
+               dram_rows = 1 << (tmp + 10);
+       else
+               dram_rows = DDR3_DRAM_ROWS;
+
+       dram_refresh_rate = 1 << ((RREG32(MC_SEQ_MISC0) & 0x3) + 3);
+       mc_arb_rfsh_rate = ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+
+       return mc_arb_rfsh_rate;
+}
+
+static int si_populate_memory_timing_parameters(struct radeon_device *rdev,
+                                               struct rv7xx_pl *pl,
+                                               SMC_SIslands_MCArbDramTimingRegisterSet *arb_regs)
+{
+       u32 dram_timing;
+       u32 dram_timing2;
+       u32 burst_time;
+
+       arb_regs->mc_arb_rfsh_rate =
+               (u8)si_calculate_memory_refresh_rate(rdev, pl->sclk);
+
+       radeon_atom_set_engine_dram_timings(rdev,
+                                           pl->sclk,
+                                            pl->mclk);
+
+       dram_timing  = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+       burst_time = RREG32(MC_ARB_BURST_TIME) & STATE0_MASK;
+
+       arb_regs->mc_arb_dram_timing  = cpu_to_be32(dram_timing);
+       arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2);
+       arb_regs->mc_arb_burst_time = (u8)burst_time;
+
+       return 0;
+}
+
+static int si_do_program_memory_timing_parameters(struct radeon_device *rdev,
+                                                 struct radeon_ps *radeon_state,
+                                                 unsigned int first_arb_set)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int i, ret = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               ret = si_populate_memory_timing_parameters(rdev, &state->performance_levels[i], &arb_regs);
+               if (ret)
+                       break;
+               ret = si_copy_bytes_to_smc(rdev,
+                                          si_pi->arb_table_start +
+                                          offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+                                          sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i),
+                                          (u8 *)&arb_regs,
+                                          sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+                                          si_pi->sram_end);
+               if (ret)
+                       break;
+        }
+
+       return ret;
+}
+
+static int si_program_memory_timing_parameters(struct radeon_device *rdev,
+                                              struct radeon_ps *radeon_new_state)
+{
+       return si_do_program_memory_timing_parameters(rdev, radeon_new_state,
+                                                     SISLANDS_DRIVER_STATE_ARB_INDEX);
+}
+
+static int si_populate_initial_mvdd_value(struct radeon_device *rdev,
+                                         struct SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (pi->mvdd_control)
+               return si_populate_voltage_value(rdev, &si_pi->mvdd_voltage_table,
+                                                si_pi->mvdd_bootup_value, voltage);
+
+       return 0;
+}
+
+static int si_populate_smc_initial_state(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_initial_state,
+                                        SISLANDS_SMC_STATETABLE *table)
+{
+       struct ni_ps *initial_state = ni_get_ps(radeon_initial_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 reg;
+       int ret;
+
+       table->initialState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(si_pi->clock_registers.dll_cntl);
+       table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_ad_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_dq_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_1);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_2);
+       table->initialState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+       table->initialState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+       table->initialState.levels[0].mclk.mclk_value =
+               cpu_to_be32(initial_state->performance_levels[0].mclk);
+
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_4);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2  =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum_2);
+
+       table->initialState.levels[0].sclk.sclk_value =
+               cpu_to_be32(initial_state->performance_levels[0].sclk);
+
+       table->initialState.levels[0].arbRefreshState =
+               SISLANDS_INITIAL_STATE_ARB_INDEX;
+
+       table->initialState.levels[0].ACIndex = 0;
+
+       ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                       initial_state->performance_levels[0].vddc,
+                                       &table->initialState.levels[0].vddc);
+
+       if (!ret) {
+               u16 std_vddc;
+
+               ret = si_get_std_voltage_value(rdev,
+                                              &table->initialState.levels[0].vddc,
+                                              &std_vddc);
+               if (!ret)
+                       si_populate_std_voltage_value(rdev, std_vddc,
+                                                     table->initialState.levels[0].vddc.index,
+                                                     &table->initialState.levels[0].std_vddc);
+       }
+
+       if (eg_pi->vddci_control)
+               si_populate_voltage_value(rdev,
+                                         &eg_pi->vddci_voltage_table,
+                                         initial_state->performance_levels[0].vddci,
+                                         &table->initialState.levels[0].vddci);
+
+       if (si_pi->vddc_phase_shed_control)
+               si_populate_phase_shedding_value(rdev,
+                                                &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                initial_state->performance_levels[0].vddc,
+                                                initial_state->performance_levels[0].sclk,
+                                                initial_state->performance_levels[0].mclk,
+                                                &table->initialState.levels[0].vddc);
+
+       si_populate_initial_mvdd_value(rdev, &table->initialState.levels[0].mvdd);
+
+       reg = CG_R(0xffff) | CG_L(0);
+       table->initialState.levels[0].aT = cpu_to_be32(reg);
+
+       table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
+
+       table->initialState.levels[0].gen2PCIE = (u8)si_pi->boot_pcie_gen;
+
+       if (pi->mem_gddr5) {
+               table->initialState.levels[0].strobeMode =
+                       si_get_strobe_mode_settings(rdev,
+                                                   initial_state->performance_levels[0].mclk);
+
+               if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold)
+                       table->initialState.levels[0].mcFlags = SISLANDS_SMC_MC_EDC_RD_FLAG | SISLANDS_SMC_MC_EDC_WR_FLAG;
+               else
+                       table->initialState.levels[0].mcFlags =  0;
+       }
+
+       table->initialState.levelCount = 1;
+
+       table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       table->initialState.levels[0].dpm2.MaxPS = 0;
+       table->initialState.levels[0].dpm2.NearTDPDec = 0;
+       table->initialState.levels[0].dpm2.AboveSafeInc = 0;
+       table->initialState.levels[0].dpm2.BelowSafeInc = 0;
+       table->initialState.levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+       reg = MIN_POWER_MASK | MAX_POWER_MASK;
+       table->initialState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+       reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+       table->initialState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+       return 0;
+}
+
+static int si_populate_smc_acpi_state(struct radeon_device *rdev,
+                                     SISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 dll_cntl = si_pi->clock_registers.dll_cntl;
+       u32 mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+       u32 mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+       u32 mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+       u32 mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+       u32 reg;
+       int ret;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               pi->acpi_vddc, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = si_get_std_voltage_value(rdev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+                       if (!ret)
+                               si_populate_std_voltage_value(rdev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = si_pi->acpi_pcie_gen;
+
+               if (si_pi->vddc_phase_shed_control) {
+                       si_populate_phase_shedding_value(rdev,
+                                                        &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                        pi->acpi_vddc,
+                                                        0,
+                                                        0,
+                                                        &table->ACPIState.levels[0].vddc);
+               }
+       } else {
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
+                                               pi->min_vddc_in_table, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = si_get_std_voltage_value(rdev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+
+                       if (!ret)
+                               si_populate_std_voltage_value(rdev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = (u8)r600_get_pcie_gen_support(rdev,
+                                                                                   si_pi->sys_pcie_mask,
+                                                                                   si_pi->boot_pcie_gen,
+                                                                                   RADEON_PCIE_GEN1);
+
+               if (si_pi->vddc_phase_shed_control)
+                       si_populate_phase_shedding_value(rdev,
+                                                        &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                        pi->min_vddc_in_table,
+                                                        0,
+                                                        0,
+                                                        &table->ACPIState.levels[0].vddc);
+       }
+
+       if (pi->acpi_vddc) {
+               if (eg_pi->acpi_vddci)
+                       si_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table,
+                                                 eg_pi->acpi_vddci,
+                                                 &table->ACPIState.levels[0].vddci);
+       }
+
+       mclk_pwrmgt_cntl |= MRDCK0_RESET | MRDCK1_RESET;
+       mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       dll_cntl &= ~(MRDCK0_BYPASS | MRDCK1_BYPASS);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(dll_cntl);
+       table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL =
+               cpu_to_be32(mpll_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+               cpu_to_be32(mpll_func_cntl_1);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+               cpu_to_be32(mpll_func_cntl_2);
+       table->ACPIState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+       table->ACPIState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
+               cpu_to_be32(spll_func_cntl);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(spll_func_cntl_2);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(spll_func_cntl_3);
+       table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+               cpu_to_be32(spll_func_cntl_4);
+
+       table->ACPIState.levels[0].mclk.mclk_value = 0;
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       si_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
+
+       if (eg_pi->dynamic_ac_timing)
+               table->ACPIState.levels[0].ACIndex = 0;
+
+       table->ACPIState.levels[0].dpm2.MaxPS = 0;
+       table->ACPIState.levels[0].dpm2.NearTDPDec = 0;
+       table->ACPIState.levels[0].dpm2.AboveSafeInc = 0;
+       table->ACPIState.levels[0].dpm2.BelowSafeInc = 0;
+       table->ACPIState.levels[0].dpm2.PwrEfficiencyRatio = 0;
+
+       reg = MIN_POWER_MASK | MAX_POWER_MASK;
+       table->ACPIState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
+
+       reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
+       table->ACPIState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
+
+       return 0;
+}
+
+static int si_populate_ulv_state(struct radeon_device *rdev,
+                                SISLANDS_SMC_SWSTATE *state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       u32 sclk_in_sr = 1350; /* ??? */
+       int ret;
+
+       ret = si_convert_power_level_to_smc(rdev, &ulv->pl,
+                                           &state->levels[0]);
+       if (!ret) {
+               if (eg_pi->sclk_deep_sleep) {
+                       if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+                               state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+                       else
+                               state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+               }
+               if (ulv->one_pcie_lane_in_ulv)
+                       state->flags |= PPSMC_SWSTATE_FLAG_PCIE_X1;
+               state->levels[0].arbRefreshState = (u8)(SISLANDS_ULV_STATE_ARB_INDEX);
+               state->levels[0].ACIndex = 1;
+               state->levels[0].std_vddc = state->levels[0].vddc;
+               state->levelCount = 1;
+
+               state->flags |= PPSMC_SWSTATE_FLAG_DC;
+       }
+
+       return ret;
+}
+
+static int si_program_ulv_memory_timing_parameters(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int ret;
+
+       ret = si_populate_memory_timing_parameters(rdev, &ulv->pl,
+                                                  &arb_regs);
+       if (ret)
+               return ret;
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_ulv_volt_change_delay,
+                                  ulv->volt_change_delay);
+
+       ret = si_copy_bytes_to_smc(rdev,
+                                  si_pi->arb_table_start +
+                                  offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+                                  sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * SISLANDS_ULV_STATE_ARB_INDEX,
+                                  (u8 *)&arb_regs,
+                                  sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+                                  si_pi->sram_end);
+
+       return ret;
+}
+
+static void si_get_mvdd_configuration(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+
+       pi->mvdd_split_frequency = 30000;
+}
+
+static int si_init_smc_table(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps;
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+       SISLANDS_SMC_STATETABLE  *table = &si_pi->smc_statetable;
+       int ret;
+       u32 lane_width;
+       u32 vr_hot_gpio;
+
+       si_populate_smc_voltage_tables(rdev, table);
+
+       switch (rdev->pm.int_thermal_type) {
+       case THERMAL_TYPE_SI:
+       case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
+               break;
+       case THERMAL_TYPE_NONE:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
+               break;
+       default:
+               table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
+               break;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) {
+               if ((rdev->pdev->device != 0x6818) && (rdev->pdev->device != 0x6819))
+                       table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+       }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY)
+               table->systemFlags |= PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE) {
+               table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO;
+               vr_hot_gpio = rdev->pm.dpm.backbias_response_time;
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_vr_hot_gpio,
+                                          vr_hot_gpio);
+       }
+
+       ret = si_populate_smc_initial_state(rdev, radeon_boot_state, table);
+       if (ret)
+               return ret;
+
+       ret = si_populate_smc_acpi_state(rdev, table);
+       if (ret)
+               return ret;
+
+       table->driverState = table->initialState;
+
+       ret = si_do_program_memory_timing_parameters(rdev, radeon_boot_state,
+                                                    SISLANDS_INITIAL_STATE_ARB_INDEX);
+       if (ret)
+               return ret;
+
+       if (ulv->supported && ulv->pl.vddc) {
+               ret = si_populate_ulv_state(rdev, &table->ULVState);
+               if (ret)
+                       return ret;
+
+               ret = si_program_ulv_memory_timing_parameters(rdev);
+               if (ret)
+                       return ret;
+
+               WREG32(CG_ULV_CONTROL, ulv->cg_ulv_control);
+               WREG32(CG_ULV_PARAMETER, ulv->cg_ulv_parameter);
+
+               lane_width = radeon_get_pcie_lanes(rdev);
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+       } else {
+               table->ULVState = table->initialState;
+       }
+
+       return si_copy_bytes_to_smc(rdev, si_pi->state_table_start,
+                                   (u8 *)table, sizeof(SISLANDS_SMC_STATETABLE),
+                                   si_pi->sram_end);
+}
+
+static int si_calculate_sclk_params(struct radeon_device *rdev,
+                                   u32 engine_clock,
+                                   SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum = si_pi->clock_registers.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = si_pi->clock_registers.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+       tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16384;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK);
+       spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
+       spll_func_cntl |= SPLL_PDIV_A(dividers.post_div);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(2);
+
+        spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+        spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+        spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->sclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLK_S_MASK;
+                       cg_spll_spread_spectrum |= CLK_S(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+                       cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+               }
+       }
+
+       sclk->sclk_value = engine_clock;
+       sclk->vCG_SPLL_FUNC_CNTL = spll_func_cntl;
+       sclk->vCG_SPLL_FUNC_CNTL_2 = spll_func_cntl_2;
+       sclk->vCG_SPLL_FUNC_CNTL_3 = spll_func_cntl_3;
+       sclk->vCG_SPLL_FUNC_CNTL_4 = spll_func_cntl_4;
+       sclk->vCG_SPLL_SPREAD_SPECTRUM = cg_spll_spread_spectrum;
+       sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cg_spll_spread_spectrum_2;
+
+       return 0;
+}
+
+static int si_populate_sclk_value(struct radeon_device *rdev,
+                                 u32 engine_clock,
+                                 SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       SISLANDS_SMC_SCLK_VALUE sclk_tmp;
+       int ret;
+
+       ret = si_calculate_sclk_params(rdev, engine_clock, &sclk_tmp);
+       if (!ret) {
+               sclk->sclk_value = cpu_to_be32(sclk_tmp.sclk_value);
+               sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL);
+               sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_2);
+               sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_3);
+               sclk->vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_4);
+               sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM);
+               sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM_2);
+       }
+
+       return ret;
+}
+
+static int si_populate_mclk_value(struct radeon_device *rdev,
+                                 u32 engine_clock,
+                                 u32 memory_clock,
+                                 SISLANDS_SMC_MCLK_VALUE *mclk,
+                                 bool strobe_mode,
+                                 bool dll_state_on)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32  dll_cntl = si_pi->clock_registers.dll_cntl;
+       u32  mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32  mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+       u32  mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+       u32  mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+       u32  mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+       u32  mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+       u32  mpll_ss1 = si_pi->clock_registers.mpll_ss1;
+       u32  mpll_ss2 = si_pi->clock_registers.mpll_ss2;
+       struct atom_mpll_param mpll_param;
+       int ret;
+
+       ret = radeon_atom_get_memory_pll_dividers(rdev, memory_clock, strobe_mode, &mpll_param);
+       if (ret)
+               return ret;
+
+       mpll_func_cntl &= ~BWCTRL_MASK;
+       mpll_func_cntl |= BWCTRL(mpll_param.bwcntl);
+
+       mpll_func_cntl_1 &= ~(CLKF_MASK | CLKFRAC_MASK | VCO_MODE_MASK);
+       mpll_func_cntl_1 |= CLKF(mpll_param.clkf) |
+               CLKFRAC(mpll_param.clkfrac) | VCO_MODE(mpll_param.vco_mode);
+
+       mpll_ad_func_cntl &= ~YCLK_POST_DIV_MASK;
+       mpll_ad_func_cntl |= YCLK_POST_DIV(mpll_param.post_div);
+
+       if (pi->mem_gddr5) {
+               mpll_dq_func_cntl &= ~(YCLK_SEL_MASK | YCLK_POST_DIV_MASK);
+               mpll_dq_func_cntl |= YCLK_SEL(mpll_param.yclk_sel) |
+                       YCLK_POST_DIV(mpll_param.post_div);
+       }
+
+       if (pi->mclk_ss) {
+               struct radeon_atom_ss ss;
+               u32 freq_nom;
+               u32 tmp;
+               u32 reference_clock = rdev->clock.mpll.reference_freq;
+
+               if (pi->mem_gddr5)
+                       freq_nom = memory_clock * 4;
+               else
+                       freq_nom = memory_clock * 2;
+
+               tmp = freq_nom / reference_clock;
+               tmp = tmp * tmp;
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                     ASIC_INTERNAL_MEMORY_SS, freq_nom)) {
+                       u32 clks = reference_clock * 5 / ss.rate;
+                       u32 clkv = (u32)((((131 * ss.percentage * ss.rate) / 100) * tmp) / freq_nom);
+
+                        mpll_ss1 &= ~CLKV_MASK;
+                        mpll_ss1 |= CLKV(clkv);
+
+                        mpll_ss2 &= ~CLKS_MASK;
+                        mpll_ss2 |= CLKS(clks);
+               }
+       }
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(mpll_param.dll_speed);
+
+       if (dll_state_on)
+               mclk_pwrmgt_cntl |= MRDCK0_PDNB | MRDCK1_PDNB;
+       else
+               mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       mclk->mclk_value = cpu_to_be32(memory_clock);
+       mclk->vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl);
+       mclk->vMPLL_FUNC_CNTL_1 = cpu_to_be32(mpll_func_cntl_1);
+       mclk->vMPLL_FUNC_CNTL_2 = cpu_to_be32(mpll_func_cntl_2);
+       mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       mclk->vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
+       mclk->vDLL_CNTL = cpu_to_be32(dll_cntl);
+       mclk->vMPLL_SS = cpu_to_be32(mpll_ss1);
+       mclk->vMPLL_SS2 = cpu_to_be32(mpll_ss2);
+
+       return 0;
+}
+
+static void si_populate_smc_sp(struct radeon_device *rdev,
+                              struct radeon_ps *radeon_state,
+                              SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_ps *ps = ni_get_ps(radeon_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       int i;
+
+       for (i = 0; i < ps->performance_level_count - 1; i++)
+               smc_state->levels[i].bSP = cpu_to_be32(pi->dsp);
+
+       smc_state->levels[ps->performance_level_count - 1].bSP =
+               cpu_to_be32(pi->psp);
+}
+
+static int si_convert_power_level_to_smc(struct radeon_device *rdev,
+                                        struct rv7xx_pl *pl,
+                                        SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       int ret;
+       bool dll_state_on;
+       u16 std_vddc;
+       bool gmc_pg = false;
+
+       if (eg_pi->pcie_performance_request &&
+           (si_pi->force_pcie_gen != RADEON_PCIE_GEN_INVALID))
+               level->gen2PCIE = (u8)si_pi->force_pcie_gen;
+       else
+               level->gen2PCIE = (u8)pl->pcie_gen;
+
+       ret = si_populate_sclk_value(rdev, pl->sclk, &level->sclk);
+       if (ret)
+               return ret;
+
+       level->mcFlags =  0;
+
+       if (pi->mclk_stutter_mode_threshold &&
+           (pl->mclk <= pi->mclk_stutter_mode_threshold) &&
+           !eg_pi->uvd_enabled &&
+           (RREG32(DPG_PIPE_STUTTER_CONTROL) & STUTTER_ENABLE) &&
+           (rdev->pm.dpm.new_active_crtc_count <= 2)) {
+               level->mcFlags |= SISLANDS_SMC_MC_STUTTER_EN;
+
+               if (gmc_pg)
+                       level->mcFlags |= SISLANDS_SMC_MC_PG_EN;
+       }
+
+       if (pi->mem_gddr5) {
+               if (pl->mclk > pi->mclk_edc_enable_threshold)
+                       level->mcFlags |= SISLANDS_SMC_MC_EDC_RD_FLAG;
+
+               if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
+                       level->mcFlags |= SISLANDS_SMC_MC_EDC_WR_FLAG;
+
+               level->strobeMode = si_get_strobe_mode_settings(rdev, pl->mclk);
+
+               if (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) {
+                       if (si_get_mclk_frequency_ratio(pl->mclk, true) >=
+                           ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
+                               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+                       else
+                               dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
+               } else {
+                       dll_state_on = false;
+               }
+       } else {
+               level->strobeMode = si_get_strobe_mode_settings(rdev,
+                                                               pl->mclk);
+
+               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+       }
+
+       ret = si_populate_mclk_value(rdev,
+                                    pl->sclk,
+                                    pl->mclk,
+                                    &level->mclk,
+                                    (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) != 0, dll_state_on);
+       if (ret)
+               return ret;
+
+       ret = si_populate_voltage_value(rdev,
+                                       &eg_pi->vddc_voltage_table,
+                                       pl->vddc, &level->vddc);
+       if (ret)
+               return ret;
+
+
+       ret = si_get_std_voltage_value(rdev, &level->vddc, &std_vddc);
+       if (ret)
+               return ret;
+
+       ret = si_populate_std_voltage_value(rdev, std_vddc,
+                                           level->vddc.index, &level->std_vddc);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddci_control) {
+               ret = si_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table,
+                                               pl->vddci, &level->vddci);
+               if (ret)
+                       return ret;
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               ret = si_populate_phase_shedding_value(rdev,
+                                                      &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                      pl->vddc,
+                                                      pl->sclk,
+                                                      pl->mclk,
+                                                      &level->vddc);
+               if (ret)
+                       return ret;
+       }
+
+       level->MaxPoweredUpCU = si_pi->max_cu;
+
+       ret = si_populate_mvdd_value(rdev, pl->mclk, &level->mvdd);
+
+       return ret;
+}
+
+static int si_populate_smc_t(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_state,
+                            SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       u32 a_t;
+       u32 t_l, t_h;
+       u32 high_bsp;
+       int i, ret;
+
+       if (state->performance_level_count >= 9)
+               return -EINVAL;
+
+       if (state->performance_level_count < 2) {
+               a_t = CG_R(0xffff) | CG_L(0);
+               smc_state->levels[0].aT = cpu_to_be32(a_t);
+               return 0;
+       }
+
+       smc_state->levels[0].aT = cpu_to_be32(0);
+
+       for (i = 0; i <= state->performance_level_count - 2; i++) {
+               ret = r600_calculate_at(
+                       (50 / SISLANDS_MAX_HARDWARE_POWERLEVELS) * 100 * (i + 1),
+                       100 * R600_AH_DFLT,
+                       state->performance_levels[i + 1].sclk,
+                       state->performance_levels[i].sclk,
+                       &t_l,
+                       &t_h);
+
+               if (ret) {
+                       t_h = (i + 1) * 1000 - 50 * R600_AH_DFLT;
+                       t_l = (i + 1) * 1000 + 50 * R600_AH_DFLT;
+               }
+
+               a_t = be32_to_cpu(smc_state->levels[i].aT) & ~CG_R_MASK;
+               a_t |= CG_R(t_l * pi->bsp / 20000);
+               smc_state->levels[i].aT = cpu_to_be32(a_t);
+
+               high_bsp = (i == state->performance_level_count - 2) ?
+                       pi->pbsp : pi->bsp;
+               a_t = CG_R(0xffff) | CG_L(t_h * high_bsp / 20000);
+               smc_state->levels[i + 1].aT = cpu_to_be32(a_t);
+       }
+
+       return 0;
+}
+
+static int si_disable_ulv(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+
+       if (ulv->supported)
+               return (si_send_msg_to_smc(rdev, PPSMC_MSG_DisableULV) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+
+static bool si_is_state_ulv_compatible(struct radeon_device *rdev,
+                                      struct radeon_ps *radeon_state)
+{
+       const struct si_power_info *si_pi = si_get_pi(rdev);
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+       const struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+
+       if (state->performance_levels[0].mclk != ulv->pl.mclk)
+               return false;
+
+       /* XXX validate against display requirements! */
+
+       for (i = 0; i < rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count; i++) {
+               if (rdev->clock.current_dispclk <=
+                   rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].clk) {
+                       if (ulv->pl.vddc <
+                           rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].v)
+                               return false;
+               }
+       }
+
+       if ((radeon_state->vclk != 0) || (radeon_state->dclk != 0))
+               return false;
+
+       return true;
+}
+
+static int si_set_power_state_conditionally_enable_ulv(struct radeon_device *rdev,
+                                                      struct radeon_ps *radeon_new_state)
+{
+       const struct si_power_info *si_pi = si_get_pi(rdev);
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+
+       if (ulv->supported) {
+               if (si_is_state_ulv_compatible(rdev, radeon_new_state))
+                       return (si_send_msg_to_smc(rdev, PPSMC_MSG_EnableULV) == PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+       }
+       return 0;
+}
+
+static int si_convert_power_state_to_smc(struct radeon_device *rdev,
+                                        struct radeon_ps *radeon_state,
+                                        SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct ni_power_info *ni_pi = ni_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i, ret;
+       u32 threshold;
+       u32 sclk_in_sr = 1350; /* ??? */
+
+       if (state->performance_level_count > SISLANDS_MAX_HARDWARE_POWERLEVELS)
+               return -EINVAL;
+
+       threshold = state->performance_levels[state->performance_level_count-1].sclk * 100 / 100;
+
+       if (radeon_state->vclk && radeon_state->dclk) {
+               eg_pi->uvd_enabled = true;
+               if (eg_pi->smu_uvd_hs)
+                       smc_state->flags |= PPSMC_SWSTATE_FLAG_UVD;
+       } else {
+               eg_pi->uvd_enabled = false;
+       }
+
+       if (state->dc_compatible)
+               smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       smc_state->levelCount = 0;
+       for (i = 0; i < state->performance_level_count; i++) {
+               if (eg_pi->sclk_deep_sleep) {
+                       if ((i == 0) || si_pi->sclk_deep_sleep_above_low) {
+                               if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+                                       smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+                               else
+                                       smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+                       }
+               }
+
+               ret = si_convert_power_level_to_smc(rdev, &state->performance_levels[i],
+                                                   &smc_state->levels[i]);
+               smc_state->levels[i].arbRefreshState =
+                       (u8)(SISLANDS_DRIVER_STATE_ARB_INDEX + i);
+
+               if (ret)
+                       return ret;
+
+               if (ni_pi->enable_power_containment)
+                       smc_state->levels[i].displayWatermark =
+                               (state->performance_levels[i].sclk < threshold) ?
+                               PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+               else
+                       smc_state->levels[i].displayWatermark = (i < 2) ?
+                               PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
+
+               if (eg_pi->dynamic_ac_timing)
+                       smc_state->levels[i].ACIndex = SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i;
+               else
+                       smc_state->levels[i].ACIndex = 0;
+
+               smc_state->levelCount++;
+       }
+
+       si_write_smc_soft_register(rdev,
+                                  SI_SMC_SOFT_REGISTER_watermark_threshold,
+                                  threshold / 512);
+
+       si_populate_smc_sp(rdev, radeon_state, smc_state);
+
+       ret = si_populate_power_containment_values(rdev, radeon_state, smc_state);
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       ret = si_populate_sq_ramping_values(rdev, radeon_state, smc_state);
+        if (ret)
+               ni_pi->enable_sq_ramping = false;
+
+       return si_populate_smc_t(rdev, radeon_state, smc_state);
+}
+
+static int si_upload_sw_state(struct radeon_device *rdev,
+                             struct radeon_ps *radeon_new_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *new_state = ni_get_ps(radeon_new_state);
+       int ret;
+       u32 address = si_pi->state_table_start +
+               offsetof(SISLANDS_SMC_STATETABLE, driverState);
+       u32 state_size = sizeof(SISLANDS_SMC_SWSTATE) +
+               ((new_state->performance_level_count - 1) *
+                sizeof(SISLANDS_SMC_HW_PERFORMANCE_LEVEL));
+       SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.driverState;
+
+       memset(smc_state, 0, state_size);
+
+       ret = si_convert_power_state_to_smc(rdev, radeon_new_state, smc_state);
+       if (ret)
+               return ret;
+
+       ret = si_copy_bytes_to_smc(rdev, address, (u8 *)smc_state,
+                                  state_size, si_pi->sram_end);
+
+       return ret;
+}
+
+static int si_upload_ulv_state(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       int ret = 0;
+
+       if (ulv->supported && ulv->pl.vddc) {
+               u32 address = si_pi->state_table_start +
+                       offsetof(SISLANDS_SMC_STATETABLE, ULVState);
+               SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.ULVState;
+               u32 state_size = sizeof(SISLANDS_SMC_SWSTATE);
+
+               memset(smc_state, 0, state_size);
+
+               ret = si_populate_ulv_state(rdev, smc_state);
+               if (!ret)
+                       ret = si_copy_bytes_to_smc(rdev, address, (u8 *)smc_state,
+                                                  state_size, si_pi->sram_end);
+       }
+
+       return ret;
+}
+
+static int si_upload_smc_data(struct radeon_device *rdev)
+{
+       struct radeon_crtc *radeon_crtc = NULL;
+       int i;
+
+       if (rdev->pm.dpm.new_active_crtc_count == 0)
+               return 0;
+
+       for (i = 0; i < rdev->num_crtc; i++) {
+               if (rdev->pm.dpm.new_active_crtcs & (1 << i)) {
+                       radeon_crtc = rdev->mode_info.crtcs[i];
+                       break;
+               }
+       }
+
+       if (radeon_crtc == NULL)
+               return 0;
+
+       if (radeon_crtc->line_time <= 0)
+               return 0;
+
+       if (si_write_smc_soft_register(rdev,
+                                      SI_SMC_SOFT_REGISTER_crtc_index,
+                                      radeon_crtc->crtc_id) != PPSMC_Result_OK)
+               return 0;
+
+       if (si_write_smc_soft_register(rdev,
+                                      SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min,
+                                      radeon_crtc->wm_high / radeon_crtc->line_time) != PPSMC_Result_OK)
+               return 0;
+
+       if (si_write_smc_soft_register(rdev,
+                                      SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max,
+                                      radeon_crtc->wm_low / radeon_crtc->line_time) != PPSMC_Result_OK)
+               return 0;
+
+       return 0;
+}
+
+static int si_set_mc_special_registers(struct radeon_device *rdev,
+                                      struct si_mc_reg_table *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       u8 i, j, k;
+       u32 temp_reg;
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                       return -EINVAL;
+               switch (table->mc_reg_address[i].s1 << 2) {
+               case MC_SEQ_MISC1:
+                       temp_reg = RREG32(MC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++)
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       ((temp_reg & 0xffff0000)) |
+                                       ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+                       j++;
+                       if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       temp_reg = RREG32(MC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                               if (!pi->mem_gddr5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+                       if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       if (!pi->mem_gddr5) {
+                               table->mc_reg_address[j].s1 = MC_PMG_AUTO_CMD >> 2;
+                               table->mc_reg_address[j].s0 = MC_PMG_AUTO_CMD >> 2;
+                               for (k = 0; k < table->num_entries; k++)
+                                       table->mc_reg_table_entry[k].mc_data[j] =
+                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+                               j++;
+                               if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                                       return -EINVAL;
+                       }
+                       break;
+               case MC_SEQ_RESERVE_M:
+                       temp_reg = RREG32(MC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+                       for(k = 0; k < table->num_entries; k++)
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) |
+                                       (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                       j++;
+                       if (j > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static bool si_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+       bool result = true;
+
+       switch (in_reg) {
+       case  MC_SEQ_RAS_TIMING >> 2:
+               *out_reg = MC_SEQ_RAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_CAS_TIMING >> 2:
+               *out_reg = MC_SEQ_CAS_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING_LP >> 2;
+               break;
+        case MC_SEQ_MISC_TIMING2 >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_RD_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2;
+               break;
+        case MC_PMG_CMD_EMRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS1 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+               break;
+        case MC_SEQ_PMG_TIMING >> 2:
+               *out_reg = MC_SEQ_PMG_TIMING_LP >> 2;
+               break;
+        case MC_PMG_CMD_MRS2 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS2_LP >> 2;
+               break;
+        case MC_SEQ_WR_CTL_2 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_2_LP >> 2;
+               break;
+        default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void si_set_valid_flag(struct si_mc_reg_table *table)
+{
+       u8 i, j;
+
+       for (i = 0; i < table->last; i++) {
+               for (j = 1; j < table->num_entries; j++) {
+                       if (table->mc_reg_table_entry[j-1].mc_data[i] != table->mc_reg_table_entry[j].mc_data[i]) {
+                               table->valid_flag |= 1 << i;
+                               break;
+                       }
+               }
+       }
+}
+
+static void si_set_s0_mc_reg_index(struct si_mc_reg_table *table)
+{
+       u32 i;
+       u16 address;
+
+       for (i = 0; i < table->last; i++)
+               table->mc_reg_address[i].s0 = si_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+                       address : table->mc_reg_address[i].s1;
+
+}
+
+static int si_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
+                                     struct si_mc_reg_table *si_table)
+{
+       u8 i, j;
+
+       if (table->last > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+               return -EINVAL;
+       if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+               return -EINVAL;
+
+       for (i = 0; i < table->last; i++)
+               si_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+       si_table->last = table->last;
+
+       for (i = 0; i < table->num_entries; i++) {
+               si_table->mc_reg_table_entry[i].mclk_max =
+                       table->mc_reg_table_entry[i].mclk_max;
+               for (j = 0; j < table->last; j++) {
+                       si_table->mc_reg_table_entry[i].mc_data[j] =
+                               table->mc_reg_table_entry[i].mc_data[j];
+               }
+       }
+       si_table->num_entries = table->num_entries;
+
+       return 0;
+}
+
+static int si_initialize_mc_reg_table(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct atom_mc_reg_table *table;
+       struct si_mc_reg_table *si_table = &si_pi->mc_reg_table;
+       u8 module_index = rv770_get_memory_module_index(rdev);
+       int ret;
+
+       table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
+       WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
+       WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
+       WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
+       WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
+       WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
+       WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
+       WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING));
+       WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2));
+       WREG32(MC_SEQ_WR_CTL_2_LP, RREG32(MC_SEQ_WR_CTL_2));
+
+        ret = radeon_atom_init_mc_reg_table(rdev, module_index, table);
+        if (ret)
+                goto init_mc_done;
+
+        ret = si_copy_vbios_mc_reg_table(table, si_table);
+        if (ret)
+                goto init_mc_done;
+
+       si_set_s0_mc_reg_index(si_table);
+
+       ret = si_set_mc_special_registers(rdev, si_table);
+        if (ret)
+                goto init_mc_done;
+
+       si_set_valid_flag(si_table);
+
+init_mc_done:
+       kfree(table);
+
+       return ret;
+
+}
+
+static void si_populate_mc_reg_addresses(struct radeon_device *rdev,
+                                        SMC_SIslands_MCRegisters *mc_reg_table)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 i, j;
+
+       for (i = 0, j = 0; j < si_pi->mc_reg_table.last; j++) {
+               if (si_pi->mc_reg_table.valid_flag & (1 << j)) {
+                       if (i >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               break;
+                       mc_reg_table->address[i].s0 =
+                               cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+       mc_reg_table->last = (u8)i;
+}
+
+static void si_convert_mc_registers(const struct si_mc_reg_entry *entry,
+                                   SMC_SIslands_MCRegisterSet *data,
+                                   u32 num_entries, u32 valid_flag)
+{
+       u32 i, j;
+
+       for(i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & (1 << j)) {
+                       data->value[i] = cpu_to_be32(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static void si_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev,
+                                                struct rv7xx_pl *pl,
+                                                SMC_SIslands_MCRegisterSet *mc_reg_table_data)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 i = 0;
+
+       for (i = 0; i < si_pi->mc_reg_table.num_entries; i++) {
+               if (pl->mclk <= si_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+                       break;
+       }
+
+       if ((i == si_pi->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data, si_pi->mc_reg_table.last,
+                               si_pi->mc_reg_table.valid_flag);
+}
+
+static void si_convert_mc_reg_table_to_smc(struct radeon_device *rdev,
+                                          struct radeon_ps *radeon_state,
+                                          SMC_SIslands_MCRegisters *mc_reg_table)
+{
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               si_convert_mc_reg_table_entry_to_smc(rdev,
+                                                    &state->performance_levels[i],
+                                                    &mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]);
+       }
+}
+
+static int si_populate_mc_reg_table(struct radeon_device *rdev,
+                                   struct radeon_ps *radeon_boot_state)
+{
+       struct ni_ps *boot_state = ni_get_ps(radeon_boot_state);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+       memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+       si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_seq_index, 1);
+
+       si_populate_mc_reg_addresses(rdev, smc_mc_reg_table);
+
+       si_convert_mc_reg_table_entry_to_smc(rdev, &boot_state->performance_levels[0],
+                                            &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_INITIAL_SLOT]);
+
+       si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+                               &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ACPI_SLOT],
+                               si_pi->mc_reg_table.last,
+                               si_pi->mc_reg_table.valid_flag);
+
+       if (ulv->supported && ulv->pl.vddc != 0)
+               si_convert_mc_reg_table_entry_to_smc(rdev, &ulv->pl,
+                                                    &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT]);
+       else
+               si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+                                       &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT],
+                                       si_pi->mc_reg_table.last,
+                                       si_pi->mc_reg_table.valid_flag);
+
+       si_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, smc_mc_reg_table);
+
+       return si_copy_bytes_to_smc(rdev, si_pi->mc_reg_table_start,
+                                   (u8 *)smc_mc_reg_table,
+                                   sizeof(SMC_SIslands_MCRegisters), si_pi->sram_end);
+}
+
+static int si_upload_mc_reg_table(struct radeon_device *rdev,
+                                 struct radeon_ps *radeon_new_state)
+{
+       struct ni_ps *new_state = ni_get_ps(radeon_new_state);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       u32 address = si_pi->mc_reg_table_start +
+               offsetof(SMC_SIslands_MCRegisters,
+                        data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]);
+       SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+       memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+       si_convert_mc_reg_table_to_smc(rdev, radeon_new_state, smc_mc_reg_table);
+
+
+       return si_copy_bytes_to_smc(rdev, address,
+                                   (u8 *)&smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT],
+                                   sizeof(SMC_SIslands_MCRegisterSet) * new_state->performance_level_count,
+                                   si_pi->sram_end);
+
+}
+
+static void si_enable_voltage_control(struct radeon_device *rdev, bool enable)
+{
+        if (enable)
+                WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
+        else
+                WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+
+static enum radeon_pcie_gen si_get_maximum_link_speed(struct radeon_device *rdev,
+                                                     struct radeon_ps *radeon_state)
+{
+       struct ni_ps *state = ni_get_ps(radeon_state);
+       int i;
+       u16 pcie_speed, max_speed = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               pcie_speed = state->performance_levels[i].pcie_gen;
+               if (max_speed < pcie_speed)
+                       max_speed = pcie_speed;
+       }
+       return max_speed;
+}
+
+static u16 si_get_current_pcie_speed(struct radeon_device *rdev)
+{
+       u32 speed_cntl;
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE_MASK;
+       speed_cntl >>= LC_CURRENT_DATA_RATE_SHIFT;
+
+       return (u16)speed_cntl;
+}
+
+static void si_request_link_speed_change_before_state_change(struct radeon_device *rdev,
+                                                            struct radeon_ps *radeon_new_state,
+                                                            struct radeon_ps *radeon_current_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       enum radeon_pcie_gen target_link_speed = si_get_maximum_link_speed(rdev, radeon_new_state);
+       enum radeon_pcie_gen current_link_speed;
+
+       if (si_pi->force_pcie_gen == RADEON_PCIE_GEN_INVALID)
+               current_link_speed = si_get_maximum_link_speed(rdev, radeon_current_state);
+       else
+               current_link_speed = si_pi->force_pcie_gen;
+
+       si_pi->force_pcie_gen = RADEON_PCIE_GEN_INVALID;
+       si_pi->pspp_notify_required = false;
+       if (target_link_speed > current_link_speed) {
+               switch (target_link_speed) {
+#if defined(CONFIG_ACPI)
+               case RADEON_PCIE_GEN3:
+                       if (radeon_acpi_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN3, false) == 0)
+                               break;
+                       si_pi->force_pcie_gen = RADEON_PCIE_GEN2;
+                       if (current_link_speed == RADEON_PCIE_GEN2)
+                               break;
+               case RADEON_PCIE_GEN2:
+                       if (radeon_acpi_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, false) == 0)
+                               break;
+#endif
+               default:
+                       si_pi->force_pcie_gen = si_get_current_pcie_speed(rdev);
+                       break;
+               }
+       } else {
+               if (target_link_speed < current_link_speed)
+                       si_pi->pspp_notify_required = true;
+       }
+}
+
+static void si_notify_link_speed_change_after_state_change(struct radeon_device *rdev,
+                                                          struct radeon_ps *radeon_new_state,
+                                                          struct radeon_ps *radeon_current_state)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       enum radeon_pcie_gen target_link_speed = si_get_maximum_link_speed(rdev, radeon_new_state);
+       u8 request;
+
+       if (si_pi->pspp_notify_required) {
+               if (target_link_speed == RADEON_PCIE_GEN3)
+                       request = PCIE_PERF_REQ_PECI_GEN3;
+               else if (target_link_speed == RADEON_PCIE_GEN2)
+                       request = PCIE_PERF_REQ_PECI_GEN2;
+               else
+                       request = PCIE_PERF_REQ_PECI_GEN1;
+
+               if ((request == PCIE_PERF_REQ_PECI_GEN1) &&
+                   (si_get_current_pcie_speed(rdev) > 0))
+                       return;
+
+#if defined(CONFIG_ACPI)
+               radeon_acpi_pcie_performance_request(rdev, request, false);
+#endif
+       }
+}
+
+#if 0
+static int si_ds_request(struct radeon_device *rdev,
+                        bool ds_status_on, u32 count_write)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+
+       if (eg_pi->sclk_deep_sleep) {
+               if (ds_status_on)
+                       return (si_send_msg_to_smc(rdev, PPSMC_MSG_CancelThrottleOVRDSCLKDS) ==
+                               PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+               else
+                       return (si_send_msg_to_smc(rdev, PPSMC_MSG_ThrottleOVRDSCLKDS) ==
+                               PPSMC_Result_OK) ? 0 : -EINVAL;
+       }
+       return 0;
+}
+#endif
+
+static void si_set_max_cu_value(struct radeon_device *rdev)
+{
+       struct si_power_info *si_pi = si_get_pi(rdev);
+
+       if (rdev->family == CHIP_VERDE) {
+               switch (rdev->pdev->device) {
+               case 0x6820:
+               case 0x6825:
+               case 0x6821:
+               case 0x6823:
+               case 0x6827:
+                       si_pi->max_cu = 10;
+                       break;
+               case 0x682D:
+               case 0x6824:
+               case 0x682F:
+               case 0x6826:
+                       si_pi->max_cu = 8;
+                       break;
+               case 0x6828:
+               case 0x6830:
+               case 0x6831:
+               case 0x6838:
+               case 0x6839:
+               case 0x683D:
+                       si_pi->max_cu = 10;
+                       break;
+               case 0x683B:
+               case 0x683F:
+               case 0x6829:
+                       si_pi->max_cu = 8;
+                       break;
+               default:
+                       si_pi->max_cu = 0;
+                       break;
+               }
+       } else {
+               si_pi->max_cu = 0;
+       }
+}
+
+static int si_patch_single_dependency_table_based_on_leakage(struct radeon_device *rdev,
+                                                            struct radeon_clock_voltage_dependency_table *table)
+{
+       u32 i;
+       int j;
+       u16 leakage_voltage;
+
+       if (table) {
+               for (i = 0; i < table->count; i++) {
+                       switch (si_get_leakage_voltage_from_leakage_index(rdev,
+                                                                         table->entries[i].v,
+                                                                         &leakage_voltage)) {
+                       case 0:
+                               table->entries[i].v = leakage_voltage;
+                               break;
+                       case -EAGAIN:
+                               return -EINVAL;
+                       case -EINVAL:
+                       default:
+                               break;
+                       }
+               }
+
+               for (j = (table->count - 2); j >= 0; j--) {
+                       table->entries[j].v = (table->entries[j].v <= table->entries[j + 1].v) ?
+                               table->entries[j].v : table->entries[j + 1].v;
+               }
+       }
+       return 0;
+}
+
+static int si_patch_dependency_tables_based_on_leakage(struct radeon_device *rdev)
+{
+       int ret = 0;
+
+       ret = si_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
+       ret = si_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
+       ret = si_patch_single_dependency_table_based_on_leakage(rdev,
+                                                               &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk);
+       return ret;
+}
+
+static void si_set_pcie_lane_width_in_smc(struct radeon_device *rdev,
+                                         struct radeon_ps *radeon_new_state,
+                                         struct radeon_ps *radeon_current_state)
+{
+       u32 lane_width;
+       u32 new_lane_width =
+               (radeon_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+       u32 current_lane_width =
+               (radeon_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+
+       if (new_lane_width != current_lane_width) {
+               radeon_set_pcie_lanes(rdev, new_lane_width);
+               lane_width = radeon_get_pcie_lanes(rdev);
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+       }
+}
+
+void si_dpm_setup_asic(struct radeon_device *rdev)
+{
+       rv770_get_memory_type(rdev);
+       si_read_clock_registers(rdev);
+       si_enable_acpi_power_management(rdev);
+}
+
+static int si_set_thermal_temperature_range(struct radeon_device *rdev,
+                                       int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+
+       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;
+       }
+
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK);
+       WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK);
+       WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK);
+
+       rdev->pm.dpm.thermal.min_temp = low_temp;
+       rdev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+int si_dpm_enable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (si_is_smc_running(rdev))
+               return -EINVAL;
+       if (pi->voltage_control)
+               si_enable_voltage_control(rdev, true);
+       if (pi->mvdd_control)
+               si_get_mvdd_configuration(rdev);
+       if (pi->voltage_control) {
+               ret = si_construct_voltage_tables(rdev);
+               if (ret)
+                       return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_initialize_mc_reg_table(rdev);
+               if (ret)
+                       eg_pi->dynamic_ac_timing = false;
+       }
+       if (pi->dynamic_ss)
+               si_enable_spread_spectrum(rdev, true);
+       if (pi->thermal_protection)
+               si_enable_thermal_protection(rdev, true);
+       si_setup_bsp(rdev);
+       si_program_git(rdev);
+       si_program_tp(rdev);
+       si_program_tpp(rdev);
+       si_program_sstp(rdev);
+       si_enable_display_gap(rdev);
+       si_program_vc(rdev);
+       ret = si_upload_firmware(rdev);
+       if (ret)
+               return ret;
+       ret = si_process_firmware_header(rdev);
+       if (ret)
+               return ret;
+       ret = si_initial_switch_from_arb_f0_to_f1(rdev);
+       if (ret)
+               return ret;
+       ret = si_init_smc_table(rdev);
+       if (ret)
+               return ret;
+       ret = si_init_smc_spll_table(rdev);
+       if (ret)
+               return ret;
+       ret = si_init_arb_table_index(rdev);
+       if (ret)
+               return ret;
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_populate_mc_reg_table(rdev, boot_ps);
+               if (ret)
+                       return ret;
+       }
+       ret = si_initialize_smc_cac_tables(rdev);
+       if (ret)
+               return ret;
+       ret = si_initialize_hardware_cac_manager(rdev);
+       if (ret)
+               return ret;
+       ret = si_initialize_smc_dte_tables(rdev);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits(rdev, boot_ps);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits_2(rdev, boot_ps);
+       if (ret)
+               return ret;
+       si_program_response_times(rdev);
+       si_program_ds_registers(rdev);
+       si_dpm_start_smc(rdev);
+       ret = si_notify_smc_display_change(rdev, false);
+       if (ret)
+               return ret;
+       si_enable_sclk_control(rdev, true);
+       si_start_dpm(rdev);
+
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+               PPSMC_Result result;
+
+               ret = si_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret)
+                       return ret;
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+               result = si_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+       }
+
+       si_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       ni_update_current_ps(rdev, boot_ps);
+
+       return 0;
+}
+
+void si_dpm_disable(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       if (!si_is_smc_running(rdev))
+               return;
+       si_disable_ulv(rdev);
+       si_clear_vc(rdev);
+       if (pi->thermal_protection)
+               si_enable_thermal_protection(rdev, false);
+       si_enable_power_containment(rdev, boot_ps, false);
+       si_enable_smc_cac(rdev, boot_ps, false);
+       si_enable_spread_spectrum(rdev, false);
+       si_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
+       si_stop_dpm(rdev);
+       si_reset_to_default(rdev);
+       si_dpm_stop_smc(rdev);
+       si_force_switch_to_arb_f0(rdev);
+
+       ni_update_current_ps(rdev, boot_ps);
+}
+
+int si_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       ni_update_requested_ps(rdev, new_ps);
+
+       si_apply_state_adjust_rules(rdev, &eg_pi->requested_rps);
+
+       return 0;
+}
+
+int si_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+       struct radeon_ps *old_ps = &eg_pi->current_rps;
+       int ret;
+
+       ret = si_disable_ulv(rdev);
+       if (ret)
+               return ret;
+       ret = si_restrict_performance_levels_before_switch(rdev);
+       if (ret)
+               return ret;
+       if (eg_pi->pcie_performance_request)
+               si_request_link_speed_change_before_state_change(rdev, new_ps, old_ps);
+       rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps);
+       ret = si_enable_power_containment(rdev, new_ps, false);
+       if (ret)
+               return ret;
+       ret = si_enable_smc_cac(rdev, new_ps, false);
+       if (ret)
+               return ret;
+       ret = si_halt_smc(rdev);
+       if (ret)
+               return ret;
+       ret = si_upload_sw_state(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_upload_smc_data(rdev);
+       if (ret)
+               return ret;
+       ret = si_upload_ulv_state(rdev);
+       if (ret)
+               return ret;
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_upload_mc_reg_table(rdev, new_ps);
+               if (ret)
+                       return ret;
+       }
+       ret = si_program_memory_timing_parameters(rdev, new_ps);
+       if (ret)
+               return ret;
+       si_set_pcie_lane_width_in_smc(rdev, new_ps, old_ps);
+
+       ret = si_populate_smc_tdp_limits(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits_2(rdev, new_ps);
+       if (ret)
+               return ret;
+
+       ret = si_resume_smc(rdev);
+       if (ret)
+               return ret;
+       ret = si_set_sw_state(rdev);
+       if (ret)
+               return ret;
+       rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
+       if (eg_pi->pcie_performance_request)
+               si_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
+       ret = si_set_power_state_conditionally_enable_ulv(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_enable_smc_cac(rdev, new_ps, true);
+       if (ret)
+               return ret;
+       ret = si_enable_power_containment(rdev, new_ps, true);
+       if (ret)
+               return ret;
+
+#if 0
+       /* XXX */
+       ret = si_unrestrict_performance_levels_after_switch(rdev);
+       if (ret)
+               return ret;
+#endif
+
+       return 0;
+}
+
+
+int si_power_control_set_level(struct radeon_device *rdev)
+{
+       struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps;
+       int ret;
+
+       ret = si_restrict_performance_levels_before_switch(rdev);
+       if (ret)
+               return ret;
+       ret = si_halt_smc(rdev);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits_2(rdev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_resume_smc(rdev);
+       if (ret)
+               return ret;
+       ret = si_set_sw_state(rdev);
+       if (ret)
+               return ret;
+       return 0;
+}
+
+void si_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct radeon_ps *new_ps = &eg_pi->requested_rps;
+
+       ni_update_current_ps(rdev, new_ps);
+}
+
+
+void si_dpm_reset_asic(struct radeon_device *rdev)
+{
+       si_restrict_performance_levels_before_switch(rdev);
+       si_disable_ulv(rdev);
+       si_set_boot_state(rdev);
+}
+
+void si_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       si_program_display_gap(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+       struct _ATOM_PPLIB_SI_CLOCK_INFO si;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void si_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                         struct radeon_ps *rps,
+                                         struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                         u8 table_rev)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else if (r600_is_uvd_state(rps->class, rps->class2)) {
+               rps->vclk = RV770_DEFAULT_VCLK_FREQ;
+               rps->dclk = RV770_DEFAULT_DCLK_FREQ;
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void si_parse_pplib_clock_info(struct radeon_device *rdev,
+                                     struct radeon_ps *rps, int index,
+                                     union pplib_clock_info *clock_info)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(rdev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+       struct si_power_info *si_pi = si_get_pi(rdev);
+       struct ni_ps *ps = ni_get_ps(rps);
+       u16 leakage_voltage;
+       struct rv7xx_pl *pl = &ps->performance_levels[index];
+       int ret;
+
+       ps->performance_level_count = index + 1;
+
+       pl->sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+       pl->sclk |= clock_info->si.ucEngineClockHigh << 16;
+       pl->mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+       pl->mclk |= clock_info->si.ucMemoryClockHigh << 16;
+
+       pl->vddc = le16_to_cpu(clock_info->si.usVDDC);
+       pl->vddci = le16_to_cpu(clock_info->si.usVDDCI);
+       pl->flags = le32_to_cpu(clock_info->si.ulFlags);
+       pl->pcie_gen = r600_get_pcie_gen_support(rdev,
+                                                si_pi->sys_pcie_mask,
+                                                si_pi->boot_pcie_gen,
+                                                clock_info->si.ucPCIEGen);
+
+       /* patch up vddc if necessary */
+       ret = si_get_leakage_voltage_from_leakage_index(rdev, pl->vddc,
+                                                       &leakage_voltage);
+       if (ret == 0)
+               pl->vddc = leakage_voltage;
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+               pi->acpi_vddc = pl->vddc;
+               eg_pi->acpi_vddci = pl->vddci;
+               si_pi->acpi_pcie_gen = pl->pcie_gen;
+       }
+
+       if ((rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) &&
+           index == 0) {
+               /* XXX disable for A0 tahiti */
+               si_pi->ulv.supported = true;
+               si_pi->ulv.pl = *pl;
+               si_pi->ulv.one_pcie_lane_in_ulv = false;
+               si_pi->ulv.volt_change_delay = SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT;
+               si_pi->ulv.cg_ulv_parameter = SISLANDS_CGULVPARAMETER_DFLT;
+               si_pi->ulv.cg_ulv_control = SISLANDS_CGULVCONTROL_DFLT;
+       }
+
+       if (pi->min_vddc_in_table > pl->vddc)
+               pi->min_vddc_in_table = pl->vddc;
+
+       if (pi->max_vddc_in_table < pl->vddc)
+               pi->max_vddc_in_table = pl->vddc;
+
+       /* patch up boot state */
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               u16 vddc, vddci, mvdd;
+               radeon_atombios_get_default_voltages(rdev, &vddc, &vddci, &mvdd);
+               pl->mclk = rdev->clock.default_mclk;
+               pl->sclk = rdev->clock.default_sclk;
+               pl->vddc = vddc;
+               pl->vddci = vddci;
+               si_pi->mvdd_bootup_value = mvdd;
+       }
+
+       if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+           ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
+       }
+}
+
+static int si_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j, k, non_clock_array_index, clock_array_index;
+       union pplib_clock_info *clock_info;
+       struct _StateArray *state_array;
+       struct _ClockInfoArray *clock_info_array;
+       struct _NonClockInfoArray *non_clock_info_array;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       u8 *power_state_offset;
+       struct ni_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       state_array = (struct _StateArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usStateArrayOffset));
+       clock_info_array = (struct _ClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+       non_clock_info_array = (struct _NonClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 state_array->ucNumEntries, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       power_state_offset = (u8 *)state_array->states;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+       for (i = 0; i < state_array->ucNumEntries; i++) {
+               power_state = (union pplib_power_state *)power_state_offset;
+               non_clock_array_index = power_state->v2.nonClockInfoIndex;
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       &non_clock_info_array->nonClockInfo[non_clock_array_index];
+               if (!rdev->pm.power_state[i].clock_info)
+                       return -EINVAL;
+               ps = kzalloc(sizeof(struct ni_ps), GFP_KERNEL);
+               if (ps == NULL) {
+                       kfree(rdev->pm.dpm.ps);
+                       return -ENOMEM;
+               }
+               rdev->pm.dpm.ps[i].ps_priv = ps;
+               si_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                             non_clock_info,
+                                             non_clock_info_array->ucEntrySize);
+               k = 0;
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = power_state->v2.clockInfoIndex[j];
+                       if (clock_array_index >= clock_info_array->ucNumEntries)
+                               continue;
+                       if (k >= SISLANDS_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+                       si_parse_pplib_clock_info(rdev,
+                                                 &rdev->pm.dpm.ps[i], k,
+                                                 clock_info);
+                       k++;
+               }
+               power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+       }
+       rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+       return 0;
+}
+
+int si_dpm_init(struct radeon_device *rdev)
+{
+       struct rv7xx_power_info *pi;
+       struct evergreen_power_info *eg_pi;
+       struct ni_power_info *ni_pi;
+       struct si_power_info *si_pi;
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       u16 data_offset, size;
+       u8 frev, crev;
+       struct atom_clock_dividers dividers;
+       int ret;
+       u32 mask;
+
+       si_pi = kzalloc(sizeof(struct si_power_info), GFP_KERNEL);
+       if (si_pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = si_pi;
+       ni_pi = &si_pi->ni;
+       eg_pi = &ni_pi->eg;
+       pi = &eg_pi->rv7xx;
+
+       ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask);
+       if (ret)
+               si_pi->sys_pcie_mask = 0;
+       else
+               si_pi->sys_pcie_mask = mask;
+       si_pi->force_pcie_gen = RADEON_PCIE_GEN_INVALID;
+       si_pi->boot_pcie_gen = si_get_current_pcie_speed(rdev);
+
+       si_set_max_cu_value(rdev);
+
+       rv770_get_max_vddc(rdev);
+       si_get_leakage_vddc(rdev);
+       si_patch_dependency_tables_based_on_leakage(rdev);
+
+       pi->acpi_vddc = 0;
+       eg_pi->acpi_vddci = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = si_parse_power_table(rdev);
+       if (ret)
+               return ret;
+       ret = r600_parse_extended_power_table(rdev);
+       if (ret)
+               return ret;
+
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+               kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL);
+       if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+               r600_free_extended_power_table(rdev);
+               return -ENOMEM;
+       }
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 720;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 810;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 900;
+
+       if (rdev->pm.dpm.voltage_response_time == 0)
+               rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (rdev->pm.dpm.backbias_response_time == 0)
+               rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       eg_pi->smu_uvd_hs = false;
+
+       pi->mclk_strobe_mode_threshold = 40000;
+       if (si_is_special_1gb_platform(rdev))
+               pi->mclk_stutter_mode_threshold = 0;
+       else
+               pi->mclk_stutter_mode_threshold = pi->mclk_strobe_mode_threshold;
+       pi->mclk_edc_enable_threshold = 40000;
+       eg_pi->mclk_edc_wr_enable_threshold = 40000;
+
+       ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold;
+
+       pi->voltage_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_GPIO_LUT);
+
+       pi->mvdd_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, VOLTAGE_OBJ_GPIO_LUT);
+
+       eg_pi->vddci_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, VOLTAGE_OBJ_GPIO_LUT);
+
+       si_pi->vddc_phase_shed_control =
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_PHASE_LUT);
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->sclk_ss = true;
+               pi->mclk_ss = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->sclk_ss = false;
+               pi->mclk_ss = false;
+               pi->dynamic_ss = true;
+       }
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->vrc = SISLANDS_VRC_DFLT;
+
+       pi->gfx_clock_gating = true;
+
+       eg_pi->sclk_deep_sleep = true;
+       si_pi->sclk_deep_sleep_above_low = false;
+
+       if (pi->gfx_clock_gating &&
+           (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       eg_pi->dynamic_ac_timing = true;
+
+       eg_pi->light_sleep = true;
+#if defined(CONFIG_ACPI)
+       eg_pi->pcie_performance_request =
+               radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
+       eg_pi->pcie_performance_request = false;
+#endif
+
+       si_pi->sram_end = SMC_RAM_END;
+
+       rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 4;
+       rdev->pm.dpm.dyn_state.sclk_mclk_delta = 15000;
+       rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.values = NULL;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+
+       si_initialize_powertune_defaults(rdev);
+
+       return 0;
+}
+
+void si_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+       kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+       r600_free_extended_power_table(rdev);
+}
+
diff --git a/drivers/gpu/drm/radeon/si_dpm.h b/drivers/gpu/drm/radeon/si_dpm.h
new file mode 100644 (file)
index 0000000..4ce5032
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2012 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#ifndef __SI_DPM_H__
+#define __SI_DPM_H__
+
+#include "ni_dpm.h"
+#include "sislands_smc.h"
+
+enum si_cac_config_reg_type
+{
+       SISLANDS_CACCONFIG_MMR = 0,
+       SISLANDS_CACCONFIG_CGIND,
+       SISLANDS_CACCONFIG_MAX
+};
+
+struct si_cac_config_reg
+{
+       u32 offset;
+       u32 mask;
+       u32 shift;
+       u32 value;
+       enum si_cac_config_reg_type type;
+};
+
+struct si_powertune_data
+{
+       u32 cac_window;
+       u32 l2_lta_window_size_default;
+       u8 lts_truncate_default;
+       u8 shift_n_default;
+       u8 operating_temp;
+       struct ni_leakage_coeffients leakage_coefficients;
+       u32 fixed_kt;
+       u32 lkge_lut_v0_percent;
+       u8 dc_cac[NISLANDS_DCCAC_MAX_LEVELS];
+       bool enable_powertune_by_default;
+};
+
+struct si_dyn_powertune_data
+{
+       u32 cac_leakage;
+       s32 leakage_minimum_temperature;
+       u32 wintime;
+       u32 l2_lta_window_size;
+       u8 lts_truncate;
+       u8 shift_n;
+       u8 dc_pwr_value;
+       bool disable_uvd_powertune;
+};
+
+struct si_dte_data
+{
+       u32 tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+       u32 r[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+       u32 k;
+       u32 t0;
+       u32 max_t;
+       u8 window_size;
+       u8 temp_select;
+       u8 dte_mode;
+       u8 tdep_count;
+       u8 t_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 tdep_r[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 t_threshold;
+       bool enable_dte_by_default;
+};
+
+struct si_clock_registers {
+       u32 cg_spll_func_cntl;
+       u32 cg_spll_func_cntl_2;
+       u32 cg_spll_func_cntl_3;
+       u32 cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2;
+       u32 dll_cntl;
+       u32 mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl;
+       u32 mpll_func_cntl;
+       u32 mpll_func_cntl_1;
+       u32 mpll_func_cntl_2;
+       u32 mpll_ss1;
+       u32 mpll_ss2;
+};
+
+struct si_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct si_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       u16 valid_flag;
+       struct si_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+#define SISLANDS_MCREGISTERTABLE_INITIAL_SLOT               0
+#define SISLANDS_MCREGISTERTABLE_ACPI_SLOT                  1
+#define SISLANDS_MCREGISTERTABLE_ULV_SLOT                   2
+#define SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT     3
+
+struct si_leakage_voltage_entry
+{
+       u16 voltage;
+       u16 leakage_index;
+};
+
+#define SISLANDS_LEAKAGE_INDEX0     0xff01
+#define SISLANDS_MAX_LEAKAGE_COUNT  4
+
+struct si_leakage_voltage
+{
+       u16 count;
+       struct si_leakage_voltage_entry entries[SISLANDS_MAX_LEAKAGE_COUNT];
+};
+
+#define SISLANDS_MAX_HARDWARE_POWERLEVELS 5
+
+struct si_ulv_param {
+       bool supported;
+       u32 cg_ulv_control;
+       u32 cg_ulv_parameter;
+       u32 volt_change_delay;
+       struct rv7xx_pl pl;
+       bool one_pcie_lane_in_ulv;
+};
+
+struct si_power_info {
+       /* must be first! */
+       struct ni_power_info ni;
+       struct si_clock_registers clock_registers;
+       struct si_mc_reg_table mc_reg_table;
+       struct atom_voltage_table mvdd_voltage_table;
+       struct atom_voltage_table vddc_phase_shed_table;
+       struct si_leakage_voltage leakage_voltage;
+       u16 mvdd_bootup_value;
+       struct si_ulv_param ulv;
+       u32 max_cu;
+       /* pcie gen */
+       enum radeon_pcie_gen force_pcie_gen;
+       enum radeon_pcie_gen boot_pcie_gen;
+       enum radeon_pcie_gen acpi_pcie_gen;
+       u32 sys_pcie_mask;
+       /* flags */
+       bool enable_dte;
+       bool enable_ppm;
+       bool vddc_phase_shed_control;
+       bool pspp_notify_required;
+       bool sclk_deep_sleep_above_low;
+       /* smc offsets */
+       u32 sram_end;
+       u32 state_table_start;
+       u32 soft_regs_start;
+       u32 mc_reg_table_start;
+       u32 arb_table_start;
+       u32 cac_table_start;
+       u32 dte_table_start;
+       u32 spll_table_start;
+       u32 papm_cfg_table_start;
+       /* CAC stuff */
+       const struct si_cac_config_reg *cac_weights;
+       const struct si_cac_config_reg *lcac_config;
+       const struct si_cac_config_reg *cac_override;
+       const struct si_powertune_data *powertune_data;
+       struct si_dyn_powertune_data dyn_powertune_data;
+       /* DTE stuff */
+       struct si_dte_data dte_data;
+       /* scratch structs */
+       SMC_SIslands_MCRegisters smc_mc_reg_table;
+       SISLANDS_SMC_STATETABLE smc_statetable;
+       PP_SIslands_PAPMParameters papm_parm;
+};
+
+#define SISLANDS_INITIAL_STATE_ARB_INDEX    0
+#define SISLANDS_ACPI_STATE_ARB_INDEX       1
+#define SISLANDS_ULV_STATE_ARB_INDEX        2
+#define SISLANDS_DRIVER_STATE_ARB_INDEX     3
+
+#define SISLANDS_DPM2_MAX_PULSE_SKIP        256
+
+#define SISLANDS_DPM2_NEAR_TDP_DEC          10
+#define SISLANDS_DPM2_ABOVE_SAFE_INC        5
+#define SISLANDS_DPM2_BELOW_SAFE_INC        20
+
+#define SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT            80
+
+#define SISLANDS_DPM2_MAXPS_PERCENT_H                   99
+#define SISLANDS_DPM2_MAXPS_PERCENT_M                   99
+
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER                 0x3FFF
+#define SISLANDS_DPM2_SQ_RAMP_MIN_POWER                 0x12
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA           0x15
+#define SISLANDS_DPM2_SQ_RAMP_STI_SIZE                  0x1E
+#define SISLANDS_DPM2_SQ_RAMP_LTI_RATIO                 0xF
+
+#define SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN         10
+
+#define SISLANDS_VRC_DFLT                               0xC000B3
+#define SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT             1687
+#define SISLANDS_CGULVPARAMETER_DFLT                    0x00040035
+#define SISLANDS_CGULVCONTROL_DFLT                      0x1f007550
+
+
+#endif
diff --git a/drivers/gpu/drm/radeon/si_smc.c b/drivers/gpu/drm/radeon/si_smc.c
new file mode 100644 (file)
index 0000000..5f524c0
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2011 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.
+ *
+ * Authors: Alex Deucher
+ */
+
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "sid.h"
+#include "ppsmc.h"
+#include "radeon_ucode.h"
+
+int si_set_smc_sram_address(struct radeon_device *rdev,
+                           u32 smc_address, u32 limit)
+{
+       if (smc_address & 3)
+               return -EINVAL;
+       if ((smc_address + 3) > limit)
+               return -EINVAL;
+
+       WREG32(SMC_IND_INDEX_0, smc_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int si_copy_bytes_to_smc(struct radeon_device *rdev,
+                        u32 smc_start_address,
+                        const u8 *src, u32 byte_count, u32 limit)
+{
+       int ret;
+       u32 data, original_data, addr, extra_shift;
+
+       if (smc_start_address & 3)
+               return -EINVAL;
+       if ((smc_start_address + byte_count) > limit)
+               return -EINVAL;
+
+       addr = smc_start_address;
+
+       while (byte_count >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               ret = si_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       /* RMW for the final bytes */
+       if (byte_count > 0) {
+               data = 0;
+
+               ret = si_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               original_data = RREG32(SMC_IND_DATA_0);
+
+               extra_shift = 8 * (4 - byte_count);
+
+               while (byte_count > 0) {
+                       /* SMC address space is BE */
+                       data = (data << 8) + *src++;
+                       byte_count--;
+               }
+
+               data <<= extra_shift;
+
+               data |= (original_data & ~((~0UL) << extra_shift));
+
+               ret = si_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_IND_DATA_0, data);
+       }
+       return 0;
+}
+
+void si_start_smc(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+
+       tmp &= ~RST_REG;
+
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+void si_reset_smc(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+
+       tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+       tmp |= RST_REG;
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+int si_program_jump_on_start(struct radeon_device *rdev)
+{
+       static u8 data[] = { 0x0E, 0x00, 0x40, 0x40 };
+
+       return si_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
+}
+
+void si_stop_smc_clock(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       tmp |= CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+void si_start_smc_clock(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       tmp &= ~CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+bool si_is_smc_running(struct radeon_device *rdev)
+{
+       u32 rst = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+       u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       if (!(rst & RST_REG) && !(clk & CK_DISABLE))
+               return true;
+
+       return false;
+}
+
+PPSMC_Result si_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
+{
+       u32 tmp;
+       int i;
+
+       if (!si_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;
+}
+
+PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i;
+
+       if (!si_is_smc_running(rdev))
+               return PPSMC_Result_OK;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+               if ((tmp & CKEN) == 0)
+                       break;
+               udelay(1);
+       }
+
+       return PPSMC_Result_OK;
+}
+
+int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
+{
+       u32 ucode_start_address;
+       u32 ucode_size;
+       const u8 *src;
+       u32 data;
+
+       if (!rdev->smc_fw)
+               return -EINVAL;
+
+       switch (rdev->family) {
+       case CHIP_TAHITI:
+               ucode_start_address = TAHITI_SMC_UCODE_START;
+               ucode_size = TAHITI_SMC_UCODE_SIZE;
+               break;
+       case CHIP_PITCAIRN:
+               ucode_start_address = PITCAIRN_SMC_UCODE_START;
+               ucode_size = PITCAIRN_SMC_UCODE_SIZE;
+               break;
+       case CHIP_VERDE:
+               ucode_start_address = VERDE_SMC_UCODE_START;
+               ucode_size = VERDE_SMC_UCODE_SIZE;
+               break;
+       case CHIP_OLAND:
+               ucode_start_address = OLAND_SMC_UCODE_START;
+               ucode_size = OLAND_SMC_UCODE_SIZE;
+               break;
+       case CHIP_HAINAN:
+               ucode_start_address = HAINAN_SMC_UCODE_START;
+               ucode_size = HAINAN_SMC_UCODE_SIZE;
+               break;
+       default:
+               DRM_ERROR("unknown asic in smc ucode loader\n");
+               BUG();
+       }
+
+       if (ucode_size & 3)
+               return -EINVAL;
+
+       src = (const u8 *)rdev->smc_fw->data;
+       WREG32(SMC_IND_INDEX_0, ucode_start_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
+       while (ucode_size >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               ucode_size -= 4;
+       }
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                          u32 *value, u32 limit)
+{
+       int ret;
+
+       ret = si_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       *value = RREG32(SMC_IND_DATA_0);
+       return 0;
+}
+
+int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                           u32 value, u32 limit)
+{
+       int ret;
+
+       ret = si_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       WREG32(SMC_IND_DATA_0, value);
+       return 0;
+}
index 1390073..299d657 100644 (file)
 #define SI_MAX_TCC               16
 #define SI_MAX_TCC_MASK          0xFFFF
 
+/* SMC IND accessor regs */
+#define SMC_IND_INDEX_0                              0x200
+#define SMC_IND_DATA_0                               0x204
+
+#define SMC_IND_ACCESS_CNTL                          0x228
+#       define AUTO_INCREMENT_IND_0                  (1 << 0)
+#define SMC_MESSAGE_0                                0x22c
+#define SMC_RESP_0                                   0x230
+
 /* CG IND registers are accessed via SMC indirect space + SMC_CG_IND_START */
 #define SMC_CG_IND_START                    0xc0030000
+#define SMC_CG_IND_END                      0xc0040000
 
 #define        CG_CGTT_LOCAL_0                         0x400
 #define        CG_CGTT_LOCAL_1                         0x401
 
+/* SMC IND registers */
+#define        SMC_SYSCON_RESET_CNTL                           0x80000000
+#       define RST_REG                                  (1 << 0)
+#define        SMC_SYSCON_CLOCK_CNTL_0                         0x80000004
+#       define CK_DISABLE                               (1 << 0)
+#       define CKEN                                     (1 << 24)
+
+#define VGA_HDP_CONTROL                                0x328
+#define                VGA_MEMORY_DISABLE                              (1 << 4)
+
+#define DCCG_DISP_SLOW_SELECT_REG                       0x4fc
+#define                DCCG_DISP1_SLOW_SELECT(x)               ((x) << 0)
+#define                DCCG_DISP1_SLOW_SELECT_MASK             (7 << 0)
+#define                DCCG_DISP1_SLOW_SELECT_SHIFT            0
+#define                DCCG_DISP2_SLOW_SELECT(x)               ((x) << 4)
+#define                DCCG_DISP2_SLOW_SELECT_MASK             (7 << 4)
+#define                DCCG_DISP2_SLOW_SELECT_SHIFT            4
+
+#define        CG_SPLL_FUNC_CNTL                               0x600
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_SLEEP                              (1 << 1)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 4)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 4)
+#define                SPLL_PDIV_A(x)                          ((x) << 20)
+#define                SPLL_PDIV_A_MASK                        (0x7f << 20)
+#define                SPLL_PDIV_A_SHIFT                       20
+#define        CG_SPLL_FUNC_CNTL_2                             0x604
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0x608
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_FB_DIV_SHIFT                       0
+#define                SPLL_DITHEN                             (1 << 28)
+#define        CG_SPLL_FUNC_CNTL_4                             0x60c
+
+#define        SPLL_CNTL_MODE                                  0x618
+#      define SPLL_REFCLK_SEL(x)                       ((x) << 8)
+#      define SPLL_REFCLK_SEL_MASK                     0xFF00
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x620
+#define                SSEN                                    (1 << 0)
+#define                CLK_S(x)                                ((x) << 4)
+#define                CLK_S_MASK                              (0xfff << 4)
+#define                CLK_S_SHIFT                             4
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0x624
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+#define                CLK_V_SHIFT                             0
+
+#define        CG_SPLL_AUTOSCALE_CNTL                          0x62c
+#       define AUTOSCALE_ON_SS_CLEAR                    (1 << 9)
+
 /* discrete uvd clocks */
 #define        CG_UPLL_FUNC_CNTL                               0x634
 #      define UPLL_RESET_MASK                          0x00000001
 #define        CG_UPLL_SPREAD_SPECTRUM                         0x650
 #      define SSEN_MASK                                0x00000001
 
-#define        CG_MULT_THERMAL_STATUS                                  0x714
-#define                ASIC_MAX_TEMP(x)                                ((x) << 0)
-#define                ASIC_MAX_TEMP_MASK                              0x000001ff
-#define                ASIC_MAX_TEMP_SHIFT                             0
-#define                CTF_TEMP(x)                                     ((x) << 9)
-#define                CTF_TEMP_MASK                                   0x0003fe00
-#define                CTF_TEMP_SHIFT                                  9
-
-#define VGA_HDP_CONTROL                                0x328
-#define                VGA_MEMORY_DISABLE                              (1 << 4)
-
-#define        SPLL_CNTL_MODE                                  0x618
-#      define SPLL_REFCLK_SEL(x)                       ((x) << 8)
-#      define SPLL_REFCLK_SEL_MASK                     0xFF00
-
 #define        MPLL_BYPASSCLK_SEL                              0x65c
 #      define MPLL_CLKOUT_SEL(x)                       ((x) << 8)
 #      define MPLL_CLKOUT_SEL_MASK                     0xFF00
 #      define ZCLK_SEL(x)                              ((x) << 8)
 #      define ZCLK_SEL_MASK                            0xFF00
 
+#define        CG_THERMAL_CTRL                                 0x700
+#define        DPM_EVENT_SRC(x)                        ((x) << 0)
+#define        DPM_EVENT_SRC_MASK                      (7 << 0)
+#define                DIG_THERM_DPM(x)                        ((x) << 14)
+#define                DIG_THERM_DPM_MASK                      0x003FC000
+#define                DIG_THERM_DPM_SHIFT                     14
+
+#define        CG_THERMAL_INT                                  0x708
+#define                DIG_THERM_INTH(x)                       ((x) << 8)
+#define                DIG_THERM_INTH_MASK                     0x0000FF00
+#define                DIG_THERM_INTH_SHIFT                    8
+#define                DIG_THERM_INTL(x)                       ((x) << 16)
+#define                DIG_THERM_INTL_MASK                     0x00FF0000
+#define                DIG_THERM_INTL_SHIFT                    16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
+
+#define        CG_MULT_THERMAL_STATUS                                  0x714
+#define                ASIC_MAX_TEMP(x)                                ((x) << 0)
+#define                ASIC_MAX_TEMP_MASK                              0x000001ff
+#define                ASIC_MAX_TEMP_SHIFT                             0
+#define                CTF_TEMP(x)                                     ((x) << 9)
+#define                CTF_TEMP_MASK                                   0x0003fe00
+#define                CTF_TEMP_SHIFT                                  9
+
+#define GENERAL_PWRMGT                                  0x780
+#       define GLOBAL_PWRMGT_EN                         (1 << 0)
+#       define STATIC_PM_EN                             (1 << 1)
+#       define THERMAL_PROTECTION_DIS                   (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                  (1 << 3)
+#       define SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (1 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#define CG_TPC                                            0x784
+#define SCLK_PWRMGT_CNTL                                  0x788
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
+#       define SCLK_LOW_D1                                (1 << 1)
+#       define FIR_RESET                                  (1 << 4)
+#       define FIR_FORCE_TREND_SEL                        (1 << 5)
+#       define FIR_TREND_MODE                             (1 << 6)
+#       define DYN_GFX_CLK_OFF_EN                         (1 << 7)
+#       define GFX_CLK_FORCE_ON                           (1 << 8)
+#       define GFX_CLK_REQUEST_OFF                        (1 << 9)
+#       define GFX_CLK_FORCE_OFF                          (1 << 10)
+#       define GFX_CLK_OFF_ACPI_D1                        (1 << 11)
+#       define GFX_CLK_OFF_ACPI_D2                        (1 << 12)
+#       define GFX_CLK_OFF_ACPI_D3                        (1 << 13)
+#       define DYN_LIGHT_SLEEP_EN                         (1 << 14)
+
+#define CG_FTV                                            0x7bc
+
+#define CG_FFCT_0                                         0x7c0
+#       define UTC_0(x)                                   ((x) << 0)
+#       define UTC_0_MASK                                 (0x3ff << 0)
+#       define DTC_0(x)                                   ((x) << 10)
+#       define DTC_0_MASK                                 (0x3ff << 10)
+
+#define CG_BSP                                          0x7fc
+#       define BSP(x)                                  ((x) << 0)
+#       define BSP_MASK                                        (0xffff << 0)
+#       define BSU(x)                                  ((x) << 16)
+#       define BSU_MASK                                        (0xf << 16)
+#define CG_AT                                           0x800
+#       define CG_R(x)                                 ((x) << 0)
+#       define CG_R_MASK                               (0xffff << 0)
+#       define CG_L(x)                                 ((x) << 16)
+#       define CG_L_MASK                               (0xffff << 16)
+
+#define CG_GIT                                          0x804
+#       define CG_GICST(x)                              ((x) << 0)
+#       define CG_GICST_MASK                            (0xffff << 0)
+#       define CG_GIPOT(x)                              ((x) << 16)
+#       define CG_GIPOT_MASK                            (0xffff << 16)
+
+#define CG_SSP                                            0x80c
+#       define SST(x)                                     ((x) << 0)
+#       define SST_MASK                                   (0xffff << 0)
+#       define SSTU(x)                                    ((x) << 16)
+#       define SSTU_MASK                                  (0xf << 16)
+
+#define CG_DISPLAY_GAP_CNTL                               0x828
+#       define DISP1_GAP(x)                               ((x) << 0)
+#       define DISP1_GAP_MASK                             (3 << 0)
+#       define DISP2_GAP(x)                               ((x) << 2)
+#       define DISP2_GAP_MASK                             (3 << 2)
+#       define VBI_TIMER_COUNT(x)                         ((x) << 4)
+#       define VBI_TIMER_COUNT_MASK                       (0x3fff << 4)
+#       define VBI_TIMER_UNIT(x)                          ((x) << 20)
+#       define VBI_TIMER_UNIT_MASK                        (7 << 20)
+#       define DISP1_GAP_MCHG(x)                          ((x) << 24)
+#       define DISP1_GAP_MCHG_MASK                        (3 << 24)
+#       define DISP2_GAP_MCHG(x)                          ((x) << 26)
+#       define DISP2_GAP_MCHG_MASK                        (3 << 26)
+
+#define        CG_ULV_CONTROL                                  0x878
+#define        CG_ULV_PARAMETER                                0x87c
+
+#define        SMC_SCRATCH0                                    0x884
+
+#define        CG_CAC_CTRL                                     0x8b8
+#      define CAC_WINDOW(x)                            ((x) << 0)
+#      define CAC_WINDOW_MASK                          0x00ffffff
+
 #define DMIF_ADDR_CONFIG                               0xBD4
 
 #define DMIF_ADDR_CALC                                 0xC00
 #define                NOOFGROUPS_SHIFT                                12
 #define                NOOFGROUPS_MASK                                 0x00001000
 
+#define        MC_ARB_DRAM_TIMING                              0x2774
+#define        MC_ARB_DRAM_TIMING2                             0x2778
+
+#define MC_ARB_BURST_TIME                               0x2808
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0x1f << 0)
+#define                STATE0_SHIFT                            0
+#define                STATE1(x)                               ((x) << 5)
+#define                STATE1_MASK                             (0x1f << 5)
+#define                STATE1_SHIFT                            5
+#define                STATE2(x)                               ((x) << 10)
+#define                STATE2_MASK                             (0x1f << 10)
+#define                STATE2_SHIFT                            10
+#define                STATE3(x)                               ((x) << 15)
+#define                STATE3_MASK                             (0x1f << 15)
+#define                STATE3_SHIFT                            15
+
 #define        MC_SEQ_TRAIN_WAKEUP_CNTL                        0x2808
 #define                TRAIN_DONE_D0                           (1 << 30)
 #define                TRAIN_DONE_D1                           (1 << 31)
 #define MC_SEQ_SUP_CNTL                                0x28c8
 #define                RUN_MASK                                (1 << 0)
 #define MC_SEQ_SUP_PGM                                 0x28cc
+#define MC_PMG_AUTO_CMD                                0x28d0
 
 #define MC_IO_PAD_CNTL_D0                              0x29d0
 #define                MEM_FALL_OUT_CMD                        (1 << 8)
 
+#define MC_SEQ_RAS_TIMING                               0x28a0
+#define MC_SEQ_CAS_TIMING                               0x28a4
+#define MC_SEQ_MISC_TIMING                              0x28a8
+#define MC_SEQ_MISC_TIMING2                             0x28ac
+#define MC_SEQ_PMG_TIMING                               0x28b0
+#define MC_SEQ_RD_CTL_D0                                0x28b4
+#define MC_SEQ_RD_CTL_D1                                0x28b8
+#define MC_SEQ_WR_CTL_D0                                0x28bc
+#define MC_SEQ_WR_CTL_D1                                0x28c0
+
 #define MC_SEQ_MISC0                                           0x2a00
+#define        MC_SEQ_MISC0_VEN_ID_SHIFT               8
+#define        MC_SEQ_MISC0_VEN_ID_MASK                0x00000f00
+#define        MC_SEQ_MISC0_VEN_ID_VALUE               3
+#define        MC_SEQ_MISC0_REV_ID_SHIFT               12
+#define        MC_SEQ_MISC0_REV_ID_MASK                0x0000f000
+#define        MC_SEQ_MISC0_REV_ID_VALUE               1
+#define        MC_SEQ_MISC0_GDDR5_SHIFT                28
+#define        MC_SEQ_MISC0_GDDR5_MASK                 0xf0000000
+#define        MC_SEQ_MISC0_GDDR5_VALUE                5
+#define MC_SEQ_MISC1                                    0x2a04
+#define MC_SEQ_RESERVE_M                                0x2a08
+#define MC_PMG_CMD_EMRS                                 0x2a0c
 
 #define MC_SEQ_IO_DEBUG_INDEX                          0x2a44
 #define MC_SEQ_IO_DEBUG_DATA                                   0x2a48
 
+#define MC_SEQ_MISC5                                    0x2a54
+#define MC_SEQ_MISC6                                    0x2a58
+
+#define MC_SEQ_MISC7                                    0x2a64
+
+#define MC_SEQ_RAS_TIMING_LP                            0x2a6c
+#define MC_SEQ_CAS_TIMING_LP                            0x2a70
+#define MC_SEQ_MISC_TIMING_LP                           0x2a74
+#define MC_SEQ_MISC_TIMING2_LP                          0x2a78
+#define MC_SEQ_WR_CTL_D0_LP                             0x2a7c
+#define MC_SEQ_WR_CTL_D1_LP                             0x2a80
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0x2a84
+#define MC_SEQ_PMG_CMD_MRS_LP                           0x2a88
+
+#define MC_PMG_CMD_MRS                                  0x2aac
+
+#define MC_SEQ_RD_CTL_D0_LP                             0x2b1c
+#define MC_SEQ_RD_CTL_D1_LP                             0x2b20
+
+#define MC_PMG_CMD_MRS1                                 0x2b44
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0x2b48
+#define MC_SEQ_PMG_TIMING_LP                            0x2b4c
+
+#define MC_SEQ_WR_CTL_2                                 0x2b54
+#define MC_SEQ_WR_CTL_2_LP                              0x2b58
+#define MC_PMG_CMD_MRS2                                 0x2b5c
+#define MC_SEQ_PMG_CMD_MRS2_LP                          0x2b60
+
+#define        MCLK_PWRMGT_CNTL                                0x2ba0
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCK0_PDNB                              (1 << 8)
+#       define MRDCK1_PDNB                              (1 << 9)
+#       define MRDCK0_RESET                             (1 << 16)
+#       define MRDCK1_RESET                             (1 << 17)
+#       define DLL_READY_READ                           (1 << 24)
+#define        DLL_CNTL                                        0x2ba4
+#       define MRDCK0_BYPASS                            (1 << 24)
+#       define MRDCK1_BYPASS                            (1 << 25)
+
+#define        MPLL_FUNC_CNTL                                  0x2bb4
+#define                BWCTRL(x)                               ((x) << 20)
+#define                BWCTRL_MASK                             (0xff << 20)
+#define        MPLL_FUNC_CNTL_1                                0x2bb8
+#define                VCO_MODE(x)                             ((x) << 0)
+#define                VCO_MODE_MASK                           (3 << 0)
+#define                CLKFRAC(x)                              ((x) << 4)
+#define                CLKFRAC_MASK                            (0xfff << 4)
+#define                CLKF(x)                                 ((x) << 16)
+#define                CLKF_MASK                               (0xfff << 16)
+#define        MPLL_FUNC_CNTL_2                                0x2bbc
+#define        MPLL_AD_FUNC_CNTL                               0x2bc0
+#define                YCLK_POST_DIV(x)                        ((x) << 0)
+#define                YCLK_POST_DIV_MASK                      (7 << 0)
+#define        MPLL_DQ_FUNC_CNTL                               0x2bc4
+#define                YCLK_SEL(x)                             ((x) << 4)
+#define                YCLK_SEL_MASK                           (1 << 4)
+
+#define        MPLL_SS1                                        0x2bcc
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0x2bd0
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
 #define        HDP_HOST_PATH_CNTL                              0x2C00
 #define        HDP_NONSURFACE_BASE                             0x2C04
 #define        HDP_NONSURFACE_INFO                             0x2C08
 #       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
 #       define DC_HPDx_EN                                 (1 << 28)
 
+#define DPG_PIPE_STUTTER_CONTROL                          0x6cd4
+#       define STUTTER_ENABLE                             (1 << 0)
+
 /* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */
 #define CRTC_STATUS_FRAME_COUNT                         0x6e98
 
 
 #define        SQC_CACHES                                      0x8C08
 
+#define SQ_POWER_THROTTLE                               0x8e58
+#define                MIN_POWER(x)                            ((x) << 0)
+#define                MIN_POWER_MASK                          (0x3fff << 0)
+#define                MIN_POWER_SHIFT                         0
+#define                MAX_POWER(x)                            ((x) << 16)
+#define                MAX_POWER_MASK                          (0x3fff << 16)
+#define                MAX_POWER_SHIFT                         0
+#define SQ_POWER_THROTTLE2                              0x8e5c
+#define                MAX_POWER_DELTA(x)                      ((x) << 0)
+#define                MAX_POWER_DELTA_MASK                    (0x3fff << 0)
+#define                MAX_POWER_DELTA_SHIFT                   0
+#define                STI_SIZE(x)                             ((x) << 16)
+#define                STI_SIZE_MASK                           (0x3ff << 16)
+#define                STI_SIZE_SHIFT                          16
+#define                LTI_RATIO(x)                            ((x) << 27)
+#define                LTI_RATIO_MASK                          (0xf << 27)
+#define                LTI_RATIO_SHIFT                         27
+
 #define        SX_DEBUG_1                                      0x9060
 
 #define        SPI_STATIC_THREAD_MGMT_1                        0x90E0
diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h
new file mode 100644 (file)
index 0000000..5578e98
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ *
+ */
+#ifndef PP_SISLANDS_SMC_H
+#define PP_SISLANDS_SMC_H
+
+#include "ppsmc.h"
+
+#pragma pack(push, 1)
+
+#define SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+
+struct PP_SIslands_Dpm2PerfLevel
+{
+    uint8_t MaxPS;
+    uint8_t TgtAct;
+    uint8_t MaxPS_StepInc;
+    uint8_t MaxPS_StepDec;
+    uint8_t PSSamplingTime;
+    uint8_t NearTDPDec;
+    uint8_t AboveSafeInc;
+    uint8_t BelowSafeInc;
+    uint8_t PSDeltaLimit;
+    uint8_t PSDeltaWin;
+    uint16_t PwrEfficiencyRatio;
+    uint8_t Reserved[4];
+};
+
+typedef struct PP_SIslands_Dpm2PerfLevel PP_SIslands_Dpm2PerfLevel;
+
+struct PP_SIslands_DPM2Status
+{
+    uint32_t    dpm2Flags;
+    uint8_t     CurrPSkip;
+    uint8_t     CurrPSkipPowerShift;
+    uint8_t     CurrPSkipTDP;
+    uint8_t     CurrPSkipOCP;
+    uint8_t     MaxSPLLIndex;
+    uint8_t     MinSPLLIndex;
+    uint8_t     CurrSPLLIndex;
+    uint8_t     InfSweepMode;
+    uint8_t     InfSweepDir;
+    uint8_t     TDPexceeded;
+    uint8_t     reserved;
+    uint8_t     SwitchDownThreshold;
+    uint32_t    SwitchDownCounter;
+    uint32_t    SysScalingFactor;
+};
+
+typedef struct PP_SIslands_DPM2Status PP_SIslands_DPM2Status;
+
+struct PP_SIslands_DPM2Parameters
+{
+    uint32_t    TDPLimit;
+    uint32_t    NearTDPLimit;
+    uint32_t    SafePowerLimit;
+    uint32_t    PowerBoostLimit;
+    uint32_t    MinLimitDelta;
+};
+typedef struct PP_SIslands_DPM2Parameters PP_SIslands_DPM2Parameters;
+
+struct PP_SIslands_PAPMStatus
+{
+    uint32_t    EstimatedDGPU_T;
+    uint32_t    EstimatedDGPU_P;
+    uint32_t    EstimatedAPU_T;
+    uint32_t    EstimatedAPU_P;
+    uint8_t     dGPU_T_Limit_Exceeded;
+    uint8_t     reserved[3];
+};
+typedef struct PP_SIslands_PAPMStatus PP_SIslands_PAPMStatus;
+
+struct PP_SIslands_PAPMParameters
+{
+    uint32_t    NearTDPLimitTherm;
+    uint32_t    NearTDPLimitPAPM;
+    uint32_t    PlatformPowerLimit;
+    uint32_t    dGPU_T_Limit;
+    uint32_t    dGPU_T_Warning;
+    uint32_t    dGPU_T_Hysteresis;
+};
+typedef struct PP_SIslands_PAPMParameters PP_SIslands_PAPMParameters;
+
+struct SISLANDS_SMC_SCLK_VALUE
+{
+    uint32_t    vCG_SPLL_FUNC_CNTL;
+    uint32_t    vCG_SPLL_FUNC_CNTL_2;
+    uint32_t    vCG_SPLL_FUNC_CNTL_3;
+    uint32_t    vCG_SPLL_FUNC_CNTL_4;
+    uint32_t    vCG_SPLL_SPREAD_SPECTRUM;
+    uint32_t    vCG_SPLL_SPREAD_SPECTRUM_2;
+    uint32_t    sclk_value;
+};
+
+typedef struct SISLANDS_SMC_SCLK_VALUE SISLANDS_SMC_SCLK_VALUE;
+
+struct SISLANDS_SMC_MCLK_VALUE
+{
+    uint32_t    vMPLL_FUNC_CNTL;
+    uint32_t    vMPLL_FUNC_CNTL_1;
+    uint32_t    vMPLL_FUNC_CNTL_2;
+    uint32_t    vMPLL_AD_FUNC_CNTL;
+    uint32_t    vMPLL_DQ_FUNC_CNTL;
+    uint32_t    vMCLK_PWRMGT_CNTL;
+    uint32_t    vDLL_CNTL;
+    uint32_t    vMPLL_SS;
+    uint32_t    vMPLL_SS2;
+    uint32_t    mclk_value;
+};
+
+typedef struct SISLANDS_SMC_MCLK_VALUE SISLANDS_SMC_MCLK_VALUE;
+
+struct SISLANDS_SMC_VOLTAGE_VALUE
+{
+    uint16_t    value;
+    uint8_t     index;
+    uint8_t     phase_settings;
+};
+
+typedef struct SISLANDS_SMC_VOLTAGE_VALUE SISLANDS_SMC_VOLTAGE_VALUE;
+
+struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL
+{
+    uint8_t                     ACIndex;
+    uint8_t                     displayWatermark;
+    uint8_t                     gen2PCIE;
+    uint8_t                     UVDWatermark;
+    uint8_t                     VCEWatermark;
+    uint8_t                     strobeMode;
+    uint8_t                     mcFlags;
+    uint8_t                     padding;
+    uint32_t                    aT;
+    uint32_t                    bSP;
+    SISLANDS_SMC_SCLK_VALUE     sclk;
+    SISLANDS_SMC_MCLK_VALUE     mclk;
+    SISLANDS_SMC_VOLTAGE_VALUE  vddc;
+    SISLANDS_SMC_VOLTAGE_VALUE  mvdd;
+    SISLANDS_SMC_VOLTAGE_VALUE  vddci;
+    SISLANDS_SMC_VOLTAGE_VALUE  std_vddc;
+    uint8_t                     hysteresisUp;
+    uint8_t                     hysteresisDown;
+    uint8_t                     stateFlags;
+    uint8_t                     arbRefreshState;
+    uint32_t                    SQPowerThrottle;
+    uint32_t                    SQPowerThrottle_2;
+    uint32_t                    MaxPoweredUpCU;
+    SISLANDS_SMC_VOLTAGE_VALUE  high_temp_vddc;
+    SISLANDS_SMC_VOLTAGE_VALUE  low_temp_vddc;
+    uint32_t                    reserved[2];
+    PP_SIslands_Dpm2PerfLevel   dpm2;
+};
+
+#define SISLANDS_SMC_STROBE_RATIO    0x0F
+#define SISLANDS_SMC_STROBE_ENABLE   0x10
+
+#define SISLANDS_SMC_MC_EDC_RD_FLAG  0x01
+#define SISLANDS_SMC_MC_EDC_WR_FLAG  0x02
+#define SISLANDS_SMC_MC_RTT_ENABLE   0x04
+#define SISLANDS_SMC_MC_STUTTER_EN   0x08
+#define SISLANDS_SMC_MC_PG_EN        0x10
+
+typedef struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL SISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+
+struct SISLANDS_SMC_SWSTATE
+{
+    uint8_t                             flags;
+    uint8_t                             levelCount;
+    uint8_t                             padding2;
+    uint8_t                             padding3;
+    SISLANDS_SMC_HW_PERFORMANCE_LEVEL   levels[1];
+};
+
+typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
+
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC  0
+#define SISLANDS_SMC_VOLTAGEMASK_MVDD  1
+#define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_MAX   4
+
+struct SISLANDS_SMC_VOLTAGEMASKTABLE
+{
+    uint32_t lowMask[SISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct SISLANDS_SMC_VOLTAGEMASKTABLE SISLANDS_SMC_VOLTAGEMASKTABLE;
+
+#define SISLANDS_MAX_NO_VREG_STEPS 32
+
+struct SISLANDS_SMC_STATETABLE
+{
+    uint8_t                             thermalProtectType;
+    uint8_t                             systemFlags;
+    uint8_t                             maxVDDCIndexInPPTable;
+    uint8_t                             extraFlags;
+    uint32_t                            lowSMIO[SISLANDS_MAX_NO_VREG_STEPS];
+    SISLANDS_SMC_VOLTAGEMASKTABLE       voltageMaskTable;
+    SISLANDS_SMC_VOLTAGEMASKTABLE       phaseMaskTable;
+    PP_SIslands_DPM2Parameters          dpm2Params;
+    SISLANDS_SMC_SWSTATE                initialState;
+    SISLANDS_SMC_SWSTATE                ACPIState;
+    SISLANDS_SMC_SWSTATE                ULVState;
+    SISLANDS_SMC_SWSTATE                driverState;
+    SISLANDS_SMC_HW_PERFORMANCE_LEVEL   dpmLevels[SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+
+typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE;
+
+#define SI_SMC_SOFT_REGISTER_mclk_chg_timeout         0x0
+#define SI_SMC_SOFT_REGISTER_delay_vreg               0xC
+#define SI_SMC_SOFT_REGISTER_delay_acpi               0x28
+#define SI_SMC_SOFT_REGISTER_seq_index                0x5C
+#define SI_SMC_SOFT_REGISTER_mvdd_chg_time            0x60
+#define SI_SMC_SOFT_REGISTER_mclk_switch_lim          0x70
+#define SI_SMC_SOFT_REGISTER_watermark_threshold      0x78
+#define SI_SMC_SOFT_REGISTER_phase_shedding_delay     0x88
+#define SI_SMC_SOFT_REGISTER_ulv_volt_change_delay    0x8C
+#define SI_SMC_SOFT_REGISTER_mc_block_delay           0x98
+#define SI_SMC_SOFT_REGISTER_ticks_per_us             0xA8
+#define SI_SMC_SOFT_REGISTER_crtc_index               0xC4
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min 0xC8
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max 0xCC
+#define SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width  0xF4
+#define SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen   0xFC
+#define SI_SMC_SOFT_REGISTER_vr_hot_gpio              0x100
+
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32
+
+#define SMC_SISLANDS_SCALE_I  7
+#define SMC_SISLANDS_SCALE_R 12
+
+struct PP_SIslands_CacConfig
+{
+    uint16_t   cac_lkge_lut[SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES];
+    uint32_t   lkge_lut_V0;
+    uint32_t   lkge_lut_Vstep;
+    uint32_t   WinTime;
+    uint32_t   R_LL;
+    uint32_t   calculation_repeats;
+    uint32_t   l2numWin_TDP;
+    uint32_t   dc_cac;
+    uint8_t    lts_truncate_n;
+    uint8_t    SHIFT_N;
+    uint8_t    log2_PG_LKG_SCALE;
+    uint8_t    cac_temp;
+    uint32_t   lkge_lut_T0;
+    uint32_t   lkge_lut_Tstep;
+};
+
+typedef struct PP_SIslands_CacConfig PP_SIslands_CacConfig;
+
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE 16
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+
+struct SMC_SIslands_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_SIslands_MCRegisterAddress SMC_SIslands_MCRegisterAddress;
+
+struct SMC_SIslands_MCRegisterSet
+{
+    uint32_t value[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_SIslands_MCRegisterSet SMC_SIslands_MCRegisterSet;
+
+struct SMC_SIslands_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMC_SIslands_MCRegisterAddress      address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+    SMC_SIslands_MCRegisterSet          data[SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMC_SIslands_MCRegisters SMC_SIslands_MCRegisters;
+
+struct SMC_SIslands_MCArbDramTimingRegisterSet
+{
+    uint32_t mc_arb_dram_timing;
+    uint32_t mc_arb_dram_timing2;
+    uint8_t  mc_arb_rfsh_rate;
+    uint8_t  mc_arb_burst_time;
+    uint8_t  padding[2];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisterSet SMC_SIslands_MCArbDramTimingRegisterSet;
+
+struct SMC_SIslands_MCArbDramTimingRegisters
+{
+    uint8_t                                     arb_current;
+    uint8_t                                     reserved[3];
+    SMC_SIslands_MCArbDramTimingRegisterSet     data[16];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisters SMC_SIslands_MCArbDramTimingRegisters;
+
+struct SMC_SISLANDS_SPLL_DIV_TABLE
+{
+    uint32_t    freq[256];
+    uint32_t    ss[256];
+};
+
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK  0x01ffffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK   0xfe000000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT  25
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK   0x000fffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT  0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK   0xfff00000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT  20
+
+typedef struct SMC_SISLANDS_SPLL_DIV_TABLE SMC_SISLANDS_SPLL_DIV_TABLE;
+
+#define SMC_SISLANDS_DTE_MAX_FILTER_STAGES 5
+
+#define SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE 16
+
+struct Smc_SIslands_DTE_Configuration
+{
+    uint32_t tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+    uint32_t R[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+    uint32_t K;
+    uint32_t T0;
+    uint32_t MaxT;
+    uint8_t  WindowSize;
+    uint8_t  Tdep_count;
+    uint8_t  temp_select;
+    uint8_t  DTE_mode;
+    uint8_t  T_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tdep_R[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tthreshold;
+};
+
+typedef struct Smc_SIslands_DTE_Configuration Smc_SIslands_DTE_Configuration;
+
+#define SMC_SISLANDS_DTE_STATUS_FLAG_DTE_ON 1
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x10000
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_version                   0x0
+#define SISLANDS_SMC_FIRMWARE_HEADER_flags                     0x4
+#define SISLANDS_SMC_FIRMWARE_HEADER_softRegisters             0xC
+#define SISLANDS_SMC_FIRMWARE_HEADER_stateTable                0x10
+#define SISLANDS_SMC_FIRMWARE_HEADER_fanTable                  0x14
+#define SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable            0x18
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable           0x24
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x30
+#define SISLANDS_SMC_FIRMWARE_HEADER_spllTable                 0x38
+#define SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration          0x40
+#define SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters            0x48
+
+#pragma pack(pop)
+
+int si_set_smc_sram_address(struct radeon_device *rdev,
+                           u32 smc_address, u32 limit);
+int si_copy_bytes_to_smc(struct radeon_device *rdev,
+                        u32 smc_start_address,
+                        const u8 *src, u32 byte_count, u32 limit);
+void si_start_smc(struct radeon_device *rdev);
+void si_reset_smc(struct radeon_device *rdev);
+int si_program_jump_on_start(struct radeon_device *rdev);
+void si_stop_smc_clock(struct radeon_device *rdev);
+void si_start_smc_clock(struct radeon_device *rdev);
+bool si_is_smc_running(struct radeon_device *rdev);
+PPSMC_Result si_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg);
+PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev);
+int si_load_smc_ucode(struct radeon_device *rdev, u32 limit);
+int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                          u32 *value, u32 limit);
+int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address,
+                           u32 value, u32 limit);
+
+#endif
+