drm/radeon/dpm: add support for SVI2 voltage for SI
authorAlex Deucher <alexander.deucher@amd.com>
Fri, 6 Jun 2014 22:43:45 +0000 (18:43 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 5 Aug 2014 12:53:21 +0000 (08:53 -0400)
Some newer boards use SVI2 for voltage control rather
than GPIO.

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_atombios.c
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/radeon/si_dpm.h
drivers/gpu/drm/radeon/sislands_smc.h

index 60c47f8291222369f7f6070953eea7bda1a25ce9..d1592a0d602b05bc744a661c24856b9145f19c9f 100644 (file)
@@ -317,6 +317,9 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
                                  struct atom_voltage_table *voltage_table);
 bool radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
                                 u8 voltage_type, u8 voltage_mode);
+int radeon_atom_get_svi2_info(struct radeon_device *rdev,
+                             u8 voltage_type,
+                             u8 *svd_gpio_id, u8 *svc_gpio_id);
 void radeon_atom_update_memory_dll(struct radeon_device *rdev,
                                   u32 mem_clock);
 void radeon_atom_set_ac_timing(struct radeon_device *rdev,
index 173f378428a96d477eddf937e7bdd3d46fd222a6..fda1e4130af73871b5d8e3a691d353a7a5f25d87 100644 (file)
@@ -3397,6 +3397,50 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
        return false;
 }
 
+int radeon_atom_get_svi2_info(struct radeon_device *rdev,
+                             u8 voltage_type,
+                             u8 *svd_gpio_id, u8 *svc_gpio_id)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (rdev->mode_info.atom_context->bios + data_offset);
+
+               switch (frev) {
+               case 3:
+                       switch (crev) {
+                       case 1:
+                               voltage_object = (union voltage_object *)
+                                       atom_lookup_voltage_object_v3(&voltage_info->v3,
+                                                                     voltage_type,
+                                                                     VOLTAGE_OBJ_SVID2);
+                               if (voltage_object) {
+                                       *svd_gpio_id = voltage_object->v3.asSVID2Obj.ucSVDGpioId;
+                                       *svc_gpio_id = voltage_object->v3.asSVID2Obj.ucSVCGpioId;
+                               } else {
+                                       return -EINVAL;
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("unknown voltage object table\n");
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return -EINVAL;
+               }
+
+       }
+       return 0;
+}
+
 int radeon_atom_get_max_voltage(struct radeon_device *rdev,
                                u8 voltage_type, u16 *max_voltage)
 {
index 58918868f894572fe9fb729023d7b33eb698e2f4..70e61ffeace245aca431cc13c4fa5f833b0580de 100644 (file)
@@ -3812,6 +3812,27 @@ void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
        voltage_table->count = max_voltage_steps;
 }
 
+static int si_get_svi2_voltage_table(struct radeon_device *rdev,
+                                    struct radeon_clock_voltage_dependency_table *voltage_dependency_table,
+                                    struct atom_voltage_table *voltage_table)
+{
+       u32 i;
+
+       if (voltage_dependency_table == NULL)
+               return -EINVAL;
+
+       voltage_table->mask_low = 0;
+       voltage_table->phase_delay = 0;
+
+       voltage_table->count = voltage_dependency_table->count;
+       for (i = 0; i < voltage_table->count; i++) {
+               voltage_table->entries[i].value = voltage_dependency_table->entries[i].v;
+               voltage_table->entries[i].smio_low = 0;
+       }
+
+       return 0;
+}
+
 static int si_construct_voltage_tables(struct radeon_device *rdev)
 {
        struct rv7xx_power_info *pi = rv770_get_pi(rdev);
@@ -3819,15 +3840,25 @@ static int si_construct_voltage_tables(struct radeon_device *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 (pi->voltage_control) {
+               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,
-                                                        SISLANDS_MAX_NO_VREG_STEPS,
-                                                        &eg_pi->vddc_voltage_table);
+               if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(rdev,
+                                                                SISLANDS_MAX_NO_VREG_STEPS,
+                                                                &eg_pi->vddc_voltage_table);
+       } else if (si_pi->voltage_control_svi2) {
+               ret = si_get_svi2_voltage_table(rdev,
+                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                               &eg_pi->vddc_voltage_table);
+               if (ret)
+                       return ret;
+       } else {
+               return -EINVAL;
+       }
 
        if (eg_pi->vddci_control) {
                ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDCI,
@@ -3840,6 +3871,13 @@ static int si_construct_voltage_tables(struct radeon_device *rdev)
                                                                 SISLANDS_MAX_NO_VREG_STEPS,
                                                                 &eg_pi->vddci_voltage_table);
        }
