📄 longhaul.c
字号:
longhaul_table[j].index = longhaul_table[min_i].index; longhaul_table[min_i].index = temp; } } longhaul_table[k].frequency = CPUFREQ_TABLE_END; /* Find index we are running on */ for (j = 0; j < k; j++) { if (clock_ratio[longhaul_table[j].index & 0x1f] == mult) { longhaul_index = j; break; } } return 0;}static void __init longhaul_setup_voltagescaling(void){ union msr_longhaul longhaul; struct mV_pos minvid, maxvid, vid; unsigned int j, speed, pos, kHz_step, numvscales; int min_vid_speed; rdmsrl(MSR_VIA_LONGHAUL, longhaul.val); if (!(longhaul.bits.RevisionID & 1)) { printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n"); return; } if (!longhaul.bits.VRMRev) { printk(KERN_INFO PFX "VRM 8.5\n"); vrm_mV_table = &vrm85_mV[0]; mV_vrm_table = &mV_vrm85[0]; } else { printk(KERN_INFO PFX "Mobile VRM\n"); if (cpu_model < CPU_NEHEMIAH) return; vrm_mV_table = &mobilevrm_mV[0]; mV_vrm_table = &mV_mobilevrm[0]; } minvid = vrm_mV_table[longhaul.bits.MinimumVID]; maxvid = vrm_mV_table[longhaul.bits.MaximumVID]; if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) { printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " "Voltage scaling disabled.\n", minvid.mV/1000, minvid.mV%1000, maxvid.mV/1000, maxvid.mV%1000); return; } if (minvid.mV == maxvid.mV) { printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are " "both %d.%03d. Voltage scaling disabled\n", maxvid.mV/1000, maxvid.mV%1000); return; } /* How many voltage steps */ numvscales = maxvid.pos - minvid.pos + 1; printk(KERN_INFO PFX "Max VID=%d.%03d " "Min VID=%d.%03d, " "%d possible voltage scales\n", maxvid.mV/1000, maxvid.mV%1000, minvid.mV/1000, minvid.mV%1000, numvscales); /* Calculate max frequency at min voltage */ j = longhaul.bits.MinMHzBR; if (longhaul.bits.MinMHzBR4) j += 16; min_vid_speed = eblcr_table[j]; if (min_vid_speed == -1) return; switch (longhaul.bits.MinMHzFSB) { case 0: min_vid_speed *= 13333; break; case 1: min_vid_speed *= 10000; break; case 3: min_vid_speed *= 6666; break; default: return; break; } if (min_vid_speed >= highest_speed) return; /* Calculate kHz for one voltage step */ kHz_step = (highest_speed - min_vid_speed) / numvscales; j = 0; while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) { speed = longhaul_table[j].frequency; if (speed > min_vid_speed) pos = (speed - min_vid_speed) / kHz_step + minvid.pos; else pos = minvid.pos; longhaul_table[j].index |= mV_vrm_table[pos] << 8; vid = vrm_mV_table[mV_vrm_table[pos]]; printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV); j++; } can_scale_voltage = 1; printk(KERN_INFO PFX "Voltage scaling enabled.\n");}static int longhaul_verify(struct cpufreq_policy *policy){ return cpufreq_frequency_table_verify(policy, longhaul_table);}static int longhaul_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation){ unsigned int table_index = 0; unsigned int i; unsigned int dir = 0; u8 vid, current_vid; if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) return -EINVAL; /* Don't set same frequency again */ if (longhaul_index == table_index) return 0; if (!can_scale_voltage) longhaul_setstate(table_index); else { /* On test system voltage transitions exceeding single * step up or down were turning motherboard off. Both * "ondemand" and "userspace" are unsafe. C7 is doing * this in hardware, C3 is old and we need to do this * in software. */ i = longhaul_index; current_vid = (longhaul_table[longhaul_index].index >> 8) & 0x1f; if (table_index > longhaul_index) dir = 1; while (i != table_index) { vid = (longhaul_table[i].index >> 8) & 0x1f; if (vid != current_vid) { longhaul_setstate(i); current_vid = vid; msleep(200); } if (dir) i++; else i--; } longhaul_setstate(table_index); } longhaul_index = table_index; return 0;}static unsigned int longhaul_get(unsigned int cpu){ if (cpu) return 0; return calc_speed(longhaul_get_cpu_mult());}static acpi_status longhaul_walk_callback(acpi_handle obj_handle, u32 nesting_level, void *context, void **return_value){ struct acpi_device *d; if ( acpi_bus_get_device(obj_handle, &d) ) { return 0; } *return_value = (void *)acpi_driver_data(d); return 1;}/* VIA don't support PM2 reg, but have something similar */static int enable_arbiter_disable(void){ struct pci_dev *dev; int status = 1; int reg; u8 pci_cmd; /* Find PLE133 host bridge */ reg = 0x78; dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0, NULL); /* Find PM133/VT8605 host bridge */ if (dev == NULL) dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8605_0, NULL); /* Find CLE266 host bridge */ if (dev == NULL) { reg = 0x76; dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_862X_0, NULL); /* Find CN400 V-Link host bridge */ if (dev == NULL) dev = pci_get_device(PCI_VENDOR_ID_VIA, 0x7259, NULL); } if (dev != NULL) { /* Enable access to port 0x22 */ pci_read_config_byte(dev, reg, &pci_cmd); if (!(pci_cmd & 1<<7)) { pci_cmd |= 1<<7; pci_write_config_byte(dev, reg, pci_cmd); pci_read_config_byte(dev, reg, &pci_cmd); if (!(pci_cmd & 1<<7)) { printk(KERN_ERR PFX "Can't enable access to port 0x22.\n"); status = 0; } } pci_dev_put(dev); return status; } return 0;}static int longhaul_setup_southbridge(void){ struct pci_dev *dev; u8 pci_cmd; /* Find VT8235 southbridge */ dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL); if (dev == NULL) /* Find VT8237 southbridge */ dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, NULL); if (dev != NULL) { /* Set transition time to max */ pci_read_config_byte(dev, 0xec, &pci_cmd); pci_cmd &= ~(1 << 2); pci_write_config_byte(dev, 0xec, pci_cmd); pci_read_config_byte(dev, 0xe4, &pci_cmd); pci_cmd &= ~(1 << 7); pci_write_config_byte(dev, 0xe4, pci_cmd); pci_read_config_byte(dev, 0xe5, &pci_cmd); pci_cmd |= 1 << 7; pci_write_config_byte(dev, 0xe5, pci_cmd); /* Get address of ACPI registers block*/ pci_read_config_byte(dev, 0x81, &pci_cmd); if (pci_cmd & 1 << 7) { pci_read_config_dword(dev, 0x88, &acpi_regs_addr); acpi_regs_addr &= 0xff00; printk(KERN_INFO PFX "ACPI I/O at 0x%x\n", acpi_regs_addr); } pci_dev_put(dev); return 1; } return 0;}static int __init longhaul_cpu_init(struct cpufreq_policy *policy){ struct cpuinfo_x86 *c = &cpu_data(0); char *cpuname=NULL; int ret; u32 lo, hi; /* Check what we have on this motherboard */ switch (c->x86_model) { case 6: cpu_model = CPU_SAMUEL; cpuname = "C3 'Samuel' [C5A]"; longhaul_version = TYPE_LONGHAUL_V1; memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio)); memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); break; case 7: switch (c->x86_mask) { case 0: longhaul_version = TYPE_LONGHAUL_V1; cpu_model = CPU_SAMUEL2; cpuname = "C3 'Samuel 2' [C5B]"; /* Note, this is not a typo, early Samuel2's had * Samuel1 ratios. */ memcpy(clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio)); memcpy(eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); break; case 1 ... 15: longhaul_version = TYPE_LONGHAUL_V1; if (c->x86_mask < 8) { cpu_model = CPU_SAMUEL2; cpuname = "C3 'Samuel 2' [C5B]"; } else { cpu_model = CPU_EZRA; cpuname = "C3 'Ezra' [C5C]"; } memcpy(clock_ratio, ezra_clock_ratio, sizeof(ezra_clock_ratio)); memcpy(eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); break; } break; case 8: cpu_model = CPU_EZRA_T; cpuname = "C3 'Ezra-T' [C5M]"; longhaul_version = TYPE_POWERSAVER; numscales=32; memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio)); memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr)); break; case 9: longhaul_version = TYPE_POWERSAVER; numscales = 32; memcpy(clock_ratio, nehemiah_clock_ratio, sizeof(nehemiah_clock_ratio)); memcpy(eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr)); switch (c->x86_mask) { case 0 ... 1: cpu_model = CPU_NEHEMIAH; cpuname = "C3 'Nehemiah A' [C5XLOE]"; break; case 2 ... 4: cpu_model = CPU_NEHEMIAH; cpuname = "C3 'Nehemiah B' [C5XLOH]"; break; case 5 ... 15: cpu_model = CPU_NEHEMIAH_C; cpuname = "C3 'Nehemiah C' [C5P]"; break; } break; default: cpuname = "Unknown"; break; } /* Check Longhaul ver. 2 */ if (longhaul_version == TYPE_LONGHAUL_V2) { rdmsr(MSR_VIA_LONGHAUL, lo, hi); if (lo == 0 && hi == 0) /* Looks like MSR isn't present */ longhaul_version = TYPE_LONGHAUL_V1; } printk (KERN_INFO PFX "VIA %s CPU detected. ", cpuname); switch (longhaul_version) { case TYPE_LONGHAUL_V1: case TYPE_LONGHAUL_V2: printk ("Longhaul v%d supported.\n", longhaul_version); break; case TYPE_POWERSAVER: printk ("Powersaver supported.\n"); break; }; /* Doesn't hurt */ longhaul_setup_southbridge(); /* Find ACPI data for processor */ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, &longhaul_walk_callback, NULL, (void *)&pr); /* Check ACPI support for C3 state */ if (pr != NULL && longhaul_version == TYPE_POWERSAVER) { cx = &pr->power.states[ACPI_STATE_C3]; if (cx->address > 0 && cx->latency <= 1000) longhaul_flags |= USE_ACPI_C3; } /* Disable if it isn't working */ if (disable_acpi_c3) longhaul_flags &= ~USE_ACPI_C3; /* Check if northbridge is friendly */ if (enable_arbiter_disable()) longhaul_flags |= USE_NORTHBRIDGE; /* Check ACPI support for bus master arbiter disable */ if (!(longhaul_flags & USE_ACPI_C3 || longhaul_flags & USE_NORTHBRIDGE) && ((pr == NULL) || !(pr->flags.bm_control))) { printk(KERN_ERR PFX "No ACPI support. Unsupported northbridge.\n"); return -ENODEV; } if (longhaul_flags & USE_NORTHBRIDGE) printk(KERN_INFO PFX "Using northbridge support.\n"); if (longhaul_flags & USE_ACPI_C3) printk(KERN_INFO PFX "Using ACPI support.\n"); ret = longhaul_get_ranges(); if (ret != 0) return ret; if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0)) longhaul_setup_voltagescaling(); policy->cpuinfo.transition_latency = 200000; /* nsec */ policy->cur = calc_speed(longhaul_get_cpu_mult()); ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table); if (ret) return ret; cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); return 0;}static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy){ cpufreq_frequency_table_put_attr(policy->cpu); return 0;}static struct freq_attr* longhaul_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL,};static struct cpufreq_driver longhaul_driver = { .verify = longhaul_verify, .target = longhaul_target, .get = longhaul_get, .init = longhaul_cpu_init, .exit = __devexit_p(longhaul_cpu_exit), .name = "longhaul", .owner = THIS_MODULE, .attr = longhaul_attr,};static int __init longhaul_init(void){ struct cpuinfo_x86 *c = &cpu_data(0); if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6) return -ENODEV;#ifdef CONFIG_SMP if (num_online_cpus() > 1) { printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n"); return -ENODEV; }#endif#ifdef CONFIG_X86_IO_APIC if (cpu_has_apic) { printk(KERN_ERR PFX "APIC detected. Longhaul is currently broken in this configuration.\n"); return -ENODEV; }#endif switch (c->x86_model) { case 6 ... 9: return cpufreq_register_driver(&longhaul_driver); case 10: printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7\n"); default: ;; } return -ENODEV;}static void __exit longhaul_exit(void){ int i; for (i=0; i < numscales; i++) { if (clock_ratio[i] == maxmult) { longhaul_setstate(i); break; } } cpufreq_unregister_driver(&longhaul_driver); kfree(longhaul_table);}/* Even if BIOS is exporting ACPI C3 state, and it is used * with success when CPU is idle, this state doesn't * trigger frequency transition in some cases. */module_param (disable_acpi_c3, int, 0644);MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");/* Change CPU voltage with frequency. Very usefull to save * power, but most VIA C3 processors aren't supporting it. */module_param (scale_voltage, int, 0644);MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");/* Force revision key to 0 for processors which doesn't * support voltage scaling, but are introducing itself as * such. */module_param(revid_errata, int, 0644);MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");MODULE_LICENSE ("GPL");late_initcall(longhaul_init);module_exit(longhaul_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -