162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) 2004-2006 Sebastian Witt <se.witt@gmx.net> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based upon reverse engineered information 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/moduleparam.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/cpufreq.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NFORCE2_XTAL 25 2162306a36Sopenharmony_ci#define NFORCE2_BOOTFSB 0x48 2262306a36Sopenharmony_ci#define NFORCE2_PLLENABLE 0xa8 2362306a36Sopenharmony_ci#define NFORCE2_PLLREG 0xa4 2462306a36Sopenharmony_ci#define NFORCE2_PLLADR 0xa0 2562306a36Sopenharmony_ci#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define NFORCE2_MIN_FSB 50 2862306a36Sopenharmony_ci#define NFORCE2_SAFE_DISTANCE 50 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Delay in ms between FSB changes */ 3162306a36Sopenharmony_ci/* #define NFORCE2_DELAY 10 */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * nforce2_chipset: 3562306a36Sopenharmony_ci * FSB is changed using the chipset 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic struct pci_dev *nforce2_dev; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* fid: 4062306a36Sopenharmony_ci * multiplier * 10 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_cistatic int fid; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* min_fsb, max_fsb: 4562306a36Sopenharmony_ci * minimum and maximum FSB (= FSB at boot time) 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic int min_fsb; 4862306a36Sopenharmony_cistatic int max_fsb; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciMODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); 5162306a36Sopenharmony_ciMODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver"); 5262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cimodule_param(fid, int, 0444); 5562306a36Sopenharmony_cimodule_param(min_fsb, int, 0444); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciMODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)"); 5862306a36Sopenharmony_ciMODULE_PARM_DESC(min_fsb, 5962306a36Sopenharmony_ci "Minimum FSB to use, if not defined: current FSB - 50"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/** 6262306a36Sopenharmony_ci * nforce2_calc_fsb - calculate FSB 6362306a36Sopenharmony_ci * @pll: PLL value 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * Calculates FSB from PLL value 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_cistatic int nforce2_calc_fsb(int pll) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci unsigned char mul, div; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci mul = (pll >> 8) & 0xff; 7262306a36Sopenharmony_ci div = pll & 0xff; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (div > 0) 7562306a36Sopenharmony_ci return NFORCE2_XTAL * mul / div; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * nforce2_calc_pll - calculate PLL value 8262306a36Sopenharmony_ci * @fsb: FSB 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * Calculate PLL value for given FSB 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic int nforce2_calc_pll(unsigned int fsb) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci unsigned char xmul, xdiv; 8962306a36Sopenharmony_ci unsigned char mul = 0, div = 0; 9062306a36Sopenharmony_ci int tried = 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Try to calculate multiplier and divider up to 4 times */ 9362306a36Sopenharmony_ci while (((mul == 0) || (div == 0)) && (tried <= 3)) { 9462306a36Sopenharmony_ci for (xdiv = 2; xdiv <= 0x80; xdiv++) 9562306a36Sopenharmony_ci for (xmul = 1; xmul <= 0xfe; xmul++) 9662306a36Sopenharmony_ci if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) == 9762306a36Sopenharmony_ci fsb + tried) { 9862306a36Sopenharmony_ci mul = xmul; 9962306a36Sopenharmony_ci div = xdiv; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci tried++; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if ((mul == 0) || (div == 0)) 10562306a36Sopenharmony_ci return -1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return NFORCE2_PLL(mul, div); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/** 11162306a36Sopenharmony_ci * nforce2_write_pll - write PLL value to chipset 11262306a36Sopenharmony_ci * @pll: PLL value 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Writes new FSB PLL value to chipset 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic void nforce2_write_pll(int pll) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci int temp; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Set the pll addr. to 0x00 */ 12162306a36Sopenharmony_ci pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Now write the value in all 64 registers */ 12462306a36Sopenharmony_ci for (temp = 0; temp <= 0x3f; temp++) 12562306a36Sopenharmony_ci pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * nforce2_fsb_read - Read FSB 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * Read FSB from chipset 13262306a36Sopenharmony_ci * If bootfsb != 0, return FSB at boot-time 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistatic unsigned int nforce2_fsb_read(int bootfsb) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct pci_dev *nforce2_sub5; 13762306a36Sopenharmony_ci u32 fsb, temp = 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */ 14062306a36Sopenharmony_ci nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF, 14162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, NULL); 14262306a36Sopenharmony_ci if (!nforce2_sub5) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb); 14662306a36Sopenharmony_ci fsb /= 1000000; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Check if PLL register is already set */ 14962306a36Sopenharmony_ci pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (bootfsb || !temp) 15262306a36Sopenharmony_ci return fsb; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Use PLL register FSB value */ 15562306a36Sopenharmony_ci pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp); 15662306a36Sopenharmony_ci fsb = nforce2_calc_fsb(temp); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return fsb; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * nforce2_set_fsb - set new FSB 16362306a36Sopenharmony_ci * @fsb: New FSB 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * Sets new FSB 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic int nforce2_set_fsb(unsigned int fsb) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 temp = 0; 17062306a36Sopenharmony_ci unsigned int tfsb; 17162306a36Sopenharmony_ci int diff; 17262306a36Sopenharmony_ci int pll = 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) { 17562306a36Sopenharmony_ci pr_err("FSB %d is out of range!\n", fsb); 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci tfsb = nforce2_fsb_read(0); 18062306a36Sopenharmony_ci if (!tfsb) { 18162306a36Sopenharmony_ci pr_err("Error while reading the FSB\n"); 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* First write? Then set actual value */ 18662306a36Sopenharmony_ci pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp); 18762306a36Sopenharmony_ci if (!temp) { 18862306a36Sopenharmony_ci pll = nforce2_calc_pll(tfsb); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (pll < 0) 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci nforce2_write_pll(pll); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Enable write access */ 19762306a36Sopenharmony_ci temp = 0x01; 19862306a36Sopenharmony_ci pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci diff = tfsb - fsb; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!diff) 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) { 20662306a36Sopenharmony_ci if (diff < 0) 20762306a36Sopenharmony_ci tfsb++; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci tfsb--; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Calculate the PLL reg. value */ 21262306a36Sopenharmony_ci pll = nforce2_calc_pll(tfsb); 21362306a36Sopenharmony_ci if (pll == -1) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci nforce2_write_pll(pll); 21762306a36Sopenharmony_ci#ifdef NFORCE2_DELAY 21862306a36Sopenharmony_ci mdelay(NFORCE2_DELAY); 21962306a36Sopenharmony_ci#endif 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci temp = 0x40; 22362306a36Sopenharmony_ci pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * nforce2_get - get the CPU frequency 23062306a36Sopenharmony_ci * @cpu: CPU number 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * Returns the CPU frequency 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistatic unsigned int nforce2_get(unsigned int cpu) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci if (cpu) 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci return nforce2_fsb_read(0) * fid * 100; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/** 24262306a36Sopenharmony_ci * nforce2_target - set a new CPUFreq policy 24362306a36Sopenharmony_ci * @policy: new policy 24462306a36Sopenharmony_ci * @target_freq: the target frequency 24562306a36Sopenharmony_ci * @relation: how that frequency relates to achieved frequency 24662306a36Sopenharmony_ci * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H) 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * Sets a new CPUFreq policy. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic int nforce2_target(struct cpufreq_policy *policy, 25162306a36Sopenharmony_ci unsigned int target_freq, unsigned int relation) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci/* unsigned long flags; */ 25462306a36Sopenharmony_ci struct cpufreq_freqs freqs; 25562306a36Sopenharmony_ci unsigned int target_fsb; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if ((target_freq > policy->max) || (target_freq < policy->min)) 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci target_fsb = target_freq / (fid * 100); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci freqs.old = nforce2_get(policy->cpu); 26362306a36Sopenharmony_ci freqs.new = target_fsb * fid * 100; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (freqs.old == freqs.new) 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci pr_debug("Old CPU frequency %d kHz, new %d kHz\n", 26962306a36Sopenharmony_ci freqs.old, freqs.new); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci cpufreq_freq_transition_begin(policy, &freqs); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Disable IRQs */ 27462306a36Sopenharmony_ci /* local_irq_save(flags); */ 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (nforce2_set_fsb(target_fsb) < 0) 27762306a36Sopenharmony_ci pr_err("Changing FSB to %d failed\n", target_fsb); 27862306a36Sopenharmony_ci else 27962306a36Sopenharmony_ci pr_debug("Changed FSB successfully to %d\n", 28062306a36Sopenharmony_ci target_fsb); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Enable IRQs */ 28362306a36Sopenharmony_ci /* local_irq_restore(flags); */ 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci cpufreq_freq_transition_end(policy, &freqs, 0); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/** 29162306a36Sopenharmony_ci * nforce2_verify - verifies a new CPUFreq policy 29262306a36Sopenharmony_ci * @policy: new policy 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_cistatic int nforce2_verify(struct cpufreq_policy_data *policy) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned int fsb_pol_max; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci fsb_pol_max = policy->max / (fid * 100); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (policy->min < (fsb_pol_max * fid * 100)) 30162306a36Sopenharmony_ci policy->max = (fsb_pol_max + 1) * fid * 100; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci cpufreq_verify_within_cpu_limits(policy); 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int nforce2_cpu_init(struct cpufreq_policy *policy) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci unsigned int fsb; 31062306a36Sopenharmony_ci unsigned int rfid; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* capability check */ 31362306a36Sopenharmony_ci if (policy->cpu != 0) 31462306a36Sopenharmony_ci return -ENODEV; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Get current FSB */ 31762306a36Sopenharmony_ci fsb = nforce2_fsb_read(0); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!fsb) 32062306a36Sopenharmony_ci return -EIO; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* FIX: Get FID from CPU */ 32362306a36Sopenharmony_ci if (!fid) { 32462306a36Sopenharmony_ci if (!cpu_khz) { 32562306a36Sopenharmony_ci pr_warn("cpu_khz not set, can't calculate multiplier!\n"); 32662306a36Sopenharmony_ci return -ENODEV; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci fid = cpu_khz / (fsb * 100); 33062306a36Sopenharmony_ci rfid = fid % 5; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (rfid) { 33362306a36Sopenharmony_ci if (rfid > 2) 33462306a36Sopenharmony_ci fid += 5 - rfid; 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci fid -= rfid; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci pr_info("FSB currently at %i MHz, FID %d.%d\n", 34162306a36Sopenharmony_ci fsb, fid / 10, fid % 10); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Set maximum FSB to FSB at boot time */ 34462306a36Sopenharmony_ci max_fsb = nforce2_fsb_read(1); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!max_fsb) 34762306a36Sopenharmony_ci return -EIO; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!min_fsb) 35062306a36Sopenharmony_ci min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (min_fsb < NFORCE2_MIN_FSB) 35362306a36Sopenharmony_ci min_fsb = NFORCE2_MIN_FSB; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* cpuinfo and default policy values */ 35662306a36Sopenharmony_ci policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; 35762306a36Sopenharmony_ci policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int nforce2_cpu_exit(struct cpufreq_policy *policy) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic struct cpufreq_driver nforce2_driver = { 36862306a36Sopenharmony_ci .name = "nforce2", 36962306a36Sopenharmony_ci .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, 37062306a36Sopenharmony_ci .verify = nforce2_verify, 37162306a36Sopenharmony_ci .target = nforce2_target, 37262306a36Sopenharmony_ci .get = nforce2_get, 37362306a36Sopenharmony_ci .init = nforce2_cpu_init, 37462306a36Sopenharmony_ci .exit = nforce2_cpu_exit, 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci#ifdef MODULE 37862306a36Sopenharmony_cistatic const struct pci_device_id nforce2_ids[] = { 37962306a36Sopenharmony_ci { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 }, 38062306a36Sopenharmony_ci {} 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nforce2_ids); 38362306a36Sopenharmony_ci#endif 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * Detects nForce2 A2 and C1 stepping 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cistatic int nforce2_detect_chipset(void) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 39462306a36Sopenharmony_ci PCI_DEVICE_ID_NVIDIA_NFORCE2, 39562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, NULL); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (nforce2_dev == NULL) 39862306a36Sopenharmony_ci return -ENODEV; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci pr_info("Detected nForce2 chipset revision %X\n", 40162306a36Sopenharmony_ci nforce2_dev->revision); 40262306a36Sopenharmony_ci pr_info("FSB changing is maybe unstable and can lead to crashes and data loss\n"); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/** 40862306a36Sopenharmony_ci * nforce2_init - initializes the nForce2 CPUFreq driver 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported 41162306a36Sopenharmony_ci * devices, -EINVAL on problems during initialization, and zero on 41262306a36Sopenharmony_ci * success. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic int __init nforce2_init(void) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci /* TODO: do we need to detect the processor? */ 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* detect chipset */ 41962306a36Sopenharmony_ci if (nforce2_detect_chipset()) { 42062306a36Sopenharmony_ci pr_info("No nForce2 chipset\n"); 42162306a36Sopenharmony_ci return -ENODEV; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return cpufreq_register_driver(&nforce2_driver); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/** 42862306a36Sopenharmony_ci * nforce2_exit - unregisters cpufreq module 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * Unregisters nForce2 FSB change support. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_cistatic void __exit nforce2_exit(void) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci cpufreq_unregister_driver(&nforce2_driver); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cimodule_init(nforce2_init); 43862306a36Sopenharmony_cimodule_exit(nforce2_exit); 439