* for chipsets ICH2-M and ICH3-M.
*
* Many thanks to Ducrot Bruno for finding and fixing the last
- * "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler
+ * "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler
* for extensive testing.
*
* BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
*********************************************************************/
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/pci.h>
/* speedstep_chipset:
- * It is necessary to know which chipset is used. As accesses to
- * this device occur at various places in this module, we need a
+ * It is necessary to know which chipset is used. As accesses to
+ * this device occur at various places in this module, we need a
* static struct pci_dev * pointing to that device.
*/
-static struct pci_dev *speedstep_chipset_dev;
+static struct pci_dev *speedstep_chipset_dev;
/* speedstep_processor
*/
-static unsigned int speedstep_processor = 0;
+static unsigned int speedstep_processor = 0;
-/*
+/*
* There are only two frequency states for each processor. Values
* are in kHz for the time being.
*/
static struct cpufreq_frequency_table speedstep_freqs[] = {
- {SPEEDSTEP_HIGH, 0},
+ {SPEEDSTEP_HIGH, 0},
{SPEEDSTEP_LOW, 0},
{0, CPUFREQ_TABLE_END},
};
/**
* speedstep_set_state - set the SpeedStep state
* @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
- * @notify: whether to call cpufreq_notify_transition for CPU speed changes
*
- * Tries to change the SpeedStep state.
+ * Tries to change the SpeedStep state.
*/
-static void speedstep_set_state (unsigned int state, unsigned int notify)
+static void speedstep_set_state (unsigned int state)
{
- u32 pmbase;
- u8 pm2_blk;
- u8 value;
- unsigned long flags;
- struct cpufreq_freqs freqs;
+ u32 pmbase;
+ u8 pm2_blk;
+ u8 value;
+ unsigned long flags;
if (!speedstep_chipset_dev || (state > 0x1))
return;
- freqs.old = speedstep_get_processor_frequency(speedstep_processor);
- freqs.new = speedstep_freqs[state].frequency;
- freqs.cpu = 0; /* speedstep.c is UP only driver */
-
- if (notify)
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-
/* get PMBASE */
pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
- if (!(pmbase & 0x01))
- {
+ if (!(pmbase & 0x01)) {
printk(KERN_ERR "cpufreq: could not find speedstep register\n");
return;
}
printk (KERN_ERR "cpufreq: change failed - I/O error\n");
}
- if (notify)
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
-
return;
}
*/
static int speedstep_activate (void)
{
- u16 value = 0;
+ u16 value = 0;
if (!speedstep_chipset_dev)
return -EINVAL;
- pci_read_config_word(speedstep_chipset_dev,
- 0x00A0, &value);
+ pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value);
if (!(value & 0x08)) {
value |= 0x08;
dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM) registers\n");
- pci_write_config_word(speedstep_chipset_dev,
- 0x00A0, value);
+ pci_write_config_word(speedstep_chipset_dev, 0x00A0, value);
}
return 0;
/**
* speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
*
- * Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to
- * the LPC bridge / PM module which contains all power-management
+ * Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to
+ * the LPC bridge / PM module which contains all power-management
* functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
* chipset, or zero on failure.
*/
static unsigned int speedstep_detect_chipset (void)
{
speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82801DB_12,
+ PCI_DEVICE_ID_INTEL_82801DB_12,
PCI_ANY_ID,
PCI_ANY_ID,
NULL);
return 4; /* 4-M */
speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82801CA_12,
+ PCI_DEVICE_ID_INTEL_82801CA_12,
PCI_ANY_ID,
PCI_ANY_ID,
NULL);
NULL);
if (speedstep_chipset_dev) {
/* speedstep.c causes lockups on Dell Inspirons 8000 and
- * 8100 which use a pretty old revision of the 82815
+ * 8100 which use a pretty old revision of the 82815
* host brige. Abort on these systems.
*/
- static struct pci_dev *hostbridge;
- u8 rev = 0;
+ static struct pci_dev *hostbridge;
+ u8 rev = 0;
hostbridge = pci_find_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82815_MC,
if (!hostbridge)
return 2; /* 2-M */
-
+
pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev);
if (rev < 5) {
dprintk(KERN_INFO "cpufreq: hostbridge does not support speedstep\n");
return 0;
}
+static unsigned int speedstep_get(unsigned int cpu)
+{
+ unsigned int speed;
+ cpumask_t cpus_allowed,affected_cpu_map;
+
+ /* only run on CPU to be set, or on its sibling */
+ cpus_allowed = current->cpus_allowed;
+#ifdef CONFIG_SMP
+ affected_cpu_map = cpu_sibling_map[cpu];
+#else
+ affected_cpu_map = cpumask_of_cpu(cpu);
+#endif
+ set_cpus_allowed(current, affected_cpu_map);
+ speed=speedstep_get_processor_frequency(speedstep_processor);
+ set_cpus_allowed(current, cpus_allowed);
+ return speed;
+}
/**
* speedstep_target - set a new CPUFreq policy
unsigned int target_freq,
unsigned int relation)
{
- unsigned int newstate = 0;
+ unsigned int newstate = 0;
+ struct cpufreq_freqs freqs;
+ cpumask_t cpus_allowed, affected_cpu_map;
+ int i;
if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
return -EINVAL;
- speedstep_set_state(newstate, 1);
+ freqs.old = speedstep_get(policy->cpu);
+ freqs.new = speedstep_freqs[newstate].frequency;
+ freqs.cpu = policy->cpu;
+
+ /* no transition necessary */
+ if (freqs.old == freqs.new)
+ return 0;
+
+ cpus_allowed = current->cpus_allowed;
+
+ /* only run on CPU to be set, or on its sibling */
+#ifdef CONFIG_SMP
+ affected_cpu_map = cpu_sibling_map[policy->cpu];
+#else
+ affected_cpu_map = cpumask_of_cpu(policy->cpu);
+#endif
+
+ for_each_cpu_mask(i, affected_cpu_map) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ /* switch to physical CPU where state is to be changed */
+ set_cpus_allowed(current, affected_cpu_map);
+
+ speedstep_set_state(newstate);
+
+ /* allow to be run on all CPUs */
+ set_cpus_allowed(current, cpus_allowed);
+
+ for_each_cpu_mask(i, affected_cpu_map) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
return 0;
}
static int speedstep_cpu_init(struct cpufreq_policy *policy)
{
- int result = 0;
- unsigned int speed;
+ int result = 0;
+ unsigned int speed;
+ cpumask_t cpus_allowed,affected_cpu_map;
+
/* capability check */
- if (policy->cpu != 0)
+ if (policy->cpu != 0) /* FIXME: better support for SMT in cpufreq core. Up until then, it's better to register only one CPU */
return -ENODEV;
+ /* only run on CPU to be set, or on its sibling */
+ cpus_allowed = current->cpus_allowed;
+#ifdef CONFIG_SMP
+ affected_cpu_map = cpu_sibling_map[policy->cpu];
+#else
+ affected_cpu_map = cpumask_of_cpu(policy->cpu);
+#endif
+ set_cpus_allowed(current, affected_cpu_map);
+
/* detect low and high frequency */
result = speedstep_get_freqs(speedstep_processor,
&speedstep_freqs[SPEEDSTEP_LOW].frequency,
&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
&speedstep_set_state);
+ set_cpus_allowed(current, cpus_allowed);
if (result)
return result;
/* get current speed setting */
- speed = speedstep_get_processor_frequency(speedstep_processor);
+ speed = speedstep_get(policy->cpu);
if (!speed)
return -EIO;
- dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n",
+ dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n",
(speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
(speed / 1000));
return 0;
}
-
static struct freq_attr* speedstep_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
static struct cpufreq_driver speedstep_driver = {
- .name = "speedstep-ich",
- .verify = speedstep_verify,
- .target = speedstep_target,
- .init = speedstep_cpu_init,
- .exit = speedstep_cpu_exit,
- .owner = THIS_MODULE,
- .attr = speedstep_attr,
+ .name = "speedstep-ich",
+ .verify = speedstep_verify,
+ .target = speedstep_target,
+ .init = speedstep_cpu_init,
+ .exit = speedstep_cpu_exit,
+ .get = speedstep_get,
+ .owner = THIS_MODULE,
+ .attr = speedstep_attr,
};