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