ACPI and power management fixes for 3.8-rc5

* Two cpuidle initialization fixes from Konrad Rzeszutek Wilk.
 
 * cpufreq regression fixes for AMD processors from Borislav Petkov,
   Stefan Bader, and Matthew Garrett.
 
 * ACPI cpufreq fix from Thomas Schlichter.
 
 * cpufreq and devfreq fixes related to incorrect usage of operating
   performance points (OPP) framework and RCU from Nishanth Menon.
 
 * APEI workaround for incorrect BIOS information from Lans Zhang.
 
 /
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.19 (GNU/Linux)
 
 iQIcBAABAgAGBQJRASzdAAoJEKhOf7ml8uNsFiEP/RTaeCDutxTHNhcYRIx6MXcU
 rFtUbWnSLxGbMjtxgQaPRFihMxmOC775609433v+T/DDvyNoqGqUdN7y+P5tBUFh
 TXxV/1hmsjAyeYITq1dhCci8ESrdOmESA+i4V/wRPFqKPkQy5yrnvhqfMejrVXy7
 CaYrglcALO5tmfwQ2qKcOLXYQE52L2WW1PenvxIZWkcI6ck9kKYg63Roswbek3K9
 gMzx/QSlFbzrOcJfDlcPsHqCUlsvh5QV3TfGObYOejZe/BgZndKrJgLJ+9uEUmof
 J5wNlF2cxlQ0ggFidX53eoynAiAGC/kdXA7UOzGVbTpEVgt1r3KdlmRqfF6V3tpf
 N85GnMk7QBNK66sQM48i2Q+wwOkFj0Pq9LJ8KpT9MtoirzBHDXcLK9CgsL/JCqnV
 /cmpXOiLoDpG+kmp4Fr3MDU55FqFkvLDpvPkIdZ3zBXRGv+DUvAZjl4TGeBMt2WL
 LhU812RrRRBrqxo/mhRnSgIv6feQlrjCdDskC/Ra8NJrghyGpoEppe3JOvwdH8RF
 suuRkwvE/Y81xi28WLvIp5Jth4/svhFPoXWeAXcV/apbvi9a6TB97losAiI9gFdH
 NONT+/v4RjKrKZXS/zSqNrttG4dhey/Y18FgIAXLfwz7aUmE5XdcX0wQw0Ev8uBv
 ESASwLtkH2BlkTVPyec5
 =EEk8
 -----END PGP SIGNATURE-----

Merge tag 'pm+acpi-for-3.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI and power management fixes from Rafael Wysocki:

 - Two cpuidle initialization fixes from Konrad Rzeszutek Wilk.

 - cpufreq regression fixes for AMD processors from Borislav Petkov,
   Stefan Bader, and Matthew Garrett.

 - ACPI cpufreq fix from Thomas Schlichter.

 - cpufreq and devfreq fixes related to incorrect usage of operating
   performance points (OPP) framework and RCU from Nishanth Menon.

 - APEI workaround for incorrect BIOS information from Lans Zhang.

* tag 'pm+acpi-for-3.8-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  cpufreq: Add module aliases for acpi-cpufreq
  ACPI: Check MSR valid bit before using P-state frequencies
  PM / devfreq: exynos4_bus: honor RCU lock usage
  PM / devfreq: add locking documentation for recommended_opp
  cpufreq: cpufreq-cpu0: use RCU locks around usage of OPP
  cpufreq: OMAP: use RCU locks around usage of OPP
  ACPI, APEI: Fixup incorrect 64-bit access width firmware bug
  ACPI / processor: Get power info before updating the C-states
  powernow-k8: Add a kconfig dependency on acpi-cpufreq
  ACPI / cpuidle: Fix NULL pointer issues when cpuidle is disabled
  intel_idle: Don't register CPU notifier if we are not running.
This commit is contained in:
Linus Torvalds 2013-01-24 10:19:13 -08:00
commit ba2ab41f3d
10 changed files with 103 additions and 30 deletions

View File

