From 47ebda77810d4e489c55cd1c8240d901b61618c4 Mon Sep 17 00:00:00 2001 From: Oleg Chernovskiy Date: Sun, 7 Sep 2014 18:15:54 +0400 Subject: [PATCH] implement fan control override on CIK Implement fan control override, like aticonfig --pplib-cmd 'set fanspeed 0 x' on proprietary driver. Also provide convenient interface for lm-sensors sysfs, expose pwm1, pwm1_enable, pwm1_min, pwm1_max files in hwmon radeon directory (As pointed out by Martin Peres of nouveau) Need someone to review this patch as I'm not very sure about adding things in right places. Signed-off-by: Oleg Chernovskiy --- drivers/gpu/drm/radeon/ci_dpm.c | 58 ++++++++++++++++++++ drivers/gpu/drm/radeon/cik.c | 2 +- drivers/gpu/drm/radeon/cikd.h | 11 ++++ drivers/gpu/drm/radeon/ppsmc.h | 2 + drivers/gpu/drm/radeon/radeon.h | 10 ++++ drivers/gpu/drm/radeon/radeon_asic.c | 7 +++ drivers/gpu/drm/radeon/radeon_asic.h | 5 ++ drivers/gpu/drm/radeon/radeon_pm.c | 100 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/radeon_reg.h | 4 ++ 9 files changed, 197 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index d416bb2..90eec1c 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -4582,6 +4582,64 @@ void ci_dpm_post_set_power_state(struct radeon_device *rdev) ci_update_current_ps(rdev, new_ps); } +u32 ci_get_fan_speed(struct radeon_device *rdev) +{ + u32 speed = FanSpeedValue(RREG32_SMC(FAN_SPEED_CURRENT)); + return speed; +} + +u32 ci_get_fan_speed_max(struct radeon_device *rdev) +{ + u32 max_speed = RREG32_SMC(FAN_PWM_LIMITS) & FAN_SPEED_MASK; + + return max_speed; +} + +int ci_set_fan_speed(struct radeon_device *rdev, u32 value) +{ + u32 tmp; + u32 max_speed = RREG32_SMC(FAN_PWM_LIMITS) & FAN_SPEED_MASK; + + if(value > max_speed) + return -EINVAL; + + tmp = RREG32_SMC(FAN_SPEED_REQUESTED); // get current speed + value = (tmp & ~FAN_SPEED_MASK) | value; // replace speed bits (first 8) + WREG32_SMC(FAN_SPEED_REQUESTED, value); + + // XXX: without this line nothing will happen, unknown for now + WREG32_SMC(FAN_MANUAL_OVERRIDE, FAN_MANUAL_OVERRIDE_ENABLE); + DRM_INFO("Fan control enabled, proceeding...\n"); + return 0; +} + +u32 ci_get_fan_state(struct radeon_device *rdev) +{ + return rdev->config.cik.fan_state; +} + +int ci_set_fan_state(struct radeon_device *rdev, u32 state) +{ + PPSMC_Result smc_result; + if(rdev->config.cik.fan_state == state) // nothing to change + return 0; + + if (!ci_is_smc_running(rdev)) + return -EINVAL; + + DRM_INFO("SMC is running, proceeding...\n"); + if(state & RADEON_FAN_SPEED_TYPE_USER_DEFINED) { + smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_ForceUserFanSpeed); + if (smc_result != PPSMC_Result_OK) + return -EINVAL; + } else { + smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedFanSpeed); + if (smc_result != PPSMC_Result_OK) + return -EINVAL; + } + rdev->config.cik.fan_state = state; + return 0; +} void ci_dpm_setup_asic(struct radeon_device *rdev) { diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index fa95659..181e509 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3600,7 +3600,7 @@ static void cik_gpu_init(struct radeon_device *rdev) mc_shared_chmap = RREG32(MC_SHARED_CHMAP); mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); - + rdev->config.cik.fan_state = 0; rdev->config.cik.num_tile_pipes = rdev->config.cik.max_tile_pipes; rdev->config.cik.mem_max_burst_length_bytes = 256; tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT; diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 0c6e1b5..8913f9a 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -52,6 +52,17 @@ # define UvdBootLevel_MASK 0xff000000 # define UvdBootLevel_SHIFT 24 +/* Fan control */ +#define FAN_PWM_LIMITS 0xc0300068 +#define FAN_SPEED_REQUESTED 0xc0300064 +#define FAN_SPEED_CURRENT 0xc0300008 +# define FanSpeedValue(x) ((x) >> 9) + +#define FAN_MANUAL_OVERRIDE 0xc030006c +#define FAN_MANUAL_OVERRIDE_ENABLE 0x50cb0c00 + +#define FAN_SPEED_MASK 0xff + #define FIRMWARE_FLAGS 0x3F800 # define INTERRUPTS_ENABLED (1 << 0) diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 5670b82..ea96ab6 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -79,6 +79,8 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_DisableCac ((uint8_t)0x54) #define PPSMC_TDPClampingActive ((uint8_t)0x59) #define PPSMC_TDPClampingInactive ((uint8_t)0x5A) +#define PPSMC_MSG_NoForcedFanSpeed ((uint8_t)0x5B) +#define PPSMC_MSG_ForceUserFanSpeed ((uint8_t)0x5C) #define PPSMC_MSG_NoDisplay ((uint8_t)0x5D) #define PPSMC_MSG_HasDisplay ((uint8_t)0x5E) #define PPSMC_MSG_UVDPowerOFF ((uint8_t)0x60) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 5f05b4c..02cdc08 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1918,6 +1918,14 @@ struct radeon_asic { void (*powergate_uvd)(struct radeon_device *rdev, bool gate); void (*enable_bapm)(struct radeon_device *rdev, bool enable); } dpm; + /* fan control */ + struct { + u32 (*get_fan_speed)(struct radeon_device *rdev); + int (*set_fan_speed)(struct radeon_device *rdev, u32 value); + u32 (*get_fan_state)(struct radeon_device *rdev); + int (*set_fan_state)(struct radeon_device *rdev, u32 state); + u32 (*get_fan_speed_max)(struct radeon_device *rdev); + } fan; /* pageflipping */ struct { void (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base); @@ -2116,6 +2124,8 @@ struct cik_asic { uint32_t tile_mode_array[32]; uint32_t macrotile_mode_array[16]; uint32_t active_cus; + + uint32_t fan_state; }; union radeon_asic_config { diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index eeeeabe..2a7c74b 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2092,6 +2092,13 @@ static struct radeon_asic ci_asic = { .vblank_too_short = &ci_dpm_vblank_too_short, .powergate_uvd = &ci_dpm_powergate_uvd, }, + .fan = { + .get_fan_speed = &ci_get_fan_speed, + .set_fan_speed = &ci_set_fan_speed, + .get_fan_state = &ci_get_fan_state, + .set_fan_state = &ci_set_fan_state, + .get_fan_speed_max = &ci_get_fan_speed_max, + }, .pflip = { .page_flip = &evergreen_page_flip, .page_flip_pending = &evergreen_page_flip_pending, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 275a5dc..e2359f0 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -832,6 +832,11 @@ u32 cik_sdma_get_wptr(struct radeon_device *rdev, void cik_sdma_set_wptr(struct radeon_device *rdev, struct radeon_ring *ring); int ci_get_temp(struct radeon_device *rdev); +u32 ci_get_fan_speed(struct radeon_device *rdev); +u32 ci_get_fan_speed_max(struct radeon_device *rdev); +int ci_set_fan_speed(struct radeon_device *rdev, u32 value); +u32 ci_get_fan_state(struct radeon_device *rdev); +int ci_set_fan_state(struct radeon_device *rdev, u32 state); int kv_get_temp(struct radeon_device *rdev); int ci_dpm_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 164898b..8a0fa16 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -598,14 +598,96 @@ static ssize_t radeon_hwmon_show_temp_thresh(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", temp); } +static ssize_t radeon_hwmon_get_pwm1_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + u32 state = rdev->asic->fan.get_fan_state(rdev); + bool enabled = state & RADEON_FAN_SPEED_TYPE_USER_DEFINED; + + return sprintf(buf, "%i\n", enabled); +} + +static ssize_t radeon_hwmon_set_pwm1_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + int err; + int value; + + err = kstrtoint(buf, 10, &value); + if(err) + return err; + + switch(value) { + case 1: // manual + value = RADEON_FAN_SPEED_TYPE_USER_DEFINED; + break; + default: // disable + value = ~RADEON_FAN_SPEED_TYPE_USER_DEFINED; + break; + } + + err = rdev->asic->fan.set_fan_state(rdev, value); + if(err) + return err; + + return count; +} + +static ssize_t radeon_hwmon_get_pwm1_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", 0); +} + +static ssize_t radeon_hwmon_get_pwm1_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + u32 state = rdev->asic->fan.get_fan_speed_max(rdev); + + return sprintf(buf, "%i\n", state); +} + +static ssize_t radeon_hwmon_set_pwm1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + int err; + u32 value; + + err = kstrtou32(buf, 10, &value); + if(err) + return err; + + err = rdev->asic->fan.set_fan_speed(rdev, value); + if(err) + return err; + + return count; +} + +static ssize_t radeon_hwmon_get_pwm1(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct radeon_device *rdev = dev_get_drvdata(dev); + u32 speed = rdev->asic->fan.get_fan_speed(rdev); + + return sprintf(buf, "%i\n", speed); +} + + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, radeon_hwmon_show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, radeon_hwmon_show_temp_thresh, NULL, 1); +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1, radeon_hwmon_set_pwm1, 0); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, radeon_hwmon_get_pwm1_enable, radeon_hwmon_set_pwm1_enable, 0); +static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO, radeon_hwmon_get_pwm1_min, NULL, 0); +static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO, radeon_hwmon_get_pwm1_max, NULL, 0); static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_min.dev_attr.attr, + &sensor_dev_attr_pwm1_max.dev_attr.attr, NULL }; @@ -614,6 +696,7 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); struct radeon_device *rdev = dev_get_drvdata(dev); + umode_t mode = attr->mode; /* Skip limit attributes if DPM is not enabled */ if (rdev->pm.pm_method != PM_METHOD_DPM && @@ -621,7 +704,22 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) return 0; - return attr->mode; + /* Skip fan attributes if fan control is not present */ + if((!rdev->asic->fan.get_fan_state && attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr) || + (!rdev->asic->fan.get_fan_speed_max && attr == &sensor_dev_attr_pwm1_max.dev_attr.attr) || + (!rdev->asic->fan.get_fan_speed_max && attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) + return 0; + + if(attr == &sensor_dev_attr_pwm1.dev_attr.attr) + { + if(!rdev->asic->fan.get_fan_speed) + mode &= ~S_IRUGO; + + if(!rdev->asic->fan.set_fan_speed) + mode &= ~S_IWUSR; + } + + return mode; } static const struct attribute_group hwmon_attrgroup = { diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h index 62d5497..7e6cfb8 100644 --- a/drivers/gpu/drm/radeon/radeon_reg.h +++ b/drivers/gpu/drm/radeon/radeon_reg.h @@ -3722,4 +3722,8 @@ #define RADEON_VLINE_STAT (1 << 12) +#define RADEON_FAN_SPEED_TYPE_PERCENT 0x001 +#define RADEON_FAN_SPEED_TYPE_RPM 0x002 +#define RADEON_FAN_SPEED_TYPE_USER_DEFINED 0x100 + #endif -- 2.1.0