drm/radeon/dpm: add smc fan control for CI (v2)
authorAlex Deucher <alexander.deucher@amd.com>
Mon, 15 Sep 2014 04:15:22 +0000 (00:15 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 20 Nov 2014 18:00:10 +0000 (13:00 -0500)
Enable smc fan control for CI boards.  Should
reduce the fan noise on systems with a higher
default fan profile.

v2: disable by default, add additional fan setup, rpm control

bug:
https://bugs.freedesktop.org/show_bug.cgi?id=73338

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/radeon/ci_dpm.c
drivers/gpu/drm/radeon/ci_dpm.h
drivers/gpu/drm/radeon/cikd.h
drivers/gpu/drm/radeon/ppsmc.h
drivers/gpu/drm/radeon/pptable.h
drivers/gpu/drm/radeon/r600_dpm.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/smu7_discrete.h

index 9dbc52f3c4d1af9d24a273d0666a081c49bdd235..4581d6cf90e856b96e8af103fc6ebe467b9d36d2 100644 (file)
@@ -184,6 +184,9 @@ static int ci_set_overdrive_target_tdp(struct radeon_device *rdev,
                                       u32 target_tdp);
 static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate);
 
+static PPSMC_Result ci_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
+                                                     PPSMC_Msg msg, u32 parameter);
+
 static struct ci_power_info *ci_get_pi(struct radeon_device *rdev)
 {
         struct ci_power_info *pi = rdev->pm.dpm.priv;
@@ -355,6 +358,21 @@ static int ci_populate_dw8(struct radeon_device *rdev)
        return 0;
 }
 
