📄 powernow-k8.c
字号:
data->currfid, reqfid); return 1; } if (savevid != data->currvid) { printk(KERN_ERR PFX "ph2: vid changed, save 0x%x, curr 0x%x\n", savevid, data->currvid); return 1; } dprintk("ph2 complete, currfid 0x%x, currvid 0x%x\n", data->currfid, data->currvid); return 0;}/* Phase 3 - core voltage transition flow ... jump to the final vid. */static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid){ u32 savefid = data->currfid; u32 savereqvid = reqvid; dprintk("ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n", smp_processor_id(), data->currfid, data->currvid); if (reqvid != data->currvid) { if (write_new_vid(data, reqvid)) return 1; if (savefid != data->currfid) { printk(KERN_ERR PFX "ph3: bad fid change, save 0x%x, curr 0x%x\n", savefid, data->currfid); return 1; } if (data->currvid != reqvid) { printk(KERN_ERR PFX "ph3: failed vid transition\n, req 0x%x, curr 0x%x", reqvid, data->currvid); return 1; } } if (query_current_values_with_pending_wait(data)) return 1; if (savereqvid != data->currvid) { dprintk("ph3 failed, currvid 0x%x\n", data->currvid); return 1; } if (savefid != data->currfid) { dprintk("ph3 failed, currfid changed 0x%x\n", data->currfid); return 1; } dprintk("ph3 complete, currfid 0x%x, currvid 0x%x\n", data->currfid, data->currvid); return 0;}static int check_supported_cpu(unsigned int cpu){ cpumask_t oldmask = CPU_MASK_ALL; u32 eax, ebx, ecx, edx; unsigned int rc = 0; oldmask = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(cpu)); if (smp_processor_id() != cpu) { printk(KERN_ERR "limiting to cpu %u failed\n", cpu); goto out; } if (current_cpu_data.x86_vendor != X86_VENDOR_AMD) goto out; eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE); if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) || ((eax & CPUID_XFAM) != CPUID_XFAM_K8) || ((eax & CPUID_XMOD) > CPUID_XMOD_REV_F)) { printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax); goto out; } eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES); if (eax < CPUID_FREQ_VOLT_CAPABILITIES) { printk(KERN_INFO PFX "No frequency change capabilities detected\n"); goto out; } cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx); if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) { printk(KERN_INFO PFX "Power state transitions not supported\n"); goto out; } rc = 1;out: set_cpus_allowed(current, oldmask); return rc;}static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid){ unsigned int j; u8 lastfid = 0xff; for (j = 0; j < data->numps; j++) { if (pst[j].vid > LEAST_VID) { printk(KERN_ERR PFX "vid %d invalid : 0x%x\n", j, pst[j].vid); return -EINVAL; } if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */ printk(KERN_ERR BFX "0 vid exceeded with pstate %d\n", j); return -ENODEV; } if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */ printk(KERN_ERR BFX "maxvid exceeded with pstate %d\n", j); return -ENODEV; } if ((pst[j].fid > MAX_FID) || (pst[j].fid & 1) || (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) { /* Only first fid is allowed to be in "low" range */ printk(KERN_ERR PFX "two low fids - %d : 0x%x\n", j, pst[j].fid); return -EINVAL; } if (pst[j].fid < lastfid) lastfid = pst[j].fid; } if (lastfid & 1) { printk(KERN_ERR PFX "lastfid invalid\n"); return -EINVAL; } if (lastfid > LO_FID_TABLE_TOP) printk(KERN_INFO PFX "first fid not from lo freq table\n"); return 0;}static void print_basics(struct powernow_k8_data *data){ int j; for (j = 0; j < data->numps; j++) { if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x (%d mV)\n", j, data->powernow_table[j].index & 0xff, data->powernow_table[j].frequency/1000, data->powernow_table[j].index >> 8, find_millivolts_from_vid(data, data->powernow_table[j].index >> 8)); } if (data->batps) printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);}static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid){ struct cpufreq_frequency_table *powernow_table; unsigned int j; if (data->batps) { /* use ACPI support to get full speed on mains power */ printk(KERN_WARNING PFX "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) { printk(KERN_ERR PFX "PST out of sequence\n"); return -EINVAL; } } if (data->numps < 2) { printk(KERN_ERR PFX "no p states to transition\n"); return -ENODEV; } if (check_pst_table(data, pst, maxvid)) return -EINVAL; powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (data->numps + 1)), GFP_KERNEL); if (!powernow_table) { printk(KERN_ERR PFX "powernow_table memory alloc failure\n"); return -ENOMEM; } for (j = 0; j < data->numps; j++) { powernow_table[j].index = pst[j].fid; /* lower 8 bits */ powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */ powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid); } powernow_table[data->numps].frequency = CPUFREQ_TABLE_END; powernow_table[data->numps].index = 0; if (query_current_values_with_pending_wait(data)) { kfree(powernow_table); return -EIO; } dprintk("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid); data->powernow_table = powernow_table; print_basics(data); for (j = 0; j < data->numps; j++) if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid)) return 0; dprintk("currfid/vid do not match PST, ignoring\n"); return 0;}/* Find and validate the PSB/PST table in BIOS. */static int find_psb_table(struct powernow_k8_data *data){ struct psb_s *psb; unsigned int i; u32 mvs; u8 maxvid; u32 cpst = 0; u32 thiscpuid; for (i = 0xc0000; i < 0xffff0; i += 0x10) { /* Scan BIOS looking for the signature. */ /* It can not be at ffff0 - it is too big. */ psb = phys_to_virt(i); if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0) continue; dprintk("found PSB header at 0x%p\n", psb); dprintk("table vers: 0x%x\n", psb->tableversion); if (psb->tableversion != PSB_VERSION_1_4) { printk(KERN_INFO BFX "PSB table is not v1.4\n"); return -ENODEV; } dprintk("flags: 0x%x\n", psb->flags1); if (psb->flags1) { printk(KERN_ERR BFX "unknown flags\n"); return -ENODEV; } data->vstable = psb->vstable; dprintk("voltage stabilization time: %d(*20us)\n", data->vstable); dprintk("flags2: 0x%x\n", psb->flags2); data->rvo = psb->flags2 & 3; data->irt = ((psb->flags2) >> 2) & 3; mvs = ((psb->flags2) >> 4) & 3; data->vidmvs = 1 << mvs; data->batps = ((psb->flags2) >> 6) & 3; dprintk("ramp voltage offset: %d\n", data->rvo); dprintk("isochronous relief time: %d\n", data->irt); dprintk("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs); dprintk("numpst: 0x%x\n", psb->num_tables); cpst = psb->num_tables; if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0) ){ thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE); if ((thiscpuid == 0x00000fc0) || (thiscpuid == 0x00000fe0) ) { cpst = 1; } } if (cpst != 1) { printk(KERN_ERR BFX "numpst must be 1\n"); return -ENODEV; } data->plllock = psb->plllocktime; dprintk("plllocktime: 0x%x (units 1us)\n", psb->plllocktime); dprintk("maxfid: 0x%x\n", psb->maxfid); dprintk("maxvid: 0x%x\n", psb->maxvid); maxvid = psb->maxvid; data->numps = psb->numps; dprintk("numpstates: 0x%x\n", data->numps); return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid); } /* * If you see this message, complain to BIOS manufacturer. If * he tells you "we do not support Linux" or some similar * nonsense, remember that Windows 2000 uses the same legacy * mechanism that the old Linux PSB driver uses. Tell them it * is broken with Windows 2000. * * The reference to the AMD documentation is chapter 9 in the * BIOS and Kernel Developer's Guide, which is available on * www.amd.com */ printk(KERN_INFO PFX "BIOS error - no PSB or ACPI _PSS objects\n"); return -ENODEV;}#ifdef CONFIG_X86_POWERNOW_K8_ACPIstatic void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index){ if (!data->acpi_data.state_count) return; data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK; data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK; data->exttype = (data->acpi_data.states[index].control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK; data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK; data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK); data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;}static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data){ int i; int cntlofreq = 0; struct cpufreq_frequency_table *powernow_table; if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { dprintk("register performance failed: bad ACPI data\n"); return -EIO; } /* verify the data contained in the ACPI structures */ if (data->acpi_data.state_count <= 1) { dprintk("No ACPI P-States\n"); goto err_out; } if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) || (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) { dprintk("Invalid control/status registers (%x - %x)\n", data->acpi_data.control_register.space_id, data->acpi_data.status_register.space_id); goto err_out; } /* fill in data->powernow_table */ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (data->acpi_data.state_count + 1)), GFP_KERNEL); if (!powernow_table) { dprintk("powernow_table memory alloc failure\n"); goto err_out; } for (i = 0; i < data->acpi_data.state_count; i++) { u32 fid; u32 vid; if (data->exttype) { fid = data->acpi_data.states[i].status & FID_MASK; vid = (data->acpi_data.states[i].status >> VID_SHIFT) & VID_MASK; } else { fid = data->acpi_data.states[i].control & FID_MASK; vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK; } dprintk(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); powernow_table[i].index = fid; /* lower 8 bits */ powernow_table[i].index |= (vid << 8); /* upper 8 bits */ powernow_table[i].frequency = find_khz_freq_from_fid(fid); /* verify frequency is OK */ if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) || (powernow_table[i].frequency < (MIN_FREQ * 1000))) { dprintk("invalid freq %u kHz, ignoring\n", powernow_table[i].frequency); powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; continue; } /* verify voltage is OK - BIOSs are using "off" to indicate invalid */ if (vid == VID_OFF) { dprintk("invalid vid %u, ignoring\n", vid); powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; continue; } /* verify only 1 entry from the lo frequency table */ if (fid < HI_FID_TABLE_BOTTOM) { if (cntlofreq) { /* if both entries are the same, ignore this * one... */ if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -