18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) 2004-2006 Sebastian Witt <se.witt@gmx.net> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based upon reverse engineered information 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define NFORCE2_XTAL 25 218c2ecf20Sopenharmony_ci#define NFORCE2_BOOTFSB 0x48 228c2ecf20Sopenharmony_ci#define NFORCE2_PLLENABLE 0xa8 238c2ecf20Sopenharmony_ci#define NFORCE2_PLLREG 0xa4 248c2ecf20Sopenharmony_ci#define NFORCE2_PLLADR 0xa0 258c2ecf20Sopenharmony_ci#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define NFORCE2_MIN_FSB 50 288c2ecf20Sopenharmony_ci#define NFORCE2_SAFE_DISTANCE 50 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Delay in ms between FSB changes */ 318c2ecf20Sopenharmony_ci/* #define NFORCE2_DELAY 10 */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * nforce2_chipset: 358c2ecf20Sopenharmony_ci * FSB is changed using the chipset 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic struct pci_dev *nforce2_dev; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* fid: 408c2ecf20Sopenharmony_ci * multiplier * 10 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic int fid; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* min_fsb, max_fsb: 458c2ecf20Sopenharmony_ci * minimum and maximum FSB (= FSB at boot time) 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistatic int min_fsb; 488c2ecf20Sopenharmony_cistatic int max_fsb; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); 518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver"); 528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cimodule_param(fid, int, 0444); 558c2ecf20Sopenharmony_cimodule_param(min_fsb, int, 0444); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)"); 588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(min_fsb, 598c2ecf20Sopenharmony_ci "Minimum FSB to use, if not defined: current FSB - 50"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/** 628c2ecf20Sopenharmony_ci * nforce2_calc_fsb - calculate FSB 638c2ecf20Sopenharmony_ci * @pll: PLL value 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Calculates FSB from PLL value 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic int nforce2_calc_fsb(int pll) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci unsigned char mul, div; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mul = (pll >> 8) & 0xff; 728c2ecf20Sopenharmony_ci div = pll & 0xff; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (div > 0) 758c2ecf20Sopenharmony_ci return NFORCE2_XTAL * mul / div; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * nforce2_calc_pll - calculate PLL value 828c2ecf20Sopenharmony_ci * @fsb: FSB 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Calculate PLL value for given FSB 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic int nforce2_calc_pll(unsigned int fsb) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned char xmul, xdiv; 898c2ecf20Sopenharmony_ci unsigned char mul = 0, div = 0; 908c2ecf20Sopenharmony_ci int tried = 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Try to calculate multiplier and divider up to 4 times */ 938c2ecf20Sopenharmony_ci while (((mul == 0) || (div == 0)) && (tried <= 3)) { 948c2ecf20Sopenharmony_ci for (xdiv = 2; xdiv <= 0x80; xdiv++) 958c2ecf20Sopenharmony_ci for (xmul = 1; xmul <= 0xfe; xmul++) 968c2ecf20Sopenharmony_ci if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) == 978c2ecf20Sopenharmony_ci fsb + tried) { 988c2ecf20Sopenharmony_ci mul = xmul; 998c2ecf20Sopenharmony_ci div = xdiv; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci tried++; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if ((mul == 0) || (div == 0)) 1058c2ecf20Sopenharmony_ci return -1; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return NFORCE2_PLL(mul, div); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * nforce2_write_pll - write PLL value to chipset 1128c2ecf20Sopenharmony_ci * @pll: PLL value 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * Writes new FSB PLL value to chipset 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic void nforce2_write_pll(int pll) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int temp; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Set the pll addr. to 0x00 */ 1218c2ecf20Sopenharmony_ci pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Now write the value in all 64 registers */ 1248c2ecf20Sopenharmony_ci for (temp = 0; temp <= 0x3f; temp++) 1258c2ecf20Sopenharmony_ci pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/** 1298c2ecf20Sopenharmony_ci * nforce2_fsb_read - Read FSB 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Read FSB from chipset 1328c2ecf20Sopenharmony_ci * If bootfsb != 0, return FSB at boot-time 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistatic unsigned int nforce2_fsb_read(int bootfsb) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct pci_dev *nforce2_sub5; 1378c2ecf20Sopenharmony_ci u32 fsb, temp = 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */ 1408c2ecf20Sopenharmony_ci nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF, 1418c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, NULL); 1428c2ecf20Sopenharmony_ci if (!nforce2_sub5) 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb); 1468c2ecf20Sopenharmony_ci fsb /= 1000000; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Check if PLL register is already set */ 1498c2ecf20Sopenharmony_ci pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (bootfsb || !temp) 1528c2ecf20Sopenharmony_ci return fsb; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Use PLL register FSB value */ 1558c2ecf20Sopenharmony_ci pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp); 1568c2ecf20Sopenharmony_ci fsb = nforce2_calc_fsb(temp); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return fsb; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * nforce2_set_fsb - set new FSB 1638c2ecf20Sopenharmony_ci * @fsb: New FSB 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Sets new FSB 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic int nforce2_set_fsb(unsigned int fsb) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci u32 temp = 0; 1708c2ecf20Sopenharmony_ci unsigned int tfsb; 1718c2ecf20Sopenharmony_ci int diff; 1728c2ecf20Sopenharmony_ci int pll = 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) { 1758c2ecf20Sopenharmony_ci pr_err("FSB %d is out of range!\n", fsb); 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci tfsb = nforce2_fsb_read(0); 1808c2ecf20Sopenharmony_ci if (!tfsb) { 1818c2ecf20Sopenharmony_ci pr_err("Error while reading the FSB\n"); 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* First write? Then set actual value */ 1868c2ecf20Sopenharmony_ci pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp); 1878c2ecf20Sopenharmony_ci if (!temp) { 1888c2ecf20Sopenharmony_ci pll = nforce2_calc_pll(tfsb); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (pll < 0) 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci nforce2_write_pll(pll); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Enable write access */ 1978c2ecf20Sopenharmony_ci temp = 0x01; 1988c2ecf20Sopenharmony_ci pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci diff = tfsb - fsb; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!diff) 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) { 2068c2ecf20Sopenharmony_ci if (diff < 0) 2078c2ecf20Sopenharmony_ci tfsb++; 2088c2ecf20Sopenharmony_ci else 2098c2ecf20Sopenharmony_ci tfsb--; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Calculate the PLL reg. value */ 2128c2ecf20Sopenharmony_ci pll = nforce2_calc_pll(tfsb); 2138c2ecf20Sopenharmony_ci if (pll == -1) 2148c2ecf20Sopenharmony_ci return -EINVAL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci nforce2_write_pll(pll); 2178c2ecf20Sopenharmony_ci#ifdef NFORCE2_DELAY 2188c2ecf20Sopenharmony_ci mdelay(NFORCE2_DELAY); 2198c2ecf20Sopenharmony_ci#endif 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci temp = 0x40; 2238c2ecf20Sopenharmony_ci pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/** 2298c2ecf20Sopenharmony_ci * nforce2_get - get the CPU frequency 2308c2ecf20Sopenharmony_ci * @cpu: CPU number 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Returns the CPU frequency 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic unsigned int nforce2_get(unsigned int cpu) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci if (cpu) 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci return nforce2_fsb_read(0) * fid * 100; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/** 2428c2ecf20Sopenharmony_ci * nforce2_target - set a new CPUFreq policy 2438c2ecf20Sopenharmony_ci * @policy: new policy 2448c2ecf20Sopenharmony_ci * @target_freq: the target frequency 2458c2ecf20Sopenharmony_ci * @relation: how that frequency relates to achieved frequency 2468c2ecf20Sopenharmony_ci * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H) 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Sets a new CPUFreq policy. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic int nforce2_target(struct cpufreq_policy *policy, 2518c2ecf20Sopenharmony_ci unsigned int target_freq, unsigned int relation) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci/* unsigned long flags; */ 2548c2ecf20Sopenharmony_ci struct cpufreq_freqs freqs; 2558c2ecf20Sopenharmony_ci unsigned int target_fsb; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if ((target_freq > policy->max) || (target_freq < policy->min)) 2588c2ecf20Sopenharmony_ci return -EINVAL; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci target_fsb = target_freq / (fid * 100); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci freqs.old = nforce2_get(policy->cpu); 2638c2ecf20Sopenharmony_ci freqs.new = target_fsb * fid * 100; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (freqs.old == freqs.new) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci pr_debug("Old CPU frequency %d kHz, new %d kHz\n", 2698c2ecf20Sopenharmony_ci freqs.old, freqs.new); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci cpufreq_freq_transition_begin(policy, &freqs); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Disable IRQs */ 2748c2ecf20Sopenharmony_ci /* local_irq_save(flags); */ 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (nforce2_set_fsb(target_fsb) < 0) 2778c2ecf20Sopenharmony_ci pr_err("Changing FSB to %d failed\n", target_fsb); 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci pr_debug("Changed FSB successfully to %d\n", 2808c2ecf20Sopenharmony_ci target_fsb); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Enable IRQs */ 2838c2ecf20Sopenharmony_ci /* local_irq_restore(flags); */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci cpufreq_freq_transition_end(policy, &freqs, 0); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/** 2918c2ecf20Sopenharmony_ci * nforce2_verify - verifies a new CPUFreq policy 2928c2ecf20Sopenharmony_ci * @policy: new policy 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic int nforce2_verify(struct cpufreq_policy_data *policy) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci unsigned int fsb_pol_max; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci fsb_pol_max = policy->max / (fid * 100); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (policy->min < (fsb_pol_max * fid * 100)) 3018c2ecf20Sopenharmony_ci policy->max = (fsb_pol_max + 1) * fid * 100; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci cpufreq_verify_within_cpu_limits(policy); 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int nforce2_cpu_init(struct cpufreq_policy *policy) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci unsigned int fsb; 3108c2ecf20Sopenharmony_ci unsigned int rfid; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* capability check */ 3138c2ecf20Sopenharmony_ci if (policy->cpu != 0) 3148c2ecf20Sopenharmony_ci return -ENODEV; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Get current FSB */ 3178c2ecf20Sopenharmony_ci fsb = nforce2_fsb_read(0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!fsb) 3208c2ecf20Sopenharmony_ci return -EIO; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* FIX: Get FID from CPU */ 3238c2ecf20Sopenharmony_ci if (!fid) { 3248c2ecf20Sopenharmony_ci if (!cpu_khz) { 3258c2ecf20Sopenharmony_ci pr_warn("cpu_khz not set, can't calculate multiplier!\n"); 3268c2ecf20Sopenharmony_ci return -ENODEV; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci fid = cpu_khz / (fsb * 100); 3308c2ecf20Sopenharmony_ci rfid = fid % 5; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (rfid) { 3338c2ecf20Sopenharmony_ci if (rfid > 2) 3348c2ecf20Sopenharmony_ci fid += 5 - rfid; 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci fid -= rfid; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci pr_info("FSB currently at %i MHz, FID %d.%d\n", 3418c2ecf20Sopenharmony_ci fsb, fid / 10, fid % 10); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Set maximum FSB to FSB at boot time */ 3448c2ecf20Sopenharmony_ci max_fsb = nforce2_fsb_read(1); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!max_fsb) 3478c2ecf20Sopenharmony_ci return -EIO; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!min_fsb) 3508c2ecf20Sopenharmony_ci min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (min_fsb < NFORCE2_MIN_FSB) 3538c2ecf20Sopenharmony_ci min_fsb = NFORCE2_MIN_FSB; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* cpuinfo and default policy values */ 3568c2ecf20Sopenharmony_ci policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; 3578c2ecf20Sopenharmony_ci policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int nforce2_cpu_exit(struct cpufreq_policy *policy) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic struct cpufreq_driver nforce2_driver = { 3688c2ecf20Sopenharmony_ci .name = "nforce2", 3698c2ecf20Sopenharmony_ci .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, 3708c2ecf20Sopenharmony_ci .verify = nforce2_verify, 3718c2ecf20Sopenharmony_ci .target = nforce2_target, 3728c2ecf20Sopenharmony_ci .get = nforce2_get, 3738c2ecf20Sopenharmony_ci .init = nforce2_cpu_init, 3748c2ecf20Sopenharmony_ci .exit = nforce2_cpu_exit, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci#ifdef MODULE 3788c2ecf20Sopenharmony_cistatic const struct pci_device_id nforce2_ids[] = { 3798c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 }, 3808c2ecf20Sopenharmony_ci {} 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nforce2_ids); 3838c2ecf20Sopenharmony_ci#endif 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/** 3868c2ecf20Sopenharmony_ci * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * Detects nForce2 A2 and C1 stepping 3898c2ecf20Sopenharmony_ci * 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_cistatic int nforce2_detect_chipset(void) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 3948c2ecf20Sopenharmony_ci PCI_DEVICE_ID_NVIDIA_NFORCE2, 3958c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, NULL); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (nforce2_dev == NULL) 3988c2ecf20Sopenharmony_ci return -ENODEV; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci pr_info("Detected nForce2 chipset revision %X\n", 4018c2ecf20Sopenharmony_ci nforce2_dev->revision); 4028c2ecf20Sopenharmony_ci pr_info("FSB changing is maybe unstable and can lead to crashes and data loss\n"); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/** 4088c2ecf20Sopenharmony_ci * nforce2_init - initializes the nForce2 CPUFreq driver 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported 4118c2ecf20Sopenharmony_ci * devices, -EINVAL on problems during initialization, and zero on 4128c2ecf20Sopenharmony_ci * success. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic int __init nforce2_init(void) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci /* TODO: do we need to detect the processor? */ 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* detect chipset */ 4198c2ecf20Sopenharmony_ci if (nforce2_detect_chipset()) { 4208c2ecf20Sopenharmony_ci pr_info("No nForce2 chipset\n"); 4218c2ecf20Sopenharmony_ci return -ENODEV; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return cpufreq_register_driver(&nforce2_driver); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * nforce2_exit - unregisters cpufreq module 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * Unregisters nForce2 FSB change support. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistatic void __exit nforce2_exit(void) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci cpufreq_unregister_driver(&nforce2_driver); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cimodule_init(nforce2_init); 4388c2ecf20Sopenharmony_cimodule_exit(nforce2_exit); 439