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