do { if (i++ > 10000) {
pr_debug("detected change pending stuck\n"); return1;
}
rdmsr(MSR_FIDVID_STATUS, lo, hi);
} while (lo & MSR_S_LO_CHANGE_PENDING);
data->currvid = hi & MSR_S_HI_CURRENT_VID;
data->currfid = lo & MSR_S_LO_CURRENT_FID;
return0;
}
/* the isochronous relief time */ staticvoid count_off_irt(struct powernow_k8_data *data)
{
udelay((1 << data->irt) * 10);
}
/* the voltage stabilization time */ staticvoid count_off_vst(struct powernow_k8_data *data)
{
udelay(data->vstable * VST_UNITS_20US);
}
/* need to init the control msr to a safe value (for each cpu) */ staticvoid fidvid_msr_init(void)
{
u32 lo, hi;
u8 fid, vid;
rdmsr(MSR_FIDVID_STATUS, lo, hi);
vid = hi & MSR_S_HI_CURRENT_VID;
fid = lo & MSR_S_LO_CURRENT_FID;
lo = fid | (vid << MSR_C_LO_VID_SHIFT);
hi = MSR_C_HI_STP_GNT_BENIGN;
pr_debug("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi);
wrmsr(MSR_FIDVID_CTL, lo, hi);
}
/* write the new fid value along with the other control fields to the msr */ staticint write_new_fid(struct powernow_k8_data *data, u32 fid)
{
u32 lo;
u32 savevid = data->currvid;
u32 i = 0;
if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
pr_err("internal error - overflow on fid write\n"); return1;
}
lo = fid;
lo |= (data->currvid << MSR_C_LO_VID_SHIFT);
lo |= MSR_C_LO_INIT_FID_VID;
pr_debug("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
fid, lo, data->plllock * PLL_LOCK_CONVERSION);
do {
wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION); if (i++ > 100) {
pr_err("Hardware error - pending bit very stuck - no further pstate changes possible\n"); return1;
}
} while (query_current_values_with_pending_wait(data));
count_off_irt(data);
if (savevid != data->currvid) {
pr_err("vid change on fid trans, old 0x%x, new 0x%x\n",
savevid, data->currvid); return1;
}
if (fid != data->currfid) {
pr_err("fid trans failed, fid 0x%x, curr 0x%x\n", fid,
data->currfid); return1;
}
return0;
}
/* Write a new vid to the hardware */ staticint write_new_vid(struct powernow_k8_data *data, u32 vid)
{
u32 lo;
u32 savefid = data->currfid; int i = 0;
if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
pr_err("internal error - overflow on vid write\n"); return1;
}
lo = data->currfid;
lo |= (vid << MSR_C_LO_VID_SHIFT);
lo |= MSR_C_LO_INIT_FID_VID;
pr_debug("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
vid, lo, STOP_GRANT_5NS);
do {
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS); if (i++ > 100) {
pr_err("internal error - pending bit very stuck - no further pstate changes possible\n"); return1;
}
} while (query_current_values_with_pending_wait(data));
if (savefid != data->currfid) {
pr_err("fid changed on vid trans, old 0x%x new 0x%x\n",
savefid, data->currfid); return1;
}
if (vid != data->currvid) {
pr_err("vid trans failed, vid 0x%x, curr 0x%x\n",
vid, data->currvid); return1;
}
if (data->batps) { /* use ACPI support to get full speed on mains power */
pr_warn("Only %d pstates usable (use ACPI driver for full range\n",
data->batps);
data->numps = data->batps;
}
for (j = 1; j < data->numps; j++) { if (pst[j-1].fid >= pst[j].fid) {
pr_err("PST out of sequence\n"); return -EINVAL;
}
}
if (data->numps < 2) {
pr_err("no p states to transition\n"); return -ENODEV;
}
if (check_pst_table(data, pst, maxvid)) return -EINVAL;
data->numps = psb->numps;
pr_debug("numpstates: 0x%x\n", data->numps); return fill_powernow_table(data,
(struct pst_s *)(psb+1), maxvid);
} /* *Ifyouseethismessage,complaintoBIOSmanufacturer.If *hetellsyou"wedonotsupportLinux"orsomesimilar *nonsense,rememberthatWindows2000usesthesamelegacy *mechanismthattheoldLinuxPSBdriveruses.Tellthemit *isbrokenwithWindows2000. * *ThereferencetotheAMDdocumentationischapter9inthe *BIOSandKernelDeveloper'sGuide,whichisavailableon *www.amd.com
*/
pr_err(FW_BUG "No PSB or ACPI _PSS objects\n");
pr_err("Make sure that your BIOS is up to date and Cool'N'Quiet support is enabled in BIOS setup\n"); return -ENODEV;
}
/* verify frequency is OK */ if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) {
pr_debug("invalid freq %u kHz, ignoring\n", freq);
invalidate_entry(powernow_table, i); continue;
}
/* verify voltage is OK -
* BIOSs are using "off" to indicate invalid */ if (vid == VID_OFF) {
pr_debug("invalid vid %u, ignoring\n", vid);
invalidate_entry(powernow_table, i); continue;
}
staticvoid powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
{ if (data->acpi_data.state_count)
acpi_processor_unregister_performance(data->cpu);
free_cpumask_var(data->acpi_data.shared_cpu_map);
}
staticint get_transition_latency(struct powernow_k8_data *data)
{ int max_latency = 0; int i; for (i = 0; i < data->acpi_data.state_count; i++) { int cur_latency = data->acpi_data.states[i].transition_latency
+ data->acpi_data.states[i].bus_master_latency; if (cur_latency > max_latency)
max_latency = cur_latency;
} if (max_latency == 0) {
pr_err(FW_WARN "Invalid zero transition latency\n");
max_latency = 1;
} /* value in usecs, needs to be in nanoseconds */ return1000 * max_latency;
}
/* Take a frequency, and issue the fid/vid transition command */ staticint transition_frequency_fidvid(struct powernow_k8_data *data, unsignedint index, struct cpufreq_policy *policy)
{
u32 fid = 0;
u32 vid = 0; int res; struct cpufreq_freqs freqs;
pr_debug("cpu %d transition to index %u\n", smp_processor_id(), index);
/* fid/vid correctness check for k8 */ /* fid are the lower 8 bits of the index we stored into *thecpufreqfrequencytableinfind_psb_table,vid *aretheupper8bits.
*/
fid = data->powernow_table[index].driver_data & 0xFF;
vid = (data->powernow_table[index].driver_data & 0xFF00) >> 8;
pr_debug("table matched fid 0x%x, giving vid 0x%x\n", fid, vid);
if (query_current_values_with_pending_wait(data)) return1;
if ((data->currvid == vid) && (data->currfid == fid)) {
pr_debug("target matches current values (fid 0x%x, vid 0x%x)\n",
fid, vid); return0;
}
pr_debug("cpu %d, changing to fid 0x%x, vid 0x%x\n",
smp_processor_id(), fid, vid);
freqs.old = find_khz_freq_from_fid(data->currfid);
freqs.new = find_khz_freq_from_fid(fid);
cpufreq_freq_transition_begin(policy, &freqs);
res = transition_fid_vid(data, fid, vid);
cpufreq_freq_transition_end(policy, &freqs, res);
if (pending_bit_stuck()) {
pr_err("failing targ, change pending bit set\n"); return -EIO;
}
pr_debug("targ: cpu %d, %d kHz, min %d, max %d\n",
pol->cpu, data->powernow_table[newstate].frequency, pol->min,
pol->max);
if (query_current_values_with_pending_wait(data)) return -EIO;
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
if ((checkvid != data->currvid) ||
(checkfid != data->currfid)) {
pr_info("error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
checkfid, data->currfid,
checkvid, data->currvid);
}
mutex_lock(&fidvid_mutex);
powernow_k8_acpi_pst_values(data, newstate);
ret = transition_frequency_fidvid(data, newstate, pol);
if (ret) {
pr_err("transition frequency failed\n");
mutex_unlock(&fidvid_mutex); return1;
}
mutex_unlock(&fidvid_mutex);
pol->cur = find_khz_freq_from_fid(data->currfid);
return0;
}
/* Driver entry point to switch to the target frequency */ staticint powernowk8_target(struct cpufreq_policy *pol, unsigned index)
{ struct powernowk8_target_arg pta = { .pol = pol, .newstate = index };
if (pending_bit_stuck()) {
pr_err("failing init, change pending bit set\n");
init_on_cpu->rc = -ENODEV; return;
}
if (query_current_values_with_pending_wait(init_on_cpu->data)) {
init_on_cpu->rc = -ENODEV; return;
}
fidvid_msr_init();
init_on_cpu->rc = 0;
}
#define MISSING_PSS_MSG \
FW_BUG "No compatible ACPI _PSS objects found.\n" \
FW_BUG "First, make sure Cool'N'Quiet is enabled in the BIOS.\n" \
FW_BUG "If that doesn't help, try upgrading your BIOS.\n"
/* per CPU init entry point to the driver */ staticint powernowk8_cpu_init(struct cpufreq_policy *pol)
{ struct powernow_k8_data *data; struct init_on_cpu init_on_cpu; int rc, cpu;
smp_call_function_single(pol->cpu, check_supported_cpu, &rc, 1); if (rc) return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
data->cpu = pol->cpu;
if (powernow_k8_cpu_init_acpi(data)) { /* *UsethePSBBIOSstructure.Thisisonlyavailableon *anUPversion,andisdeprecatedbyAMD.
*/ if (num_online_cpus() != 1) {
pr_err_once(MISSING_PSS_MSG); goto err_out;
} if (pol->cpu != 0) {
pr_err(FW_BUG "No ACPI _PSS objects for CPU other than CPU0. Complain to your BIOS vendor.\n"); goto err_out;
}
rc = find_psb_table(data); if (rc) goto err_out;
/* Take a crude guess here.
* That guess was in microseconds, so multiply with 1000 */
pol->cpuinfo.transition_latency = (
((data->rvo + 8) * data->vstable * VST_UNITS_20US) +
((1 << data->irt) * 30)) * 1000;
} else/* ACPI _PSS objects available */
pol->cpuinfo.transition_latency = get_transition_latency(data);
/* only run on specific CPU from here on */
init_on_cpu.data = data;
smp_call_function_single(data->cpu, powernowk8_cpu_init_on_cpu,
&init_on_cpu, 1);
rc = init_on_cpu.rc; if (rc != 0) goto err_out_exit_acpi;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.