@ -590,6 +590,9 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 &&
*access_bit_width < 32)
*access_bit_width = 32;
else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 &&
*access_bit_width < 64)
*access_bit_width = 64;
if ((bit_width + bit_offset) > *access_bit_width) {
pr_warning(FW_BUG APEI_PFX

View File

@ -958,6 +958,9 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr)
return -EINVAL;
}
if (!dev)
return -EINVAL;
dev->cpu = pr->id;
if (max_cstate == 0)
@ -1149,6 +1152,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
}
/* Populate Updated C-state information */
acpi_processor_get_power_info(pr);
acpi_processor_setup_cpuidle_states(pr);
/* Enable all cpuidle devices */

View File

@ -340,6 +340,13 @@ static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10)
|| boot_cpu_data.x86 == 0x11) {
rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi);
/*
* MSR C001_0064+:
* Bit 63: PstateEn. Read-write. If set, the P-state is valid.
*/
if (!(hi & BIT(31)))
return;
fid = lo & 0x3f;
did = (lo >> 6) & 7;
if (boot_cpu_data.x86 == 0x10)

View File

@ -106,7 +106,7 @@ config X86_POWERNOW_K7_ACPI
config X86_POWERNOW_K8
tristate "AMD Opteron/Athlon64 PowerNow!"
select CPU_FREQ_TABLE
depends on ACPI && ACPI_PROCESSOR
depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ
help
This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors.
Support for K10 and newer processors is now in acpi-cpufreq.

View File

@ -1030,4 +1030,11 @@ MODULE_PARM_DESC(acpi_pstate_strict,
late_initcall(acpi_cpufreq_init);
module_exit(acpi_cpufreq_exit);
static const struct x86_cpu_id acpi_cpufreq_ids[] = {
X86_FEATURE_MATCH(X86_FEATURE_ACPI),
X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE),
{}
};
MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids);
MODULE_ALIAS("acpi");

View File

@ -71,12 +71,15 @@ static int cpu0_set_target(struct cpufreq_policy *policy,
}
if (cpu_reg) {
rcu_read_lock();
opp = opp_find_freq_ceil(cpu_dev, &freq_Hz);
if (IS_ERR(opp)) {
rcu_read_unlock();
pr_err("failed to find OPP for %ld\n", freq_Hz);
return PTR_ERR(opp);
}
volt = opp_get_voltage(opp);
rcu_read_unlock();
tol = volt * voltage_tolerance / 100;
volt_old = regulator_get_voltage(cpu_reg);
}
@ -236,12 +239,14 @@ static int cpu0_cpufreq_driver_init(void)
*/
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
;
rcu_read_lock();
opp = opp_find_freq_exact(cpu_dev,
freq_table[0].frequency * 1000, true);
min_uV = opp_get_voltage(opp);
opp = opp_find_freq_exact(cpu_dev,
freq_table[i-1].frequency * 1000, true);
max_uV = opp_get_voltage(opp);
rcu_read_unlock();
ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
if (ret > 0)
transition_latency += ret * 1000;

View File

@ -110,13 +110,16 @@ static int omap_target(struct cpufreq_policy *policy,
freq = ret;
if (mpu_reg) {
rcu_read_lock();
opp = opp_find_freq_ceil(mpu_dev, &freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
__func__, freqs.new);
return -EINVAL;
}
volt = opp_get_voltage(opp);
rcu_read_unlock();
tol = volt * OPP_TOLERANCE / 100;
volt_old = regulator_get_voltage(mpu_reg);
}

View File

@ -994,6 +994,11 @@ module_exit(devfreq_exit);
* @freq: The frequency given to target function
* @flags: Flags handed from devfreq framework.
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. The reason for the same is that the opp pointer which is
* returned will remain valid for use with opp_get_{voltage, freq} only while
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
*/
struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
u32 flags)

View File