+static int ci_populate_fuzzy_fan(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if ((rdev->pm.dpm.fan.fan_output_sensitivity & (1 << 15)) ||
+           (rdev->pm.dpm.fan.fan_output_sensitivity == 0))
+               rdev->pm.dpm.fan.fan_output_sensitivity =
+                       rdev->pm.dpm.fan.default_fan_output_sensitivity;
+
+       pi->smc_powertune_table.FuzzyFan_PwmSetDelta =
+               cpu_to_be16(rdev->pm.dpm.fan.fan_output_sensitivity);
+
+       return 0;
+}
+
 static int ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(struct radeon_device *rdev)
 {
        struct ci_power_info *pi = ci_get_pi(rdev);
@@ -478,6 +496,9 @@ static int ci_populate_pm_base(struct radeon_device *rdev)
                if (ret)
                        return ret;
                ret = ci_populate_dw8(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_populate_fuzzy_fan(rdev);
                if (ret)
                        return ret;
                ret = ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(rdev);
@@ -859,6 +880,7 @@ static int ci_thermal_enable_alert(struct radeon_device *rdev,
 
        if (enable) {
                thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+               WREG32_SMC(CG_THERMAL_INT, thermal_int);
                rdev->irq.dpm_thermal = false;
                result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Thermal_Cntl_Enable);
                if (result != PPSMC_Result_OK) {
@@ -867,6 +889,7 @@ static int ci_thermal_enable_alert(struct radeon_device *rdev,
                }
        } else {
                thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+               WREG32_SMC(CG_THERMAL_INT, thermal_int);
                rdev->irq.dpm_thermal = true;
                result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Thermal_Cntl_Disable);
                if (result != PPSMC_Result_OK) {
@@ -875,11 +898,324 @@ static int ci_thermal_enable_alert(struct radeon_device *rdev,
                }
        }
 
-       WREG32_SMC(CG_THERMAL_INT, thermal_int);
+       return 0;
+}
+
+static void ci_fan_ctrl_set_static_mode(struct radeon_device *rdev, u32 mode)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+
+       if (pi->fan_ctrl_is_in_default_mode) {
+               tmp = (RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK) >> FDO_PWM_MODE_SHIFT;
+               pi->fan_ctrl_default_mode = tmp;
+               tmp = (RREG32_SMC(CG_FDO_CTRL2) & TMIN_MASK) >> TMIN_SHIFT;
+               pi->t_min = tmp;
+               pi->fan_ctrl_is_in_default_mode = false;
+       }
+
+       tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TMIN_MASK;
+       tmp |= TMIN(0);
+       WREG32_SMC(CG_FDO_CTRL2, tmp);
+
+       tmp = RREG32_SMC(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK;
+       tmp |= FDO_PWM_MODE(mode);
+       WREG32_SMC(CG_FDO_CTRL2, tmp);
+}
+
+static int ci_thermal_setup_fan_table(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       SMU7_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+       u32 duty100;
+       u32 t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+       u16 fdo_min, slope1, slope2;
+       u32 reference_clock, tmp;
+       int ret;
+       u64 tmp64;
+
+       if (!pi->fan_table_start) {
+               rdev->pm.dpm.fan.ucode_fan_control = false;
+               return 0;
+       }
+
+       duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+
+       if (duty100 == 0) {
+               rdev->pm.dpm.fan.ucode_fan_control = false;
+               return 0;
+       }
+
+       tmp64 = (u64)rdev->pm.dpm.fan.pwm_min * duty100;
+       do_div(tmp64, 10000);
+       fdo_min = (u16)tmp64;
+
+       t_diff1 = rdev->pm.dpm.fan.t_med - rdev->pm.dpm.fan.t_min;
+       t_diff2 = rdev->pm.dpm.fan.t_high - rdev->pm.dpm.fan.t_med;
+
+       pwm_diff1 = rdev->pm.dpm.fan.pwm_med - rdev->pm.dpm.fan.pwm_min;
+       pwm_diff2 = rdev->pm.dpm.fan.pwm_high - rdev->pm.dpm.fan.pwm_med;
+
+       slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+       slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+       fan_table.TempMin = cpu_to_be16((50 + rdev->pm.dpm.fan.t_min) / 100);
+       fan_table.TempMed = cpu_to_be16((50 + rdev->pm.dpm.fan.t_med) / 100);
+       fan_table.TempMax = cpu_to_be16((50 + rdev->pm.dpm.fan.t_max) / 100);
+
+       fan_table.Slope1 = cpu_to_be16(slope1);
+       fan_table.Slope2 = cpu_to_be16(slope2);
+
+       fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+       fan_table.HystDown = cpu_to_be16(rdev->pm.dpm.fan.t_hyst);
+
+       fan_table.HystUp = cpu_to_be16(1);
+
+       fan_table.HystSlope = cpu_to_be16(1);
+
+       fan_table.TempRespLim = cpu_to_be16(5);
+
+       reference_clock = radeon_get_xclk(rdev);
+
+       fan_table.RefreshPeriod = cpu_to_be32((rdev->pm.dpm.fan.cycle_delay *
+                                              reference_clock) / 1600);
+
+       fan_table.FdoMax = cpu_to_be16((u16)duty100);
+
+       tmp = (RREG32_SMC(CG_MULT_THERMAL_CTRL) & TEMP_SEL_MASK) >> TEMP_SEL_SHIFT;
+       fan_table.TempSrc = (uint8_t)tmp;
+
+       ret = ci_copy_bytes_to_smc(rdev,
+                                  pi->fan_table_start,
+                                  (u8 *)(&fan_table),
+                                  sizeof(fan_table),
+                                  pi->sram_end);
+
+       if (ret) {
+               DRM_ERROR("Failed to load fan table to the SMC.");
+               rdev->pm.dpm.fan.ucode_fan_control = false;
+       }
+
+       return 0;
+}
+
+static int ci_fan_ctrl_start_smc_fan_control(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result ret;
+
+       if (pi->caps_od_fuzzy_fan_control_support) {
+               ret = ci_send_msg_to_smc_with_parameter(rdev,
+                                                       PPSMC_StartFanControl,
+                                                       FAN_CONTROL_FUZZY);
+               if (ret != PPSMC_Result_OK)
+                       return -EINVAL;
+               ret = ci_send_msg_to_smc_with_parameter(rdev,
+                                                       PPSMC_MSG_SetFanPwmMax,
+                                                       rdev->pm.dpm.fan.default_max_fan_pwm);
+               if (ret != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else {
+               ret = ci_send_msg_to_smc_with_parameter(rdev,
+                                                       PPSMC_StartFanControl,
+                                                       FAN_CONTROL_TABLE);
+               if (ret != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
 
        return 0;
 }
 
+#if 0
+static int ci_fan_ctrl_stop_smc_fan_control(struct radeon_device *rdev)
+{
+       PPSMC_Result ret;
+
+       ret = ci_send_msg_to_smc(rdev, PPSMC_StopFanControl);
+       if (ret == PPSMC_Result_OK)
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static int ci_fan_ctrl_get_fan_speed_percent(struct radeon_device *rdev,
+                                            u32 *speed)
+{
+       u32 duty, duty100;
+       u64 tmp64;
+
+       if (rdev->pm.no_fan)
+               return -ENOENT;
+
+       duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+       duty = (RREG32_SMC(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT;
+
+       if (duty100 == 0)
+               return -EINVAL;
+
+       tmp64 = (u64)duty * 100;
+       do_div(tmp64, duty100);
+       *speed = (u32)tmp64;
+
+       if (*speed > 100)
+               *speed = 100;
+
+       return 0;
+}
+
+static int ci_fan_ctrl_set_fan_speed_percent(struct radeon_device *rdev,
+                                            u32 speed)
+{
+       u32 tmp;
+       u32 duty, duty100;
+       u64 tmp64;
+
+       if (rdev->pm.no_fan)
+               return -ENOENT;
+
+       if (speed > 100)
+               return -EINVAL;
+
+       if (rdev->pm.dpm.fan.ucode_fan_control)
+               ci_fan_ctrl_stop_smc_fan_control(rdev);
+
+       duty100 = (RREG32_SMC(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+
+       if (duty100 == 0)
+               return -EINVAL;
+
+       tmp64 = (u64)speed * duty100;
+       do_div(tmp64, 100);
+       duty = (u32)tmp64;
+
+       tmp = RREG32_SMC(CG_FDO_CTRL0) & ~FDO_STATIC_DUTY_MASK;
+       tmp |= FDO_STATIC_DUTY(duty);
+       WREG32_SMC(CG_FDO_CTRL0, tmp);
+
+       ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC);
+
+       return 0;
+}
+
+static int ci_fan_ctrl_get_fan_speed_rpm(struct radeon_device *rdev,
+                                        u32 *speed)
+{
+       u32 tach_period;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       if (rdev->pm.no_fan)
+               return -ENOENT;
+
+       if (rdev->pm.fan_pulses_per_revolution == 0)
+               return -ENOENT;
+
+       tach_period = (RREG32_SMC(CG_TACH_STATUS) & TACH_PERIOD_MASK) >> TACH_PERIOD_SHIFT;
+       if (tach_period == 0)
+               return -ENOENT;
+
+       *speed = 60 * xclk * 10000 / tach_period;
+
+       return 0;
+}
+
+static int ci_fan_ctrl_set_fan_speed_rpm(struct radeon_device *rdev,
+                                        u32 speed)
+{
+       u32 tach_period, tmp;
+       u32 xclk = radeon_get_xclk(rdev);
+
+       if (rdev->pm.no_fan)
+               return -ENOENT;
+
+       if (rdev->pm.fan_pulses_per_revolution == 0)
+               return -ENOENT;
+
+       if ((speed < rdev->pm.fan_min_rpm) ||
+           (speed > rdev->pm.fan_max_rpm))
+               return -EINVAL;
+
+       if (rdev->pm.dpm.fan.ucode_fan_control)
+               ci_fan_ctrl_stop_smc_fan_control(rdev);
+
+       tach_period = 60 * xclk * 10000 / (8 * speed);
+       tmp = RREG32_SMC(CG_TACH_CTRL) & ~TARGET_PERIOD_MASK;
+       tmp |= TARGET_PERIOD(tach_period);
+       WREG32_SMC(CG_TACH_CTRL, tmp);
+
+       ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC);
+
+       return 0;
+}
+#endif
+
+static void ci_fan_ctrl_set_default_mode(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+
+       if (!pi->fan_ctrl_is_in_default_mode) {
+               tmp = RREG32_SMC(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
+               tmp |= FDO_PWM_MODE(pi->fan_ctrl_default_mode);
+               WREG32_SMC(CG_FDO_CTRL2, tmp);
+
+               tmp = RREG32_SMC(CG_FDO_CTRL2) & TMIN_MASK;
+               tmp |= TMIN(pi->t_min);
+               WREG32_SMC(CG_FDO_CTRL2, tmp);
+               pi->fan_ctrl_is_in_default_mode = true;
+       }
+}
+
+static void ci_thermal_start_smc_fan_control(struct radeon_device *rdev)
+{
+       if (rdev->pm.dpm.fan.ucode_fan_control) {
+               ci_fan_ctrl_start_smc_fan_control(rdev);
+               ci_fan_ctrl_set_static_mode(rdev, FDO_PWM_MODE_STATIC);
+       }
+}
+
+static void ci_thermal_initialize(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       if (rdev->pm.fan_pulses_per_revolution) {
+               tmp = RREG32_SMC(CG_TACH_CTRL) & ~EDGE_PER_REV_MASK;
+               tmp |= EDGE_PER_REV(rdev->pm.fan_pulses_per_revolution -1);
+               WREG32_SMC(CG_TACH_CTRL, tmp);
+       }
+
+       tmp = RREG32_SMC(CG_FDO_CTRL2) & ~TACH_PWM_RESP_RATE_MASK;
+       tmp |= TACH_PWM_RESP_RATE(0x28);
+       WREG32_SMC(CG_FDO_CTRL2, tmp);
+}
+
+static int ci_thermal_start_thermal_controller(struct radeon_device *rdev)
+{
+       int ret;
+
+       ci_thermal_initialize(rdev);
+       ret = ci_thermal_set_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+       if (ret)
+               return ret;
+       ret = ci_thermal_enable_alert(rdev, true);
+       if (ret)
+               return ret;
+       if (rdev->pm.dpm.fan.ucode_fan_control) {
+               ret = ci_thermal_setup_fan_table(rdev);
+               if (ret)
+                       return ret;
+               ci_thermal_start_smc_fan_control(rdev);
+       }
+
+       return 0;
+}
+
+static void ci_thermal_stop_thermal_controller(struct radeon_device *rdev)
+{
+       if (!rdev->pm.no_fan)
+               ci_fan_ctrl_set_default_mode(rdev);
+}
+
 #if 0
 static int ci_read_smc_soft_register(struct radeon_device *rdev,
                                     u16 reg_offset, u32 *value)
@@ -4841,6 +5177,8 @@ int ci_dpm_enable(struct radeon_device *rdev)
 
        ci_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
 
+       ci_thermal_start_thermal_controller(rdev);
+
        ci_update_current_ps(rdev, boot_ps);
 
        return 0;
@@ -4886,6 +5224,8 @@ void ci_dpm_disable(struct radeon_device *rdev)
        if (!ci_is_smc_running(rdev))
                return;
 
+       ci_thermal_stop_thermal_controller(rdev);
+
        if (pi->thermal_protection)
                ci_enable_thermal_protection(rdev, false);
        ci_enable_power_containment(rdev, false);
@@ -5473,6 +5813,9 @@ int ci_dpm_init(struct radeon_device *rdev)
                rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc =
                        rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
 
+       pi->fan_ctrl_is_in_default_mode = true;
+       rdev->pm.dpm.fan.ucode_fan_control = false;
+
        return 0;
 }
 
index 615cb2cacf2c248ea7e58ffef6affa4414443981..bb19fbf3ab8ad8e5decc705e970de7c95f9ec5d2 100644 (file)
@@ -266,6 +266,7 @@ struct ci_power_info {
        bool caps_automatic_dc_transition;
        bool caps_sclk_throttle_low_notification;
        bool caps_dynamic_ac_timing;
+       bool caps_od_fuzzy_fan_control_support;
        /* flags */
        bool thermal_protection;
        bool pcie_performance_request;
@@ -287,6 +288,10 @@ struct ci_power_info {
        struct ci_ps current_ps;
        struct radeon_ps requested_rps;
        struct ci_ps requested_ps;
+       /* fan control */
+       bool fan_ctrl_is_in_default_mode;
+       u32 t_min;
+       u32 fan_ctrl_default_mode;
 };
 
 #define CISLANDS_VOLTAGE_CONTROL_NONE                   0x0
index 068cbb019326660c85ef095c512decdbd441b4b5..e4e88ca8b82eb29c5a4f637c12d8b701a88b4d1c 100644 (file)
 #define                DIG_THERM_DPM(x)                        ((x) << 14)
 #define                DIG_THERM_DPM_MASK                      0x003FC000
 #define                DIG_THERM_DPM_SHIFT                     14
-
+#define        CG_THERMAL_STATUS                               0xC0300008
+#define                FDO_PWM_DUTY(x)                         ((x) << 9)
+#define                FDO_PWM_DUTY_MASK                       (0xff << 9)
+#define                FDO_PWM_DUTY_SHIFT                      9
 #define        CG_THERMAL_INT                                  0xC030000C
 #define                CI_DIG_THERM_INTH(x)                    ((x) << 8)
 #define                CI_DIG_THERM_INTH_MASK                  0x0000FF00
 #define                CI_DIG_THERM_INTL_SHIFT                 16
 #define        THERM_INT_MASK_HIGH                     (1 << 24)
 #define        THERM_INT_MASK_LOW                      (1 << 25)
-
+#define        CG_MULT_THERMAL_CTRL                            0xC0300010
+#define                TEMP_SEL(x)                             ((x) << 20)
+#define                TEMP_SEL_MASK                           (0xff << 20)
+#define                TEMP_SEL_SHIFT                          20
 #define        CG_MULT_THERMAL_STATUS                          0xC0300014
 #define                ASIC_MAX_TEMP(x)                        ((x) << 0)
 #define                ASIC_MAX_TEMP_MASK                      0x000001ff
 #define                CTF_TEMP_MASK                           0x0003fe00
 #define                CTF_TEMP_SHIFT                          9
 
+#define        CG_FDO_CTRL0                                    0xC0300064
+#define                FDO_STATIC_DUTY(x)                      ((x) << 0)
+#define                FDO_STATIC_DUTY_MASK                    0x0000000F
+#define                FDO_STATIC_DUTY_SHIFT                   0
+#define        CG_FDO_CTRL1                                    0xC0300068
+#define                FMAX_DUTY100(x)                         ((x) << 0)
+#define                FMAX_DUTY100_MASK                       0x0000000F
+#define                FMAX_DUTY100_SHIFT                      0
+#define        CG_FDO_CTRL2                                    0xC030006C
+#define                TMIN(x)                                 ((x) << 0)
+#define                TMIN_MASK                               0x0000000F
+#define                TMIN_SHIFT                              0
+#define                FDO_PWM_MODE(x)                         ((x) << 11)
+#define                FDO_PWM_MODE_MASK                       (3 << 11)
+#define                FDO_PWM_MODE_SHIFT                      11
+#define                TACH_PWM_RESP_RATE(x)                   ((x) << 25)
+#define                TACH_PWM_RESP_RATE_MASK                 (0x7f << 25)
+#define                TACH_PWM_RESP_RATE_SHIFT                25
+#define CG_TACH_CTRL                                    0xC0300070
+#       define EDGE_PER_REV(x)                          ((x) << 0)
+#       define EDGE_PER_REV_MASK                        (0x7 << 0)
+#       define EDGE_PER_REV_SHIFT                       0
+#       define TARGET_PERIOD(x)                         ((x) << 3)
+#       define TARGET_PERIOD_MASK                       0xfffffff8
+#       define TARGET_PERIOD_SHIFT                      3
+#define CG_TACH_STATUS                                  0xC0300074
+#       define TACH_PERIOD(x)                           ((x) << 0)
+#       define TACH_PERIOD_MASK                         0xffffffff
+#       define TACH_PERIOD_SHIFT                        0
+
 #define CG_ECLK_CNTL                                    0xC05000AC
 #       define ECLK_DIVIDER_MASK                        0x7f
 #       define ECLK_DIR_CNTL_EN                         (1 << 8)
index 0c4eaa60b6ca662c4f228e728b0d7aaa33d252b6..ff698b05bdf5d25901f63dac69e91589714e15d8 100644 (file)
 #define FDO_MODE_HARDWARE 0
 #define FDO_MODE_PIECE_WISE_LINEAR 1
 
+enum FAN_CONTROL {
+       FAN_CONTROL_FUZZY,
+       FAN_CONTROL_TABLE
+};
+
 #define PPSMC_Result_OK             ((uint8_t)0x01)
 #define PPSMC_Result_Failed         ((uint8_t)0xFF)
 
@@ -155,6 +160,7 @@ typedef uint8_t PPSMC_Result;
 #define PPSMC_MSG_MASTER_DeepSleep_ON         ((uint16_t) 0x18F)
 #define PPSMC_MSG_MASTER_DeepSleep_OFF        ((uint16_t) 0x190)
 #define PPSMC_MSG_Remove_DC_Clamp             ((uint16_t) 0x191)
+#define PPSMC_MSG_SetFanPwmMax                ((uint16_t) 0x19A)
 
 #define PPSMC_MSG_API_GetSclkFrequency        ((uint16_t) 0x200)
 #define PPSMC_MSG_API_GetMclkFrequency        ((uint16_t) 0x201)
index 2d532996c69795cc129cb4ac6c60a4dfc4b70808..4c2eec49dadc93427bd54336daf4ee0d410fdcc4 100644 (file)
@@ -96,6 +96,14 @@ typedef struct _ATOM_PPLIB_FANTABLE2
     USHORT  usTMax;                          // The max temperature
 } ATOM_PPLIB_FANTABLE2;
 
+typedef struct _ATOM_PPLIB_FANTABLE3
+{
+       ATOM_PPLIB_FANTABLE2 basicTable2;
+       UCHAR ucFanControlMode;
+       USHORT usFanPWMMax;
+       USHORT usFanOutputSensitivity;
+} ATOM_PPLIB_FANTABLE3;
+
 typedef struct _ATOM_PPLIB_EXTENDEDHEADER
 {
     USHORT  usSize;
index f6309bd23e0156461f625aa30e8354dbf3a129dd..76c6a17eeb2ddd3923a5ba0b45f7974420e4450d 100644 (file)
@@ -811,6 +811,7 @@ union power_info {
 union fan_info {
        struct _ATOM_PPLIB_FANTABLE fan;
        struct _ATOM_PPLIB_FANTABLE2 fan2;
+       struct _ATOM_PPLIB_FANTABLE3 fan3;
 };
 
 static int r600_parse_clk_voltage_dep_table(struct radeon_clock_voltage_dependency_table *radeon_table,
@@ -900,6 +901,14 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
                        else
                                rdev->pm.dpm.fan.t_max = 10900;
                        rdev->pm.dpm.fan.cycle_delay = 100000;
+                       if (fan_info->fan.ucFanTableFormat >= 3) {
+                               rdev->pm.dpm.fan.control_mode = fan_info->fan3.ucFanControlMode;
+                               rdev->pm.dpm.fan.default_max_fan_pwm =
+                                       le16_to_cpu(fan_info->fan3.usFanPWMMax);
+                               rdev->pm.dpm.fan.default_fan_output_sensitivity = 4836;
+                               rdev->pm.dpm.fan.fan_output_sensitivity =
+                                       le16_to_cpu(fan_info->fan3.usFanOutputSensitivity);
+                       }
                        rdev->pm.dpm.fan.ucode_fan_control = true;
                }
        }
index 1f61ff089c9e07fb1694897a03e286b7dd820f67..5aabbe0a43f5ef7c8398c31842c3e1e307667780 100644 (file)
@@ -1494,6 +1494,10 @@ struct radeon_dpm_fan {
        u8 t_hyst;
        u32 cycle_delay;
        u16 t_max;
+       u8 control_mode;
+       u16 default_max_fan_pwm;
+       u16 default_fan_output_sensitivity;
+       u16 fan_output_sensitivity;
        bool ucode_fan_control;
 };
 
index 82f70c90a9ee9623991fd538bafbaddb5cc1a53c..0b0b404ff0916a9e72f241400c113b63b43d4a0f 100644 (file)
@@ -431,6 +431,31 @@ struct SMU7_Discrete_MCRegisters
 
 typedef struct SMU7_Discrete_MCRegisters SMU7_Discrete_MCRegisters;
 
+struct SMU7_Discrete_FanTable
+{
+       uint16_t FdoMode;
+       int16_t  TempMin;
+       int16_t  TempMed;
+       int16_t  TempMax;
+       int16_t  Slope1;
+       int16_t  Slope2;
+       int16_t  FdoMin;
+       int16_t  HystUp;
+       int16_t  HystDown;
+       int16_t  HystSlope;
+       int16_t  TempRespLim;
+       int16_t  TempCurr;
+       int16_t  SlopeCurr;
+       int16_t  PwmCurr;
+       uint32_t RefreshPeriod;
+       int16_t  FdoMax;
+       uint8_t  TempSrc;
+       int8_t   Padding;
+};
+
+typedef struct SMU7_Discrete_FanTable SMU7_Discrete_FanTable;
+
+
 struct SMU7_Discrete_PmFuses {
   // dw0-dw1
   uint8_t BapmVddCVidHiSidd[8];
@@ -462,7 +487,10 @@ struct SMU7_Discrete_PmFuses {
   uint8_t BapmVddCVidHiSidd2[8];
 
   // dw11-dw12
-  uint32_t Reserved6[2];
+  int16_t FuzzyFan_ErrorSetDelta;
+  int16_t FuzzyFan_ErrorRateSetDelta;
+  int16_t FuzzyFan_PwmSetDelta;
+  uint16_t CalcMeasPowerBlend;
 
   // dw13-dw16
   uint8_t GnbLPML[16];