Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: IPS driver: Fix limit clamping when reducing CPU power [PATCH 2/2] IPS driver: disable CPU turbo IPS driver: apply BIOS provided CPU limit if different from default intel_ips -- ensure we do not enable gpu turbo mode without driver linkage intel_ips: Print MCP limit exceeded values. IPS driver: verify BIOS provided limits IPS driver: don't toggle CPU turbo on unsupported CPUs NULL pointer might be used in ips_monitor() Release symbol on error-handling path of ips_get_i915_syms() old_cpu_power is wrongly divided by 65535 in ips_monitor() seqno mask of THM_ITV register is 16bit
This commit is contained in:
commit
50c6dc9eba
|
@ -51,7 +51,6 @@
|
|||
* TODO:
|
||||
* - handle CPU hotplug
|
||||
* - provide turbo enable/disable api
|
||||
* - make sure we can write turbo enable/disable reg based on MISC_EN
|
||||
*
|
||||
* Related documents:
|
||||
* - CDI 403777, 403778 - Auburndale EDS vol 1 & 2
|
||||
|
@ -230,7 +229,7 @@
|
|||
#define THM_TC2 0xac
|
||||
#define THM_DTV 0xb0
|
||||
#define THM_ITV 0xd8
|
||||
#define ITV_ME_SEQNO_MASK 0x000f0000 /* ME should update every ~200ms */
|
||||
#define ITV_ME_SEQNO_MASK 0x00ff0000 /* ME should update every ~200ms */
|
||||
#define ITV_ME_SEQNO_SHIFT (16)
|
||||
#define ITV_MCH_TEMP_MASK 0x0000ff00
|
||||
#define ITV_MCH_TEMP_SHIFT (8)
|
||||
|
@ -325,6 +324,7 @@ struct ips_driver {
|
|||
bool gpu_preferred;
|
||||
bool poll_turbo_status;
|
||||
bool second_cpu;
|
||||
bool turbo_toggle_allowed;
|
||||
struct ips_mcp_limits *limits;
|
||||
|
||||
/* Optional MCH interfaces for if i915 is in use */
|
||||
|
@ -415,7 +415,7 @@ static void ips_cpu_lower(struct ips_driver *ips)
|
|||
new_limit = cur_limit - 8; /* 1W decrease */
|
||||
|
||||
/* Clamp to SKU TDP limit */
|
||||
if (((new_limit * 10) / 8) < (ips->orig_turbo_limit & TURBO_TDP_MASK))
|
||||
if (new_limit < (ips->orig_turbo_limit & TURBO_TDP_MASK))
|
||||
new_limit = ips->orig_turbo_limit & TURBO_TDP_MASK;
|
||||
|
||||
thm_writew(THM_MPCPC, (new_limit * 10) / 8);
|
||||
|
@ -461,7 +461,8 @@ static void ips_enable_cpu_turbo(struct ips_driver *ips)
|
|||
if (ips->__cpu_turbo_on)
|
||||
return;
|
||||
|
||||
on_each_cpu(do_enable_cpu_turbo, ips, 1);
|
||||
if (ips->turbo_toggle_allowed)
|
||||
on_each_cpu(do_enable_cpu_turbo, ips, 1);
|
||||
|
||||
ips->__cpu_turbo_on = true;
|
||||
}
|
||||
|
@ -498,7 +499,8 @@ static void ips_disable_cpu_turbo(struct ips_driver *ips)
|
|||
if (!ips->__cpu_turbo_on)
|
||||
return;
|
||||
|
||||
on_each_cpu(do_disable_cpu_turbo, ips, 1);
|
||||
if (ips->turbo_toggle_allowed)
|
||||
on_each_cpu(do_disable_cpu_turbo, ips, 1);
|
||||
|
||||
ips->__cpu_turbo_on = false;
|
||||
}
|
||||
|
@ -598,17 +600,29 @@ static bool mcp_exceeded(struct ips_driver *ips)
|
|||
{
|
||||
unsigned long flags;
|
||||
bool ret = false;
|
||||
u32 temp_limit;
|
||||
u32 avg_power;
|
||||
const char *msg = "MCP limit exceeded: ";
|
||||
|
||||
spin_lock_irqsave(&ips->turbo_status_lock, flags);
|
||||
if (ips->mcp_avg_temp > (ips->mcp_temp_limit * 100))
|
||||
ret = true;
|
||||
if (ips->cpu_avg_power + ips->mch_avg_power > ips->mcp_power_limit)
|
||||
ret = true;
|
||||
spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
|
||||
|
||||
if (ret)
|
||||
temp_limit = ips->mcp_temp_limit * 100;
|
||||
if (ips->mcp_avg_temp > temp_limit) {
|
||||
dev_info(&ips->dev->dev,
|
||||
"MCP power or thermal limit exceeded\n");
|
||||
"%sAvg temp %u, limit %u\n", msg, ips->mcp_avg_temp,
|
||||
temp_limit);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
avg_power = ips->cpu_avg_power + ips->mch_avg_power;
|
||||
if (avg_power > ips->mcp_power_limit) {
|
||||
dev_info(&ips->dev->dev,
|
||||
"%sAvg power %u, limit %u\n", msg, avg_power,
|
||||
ips->mcp_power_limit);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -662,6 +676,27 @@ static bool mch_exceeded(struct ips_driver *ips)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* verify_limits - verify BIOS provided limits
|
||||
* @ips: IPS structure
|
||||
*
|
||||
* BIOS can optionally provide non-default limits for power and temp. Check
|
||||
* them here and use the defaults if the BIOS values are not provided or
|
||||
* are otherwise unusable.
|
||||
*/
|
||||
static void verify_limits(struct ips_driver *ips)
|
||||
{
|
||||
if (ips->mcp_power_limit < ips->limits->mcp_power_limit ||
|
||||
ips->mcp_power_limit > 35000)
|
||||
ips->mcp_power_limit = ips->limits->mcp_power_limit;
|
||||
|
||||
if (ips->mcp_temp_limit < ips->limits->core_temp_limit ||
|
||||
ips->mcp_temp_limit < ips->limits->mch_temp_limit ||
|
||||
ips->mcp_temp_limit > 150)
|
||||
ips->mcp_temp_limit = min(ips->limits->core_temp_limit,
|
||||
ips->limits->mch_temp_limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* update_turbo_limits - get various limits & settings from regs
|
||||
* @ips: IPS driver struct
|
||||
|
@ -680,12 +715,21 @@ static void update_turbo_limits(struct ips_driver *ips)
|
|||
u32 hts = thm_readl(THM_HTS);
|
||||
|
||||
ips->cpu_turbo_enabled = !(hts & HTS_PCTD_DIS);
|
||||
ips->gpu_turbo_enabled = !(hts & HTS_GTD_DIS);
|
||||
/*
|
||||
* Disable turbo for now, until we can figure out why the power figures
|
||||
* are wrong
|
||||
*/
|
||||
ips->cpu_turbo_enabled = false;
|
||||
|
||||
if (ips->gpu_busy)
|
||||
ips->gpu_turbo_enabled = !(hts & HTS_GTD_DIS);
|
||||
|
||||
ips->core_power_limit = thm_readw(THM_MPCPC);
|
||||
ips->mch_power_limit = thm_readw(THM_MMGPC);
|
||||
ips->mcp_temp_limit = thm_readw(THM_PTL);
|
||||
ips->mcp_power_limit = thm_readw(THM_MPPC);
|
||||
|
||||
verify_limits(ips);
|
||||
/* Ignore BIOS CPU vs GPU pref */
|
||||
}
|
||||
|
||||
|
@ -858,7 +902,7 @@ static u32 get_cpu_power(struct ips_driver *ips, u32 *last, int period)
|
|||
ret = (ret * 1000) / 65535;
|
||||
*last = val;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u16 temp_decay_factor = 2;
|
||||
|
@ -940,7 +984,6 @@ static int ips_monitor(void *data)
|
|||
kfree(mch_samples);
|
||||
kfree(cpu_samples);
|
||||
kfree(mchp_samples);
|
||||
kthread_stop(ips->adjust);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -948,7 +991,7 @@ static int ips_monitor(void *data)
|
|||
ITV_ME_SEQNO_SHIFT;
|
||||
seqno_timestamp = get_jiffies_64();
|
||||
|
||||
old_cpu_power = thm_readl(THM_CEC) / 65535;
|
||||
old_cpu_power = thm_readl(THM_CEC);
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD));
|
||||
|
||||
/* Collect an initial average */
|
||||
|
@ -1150,11 +1193,18 @@ static irqreturn_t ips_irq_handler(int irq, void *arg)
|
|||
STS_GPL_SHIFT;
|
||||
/* ignore EC CPU vs GPU pref */
|
||||
ips->cpu_turbo_enabled = !(sts & STS_PCTD_DIS);
|
||||
ips->gpu_turbo_enabled = !(sts & STS_GTD_DIS);
|
||||
/*
|
||||
* Disable turbo for now, until we can figure
|
||||
* out why the power figures are wrong
|
||||
*/
|
||||
ips->cpu_turbo_enabled = false;
|
||||
if (ips->gpu_busy)
|
||||
ips->gpu_turbo_enabled = !(sts & STS_GTD_DIS);
|
||||
ips->mcp_temp_limit = (sts & STS_PTL_MASK) >>
|
||||
STS_PTL_SHIFT;
|
||||
ips->mcp_power_limit = (tc1 & STS_PPL_MASK) >>
|
||||
STS_PPL_SHIFT;
|
||||
verify_limits(ips);
|
||||
spin_unlock(&ips->turbo_status_lock);
|
||||
|
||||
thm_writeb(THM_SEC, SEC_ACK);
|
||||
|
@ -1333,8 +1383,10 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
|
|||
* turbo manually or we'll get an illegal MSR access, even though
|
||||
* turbo will still be available.
|
||||
*/
|
||||
if (!(misc_en & IA32_MISC_TURBO_EN))
|
||||
; /* add turbo MSR write allowed flag if necessary */
|
||||
if (misc_en & IA32_MISC_TURBO_EN)
|
||||
ips->turbo_toggle_allowed = true;
|
||||
else
|
||||
ips->turbo_toggle_allowed = false;
|
||||
|
||||
if (strstr(boot_cpu_data.x86_model_id, "CPU M"))
|
||||
limits = &ips_sv_limits;
|
||||
|
@ -1351,9 +1403,10 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
|
|||
tdp = turbo_power & TURBO_TDP_MASK;
|
||||
|
||||
/* Sanity check TDP against CPU */
|
||||
if (limits->mcp_power_limit != (tdp / 8) * 1000) {
|
||||
dev_warn(&ips->dev->dev, "Warning: CPU TDP doesn't match expected value (found %d, expected %d)\n",
|
||||
tdp / 8, limits->mcp_power_limit / 1000);
|
||||
if (limits->core_power_limit != (tdp / 8) * 1000) {
|
||||
dev_info(&ips->dev->dev, "CPU TDP doesn't match expected value (found %d, expected %d)\n",
|
||||
tdp / 8, limits->core_power_limit / 1000);
|
||||
limits->core_power_limit = (tdp / 8) * 1000;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -1390,7 +1443,7 @@ static bool ips_get_i915_syms(struct ips_driver *ips)
|
|||
return true;
|
||||
|
||||
out_put_busy:
|
||||
symbol_put(i915_gpu_turbo_disable);
|
||||
symbol_put(i915_gpu_busy);
|
||||
out_put_lower:
|
||||
symbol_put(i915_gpu_lower);
|
||||
out_put_raise:
|
||||
|
@ -1532,23 +1585,28 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
/* Save turbo limits & ratios */
|
||||
rdmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit);
|
||||
|
||||
ips_enable_cpu_turbo(ips);
|
||||
ips->cpu_turbo_enabled = true;
|
||||
|
||||
/* Set up the work queue and monitor/adjust threads */
|
||||
ips->monitor = kthread_run(ips_monitor, ips, "ips-monitor");
|
||||
if (IS_ERR(ips->monitor)) {
|
||||
dev_err(&dev->dev,
|
||||
"failed to create thermal monitor thread, aborting\n");
|
||||
ret = -ENOMEM;
|
||||
goto error_free_irq;
|
||||
}
|
||||
ips_disable_cpu_turbo(ips);
|
||||
ips->cpu_turbo_enabled = false;
|
||||
|
||||
/* Create thermal adjust thread */
|
||||
ips->adjust = kthread_create(ips_adjust, ips, "ips-adjust");
|
||||
if (IS_ERR(ips->adjust)) {
|
||||
dev_err(&dev->dev,
|
||||
"failed to create thermal adjust thread, aborting\n");
|
||||
ret = -ENOMEM;
|
||||
goto error_free_irq;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the work queue and monitor thread. The monitor thread
|
||||
* will wake up ips_adjust thread.
|
||||
*/
|
||||
ips->monitor = kthread_run(ips_monitor, ips, "ips-monitor");
|
||||
if (IS_ERR(ips->monitor)) {
|
||||
dev_err(&dev->dev,
|
||||
"failed to create thermal monitor thread, aborting\n");
|
||||
ret = -ENOMEM;
|
||||
goto error_thread_cleanup;
|
||||
}
|
||||
|
||||
|
@ -1566,7 +1624,7 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
return ret;
|
||||
|
||||
error_thread_cleanup:
|
||||
kthread_stop(ips->monitor);
|
||||
kthread_stop(ips->adjust);
|
||||
error_free_irq:
|
||||
free_irq(ips->dev->irq, ips);
|
||||
error_unmap:
|
||||
|
|
Loading…
Reference in New Issue