@ -73,6 +73,16 @@ enum busclk_level_idx {
#define EX4210_LV_NUM (LV_2 + 1)
#define EX4x12_LV_NUM (LV_4 + 1)
/**
* struct busfreq_opp_info - opp information for bus
* @rate: Frequency in hertz
* @volt: Voltage in microvolts corresponding to this OPP
*/
struct busfreq_opp_info {
unsigned long rate;
unsigned long volt;
};
struct busfreq_data {
enum exynos4_busf_type type;
struct device *dev;
@ -80,7 +90,7 @@ struct busfreq_data {
bool disabled;
struct regulator *vdd_int;
struct regulator *vdd_mif; /* Exynos4412/4212 only */
struct opp *curr_opp;
struct busfreq_opp_info curr_oppinfo;
struct exynos4_ppmu dmc[2];
struct notifier_block pm_notifier;
@ -296,13 +306,14 @@ static unsigned int exynos4x12_clkdiv_sclkip[][3] = {
};
static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp)
static int exynos4210_set_busclk(struct busfreq_data *data,
struct busfreq_opp_info *oppi)
{
unsigned int index;
unsigned int tmp;
for (index = LV_0; index < EX4210_LV_NUM; index++)
if (opp_get_freq(opp) == exynos4210_busclk_table[index].clk)
if (oppi->rate == exynos4210_busclk_table[index].clk)
break;
if (index == EX4210_LV_NUM)
@ -361,13 +372,14 @@ static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp)
return 0;
}
static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp)
static int exynos4x12_set_busclk(struct busfreq_data *data,
struct busfreq_opp_info *oppi)
{
unsigned int index;
unsigned int tmp;
for (index = LV_0; index < EX4x12_LV_NUM; index++)
if (opp_get_freq(opp) == exynos4x12_mifclk_table[index].clk)
if (oppi->rate == exynos4x12_mifclk_table[index].clk)
break;
if (index == EX4x12_LV_NUM)
@ -576,11 +588,12 @@ static int exynos4x12_get_intspec(unsigned long mifclk)
return -EINVAL;
}
static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
struct opp *oldopp)
static int exynos4_bus_setvolt(struct busfreq_data *data,
struct busfreq_opp_info *oppi,
struct busfreq_opp_info *oldoppi)
{
int err = 0, tmp;
unsigned long volt = opp_get_voltage(opp);
unsigned long volt = oppi->volt;
switch (data->type) {
case TYPE_BUSF_EXYNOS4210:
@ -595,11 +608,11 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
if (err)
break;
tmp = exynos4x12_get_intspec(opp_get_freq(opp));
tmp = exynos4x12_get_intspec(oppi->rate);
if (tmp < 0) {
err = tmp;
regulator_set_voltage(data->vdd_mif,
opp_get_voltage(oldopp),
oldoppi->volt,
MAX_SAFEVOLT);
break;
}
@ -609,7 +622,7 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
/* Try to recover */
if (err)
regulator_set_voltage(data->vdd_mif,
opp_get_voltage(oldopp),
oldoppi->volt,
MAX_SAFEVOLT);
break;
default:
@ -626,17 +639,26 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct busfreq_data *data = platform_get_drvdata(pdev);
struct opp *opp = devfreq_recommended_opp(dev, _freq, flags);
unsigned long freq = opp_get_freq(opp);
unsigned long old_freq = opp_get_freq(data->curr_opp);
struct opp *opp;
unsigned long freq;
unsigned long old_freq = data->curr_oppinfo.rate;
struct busfreq_opp_info new_oppinfo;
if (IS_ERR(opp))
rcu_read_lock();
opp = devfreq_recommended_opp(dev, _freq, flags);
if (IS_ERR(opp)) {
rcu_read_unlock();
return PTR_ERR(opp);
}
new_oppinfo.rate = opp_get_freq(opp);
new_oppinfo.volt = opp_get_voltage(opp);
rcu_read_unlock();
freq = new_oppinfo.rate;
if (old_freq == freq)
return 0;
dev_dbg(dev, "targetting %lukHz %luuV\n", freq, opp_get_voltage(opp));
dev_dbg(dev, "targetting %lukHz %luuV\n", freq, new_oppinfo.volt);
mutex_lock(&data->lock);
@ -644,17 +666,18 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
goto out;
if (old_freq < freq)
err = exynos4_bus_setvolt(data, opp, data->curr_opp);
err = exynos4_bus_setvolt(data, &new_oppinfo,
&data->curr_oppinfo);
if (err)
goto out;
if (old_freq != freq) {
switch (data->type) {
case TYPE_BUSF_EXYNOS4210:
err = exynos4210_set_busclk(data, opp);
err = exynos4210_set_busclk(data, &new_oppinfo);
break;
case TYPE_BUSF_EXYNOS4x12:
err = exynos4x12_set_busclk(data, opp);
err = exynos4x12_set_busclk(data, &new_oppinfo);
break;
default:
err = -EINVAL;
@ -664,11 +687,12 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
goto out;
if (old_freq > freq)
err = exynos4_bus_setvolt(data, opp, data->curr_opp);
err = exynos4_bus_setvolt(data, &new_oppinfo,
&data->curr_oppinfo);
if (err)
goto out;
data->curr_opp = opp;
data->curr_oppinfo = new_oppinfo;
out:
mutex_unlock(&data->lock);
return err;
@ -702,7 +726,7 @@ static int exynos4_bus_get_dev_status(struct device *dev,
exynos4_read_ppmu(data);
busier_dmc = exynos4_get_busier_dmc(data);
stat->current_frequency = opp_get_freq(data->curr_opp);
stat->current_frequency = data->curr_oppinfo.rate;
if (busier_dmc)
addr = S5P_VA_DMC1;
@ -933,6 +957,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,
struct busfreq_data *data = container_of(this, struct busfreq_data,
pm_notifier);
struct opp *opp;
struct busfreq_opp_info new_oppinfo;
unsigned long maxfreq = ULONG_MAX;
int err = 0;
@ -943,18 +968,29 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,
data->disabled = true;
rcu_read_lock();
opp = opp_find_freq_floor(data->dev, &maxfreq);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(data->dev, "%s: unable to find a min freq\n",
__func__);
return PTR_ERR(opp);
}
new_oppinfo.rate = opp_get_freq(opp);
new_oppinfo.volt = opp_get_voltage(opp);
rcu_read_unlock();
err = exynos4_bus_setvolt(data, opp, data->curr_opp);
err = exynos4_bus_setvolt(data, &new_oppinfo,
&data->curr_oppinfo);
if (err)
goto unlock;
switch (data->type) {
case TYPE_BUSF_EXYNOS4210:
err = exynos4210_set_busclk(data, opp);
err = exynos4210_set_busclk(data, &new_oppinfo);
break;
case TYPE_BUSF_EXYNOS4x12:
err = exynos4x12_set_busclk(data, opp);
err = exynos4x12_set_busclk(data, &new_oppinfo);
break;
default:
err = -EINVAL;
@ -962,7 +998,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this,
if (err)
goto unlock;
data->curr_opp = opp;
data->curr_oppinfo = new_oppinfo;
unlock:
mutex_unlock(&data->lock);
if (err)
@ -1027,13 +1063,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
}
}
rcu_read_lock();
opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(dev, "Invalid initial frequency %lu kHz.\n",
exynos4_devfreq_profile.initial_freq);
return PTR_ERR(opp);
}
data->curr_opp = opp;
data->curr_oppinfo.rate = opp_get_freq(opp);
data->curr_oppinfo.volt = opp_get_voltage(opp);
rcu_read_unlock();
platform_set_drvdata(pdev, data);

View File

@ -448,8 +448,6 @@ static int intel_idle_probe(void)
else
on_each_cpu(__setup_broadcast_timer, (void *)true, 1);
register_cpu_notifier(&cpu_hotplug_notifier);
pr_debug(PREFIX "v" INTEL_IDLE_VERSION
" model 0x%X\n", boot_cpu_data.x86_model);
@ -612,6 +610,7 @@ static int __init intel_idle_init(void)
return retval;
}
}
register_cpu_notifier(&cpu_hotplug_notifier);
return 0;
}