+       if (si_pi->vddci_control_svi2) {
+               ret = si_get_svi2_voltage_table(rdev,
+                                               &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                               &eg_pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+       }
 
        if (pi->mvdd_control) {
                ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_MVDDC,
@@ -3893,46 +3931,55 @@ static int si_populate_smc_voltage_tables(struct radeon_device *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 (si_pi->voltage_control_svi2) {
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc,
+                       si_pi->svc_gpio_id);
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd,
+                       si_pi->svd_gpio_id);
+               si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_svi_rework_plat_type,
+                                          2);
+       } else {
+               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);
+               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);
-       }
+                       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);
+               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);
-       }
+                       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);
+               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);
+                               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;
+                               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;
+                       }
                }
        }
 
@@ -5798,16 +5845,17 @@ 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 si_power_info *si_pi = si_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)
+       if (pi->voltage_control || si_pi->voltage_control_svi2)
                si_enable_voltage_control(rdev, true);
        if (pi->mvdd_control)
                si_get_mvdd_configuration(rdev);
-       if (pi->voltage_control) {
+       if (pi->voltage_control || si_pi->voltage_control_svi2) {
                ret = si_construct_voltage_tables(rdev);
                if (ret) {
                        DRM_ERROR("si_construct_voltage_tables failed\n");
@@ -6406,16 +6454,32 @@ int si_dpm_init(struct radeon_device *rdev)
        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);
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                           VOLTAGE_OBJ_GPIO_LUT);
+       if (!pi->voltage_control) {
+               si_pi->voltage_control_svi2 =
+                       radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                   VOLTAGE_OBJ_SVID2);
+               if (si_pi->voltage_control_svi2)
+                       radeon_atom_get_svi2_info(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                 &si_pi->svd_gpio_id, &si_pi->svc_gpio_id);
+       }
 
        pi->mvdd_control =
-               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, VOLTAGE_OBJ_GPIO_LUT);
+               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);
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+                                           VOLTAGE_OBJ_GPIO_LUT);
+       if (!eg_pi->vddci_control)
+               si_pi->vddci_control_svi2 =
+                       radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+                                                   VOLTAGE_OBJ_SVID2);
 
        si_pi->vddc_phase_shed_control =
-               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_PHASE_LUT);
+               radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                           VOLTAGE_OBJ_PHASE_LUT);
 
        rv770_get_engine_memory_ss(rdev);
 
index 4ce5032cdf49ecbf2dc27dca6c7b19e7f315714a..8b5c06a0832df96bfc45f876398529c1f7151c10 100644 (file)
@@ -170,6 +170,8 @@ struct si_power_info {
        bool vddc_phase_shed_control;
        bool pspp_notify_required;
        bool sclk_deep_sleep_above_low;
+       bool voltage_control_svi2;
+       bool vddci_control_svi2;
        /* smc offsets */
        u32 sram_end;
        u32 state_table_start;
@@ -192,6 +194,9 @@ struct si_power_info {
        SMC_SIslands_MCRegisters smc_mc_reg_table;
        SISLANDS_SMC_STATETABLE smc_statetable;
        PP_SIslands_PAPMParameters papm_parm;
+       /* SVI2 */
+       u8 svd_gpio_id;
+       u8 svc_gpio_id;
 };
 
 #define SISLANDS_INITIAL_STATE_ARB_INDEX    0
index 10e945a49479e3e9bb611ec98cb62b409046acf6..623a0b1e2d9dbf017b7af5218996daabc9ec2ab6 100644 (file)
@@ -241,6 +241,9 @@ typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE;
 #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 SI_SMC_SOFT_REGISTER_svi_rework_plat_type     0x118
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd   0x11c
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc   0x120
 
 #define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
 #define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32