brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · fedad10 Raw
433 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * (C) 2004-2006  Sebastian Witt <se.witt@gmx.net>4 *5 *  Based upon reverse engineered information6 *7 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*8 */9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/moduleparam.h>15#include <linux/init.h>16#include <linux/cpufreq.h>17#include <linux/pci.h>18#include <linux/delay.h>19 20#define NFORCE2_XTAL 2521#define NFORCE2_BOOTFSB 0x4822#define NFORCE2_PLLENABLE 0xa823#define NFORCE2_PLLREG 0xa424#define NFORCE2_PLLADR 0xa025#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div)26 27#define NFORCE2_MIN_FSB 5028#define NFORCE2_SAFE_DISTANCE 5029 30/* Delay in ms between FSB changes */31/* #define NFORCE2_DELAY 10 */32 33/*34 * nforce2_chipset:35 * FSB is changed using the chipset36 */37static struct pci_dev *nforce2_dev;38 39/* fid:40 * multiplier * 1041 */42static int fid;43 44/* min_fsb, max_fsb:45 * minimum and maximum FSB (= FSB at boot time)46 */47static int min_fsb;48static int max_fsb;49 50MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");51MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver");52MODULE_LICENSE("GPL");53 54module_param(fid, int, 0444);55module_param(min_fsb, int, 0444);56 57MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");58MODULE_PARM_DESC(min_fsb,59		"Minimum FSB to use, if not defined: current FSB - 50");60 61/**62 * nforce2_calc_fsb - calculate FSB63 * @pll: PLL value64 *65 *   Calculates FSB from PLL value66 */67static int nforce2_calc_fsb(int pll)68{69	unsigned char mul, div;70 71	mul = (pll >> 8) & 0xff;72	div = pll & 0xff;73 74	if (div > 0)75		return NFORCE2_XTAL * mul / div;76 77	return 0;78}79 80/**81 * nforce2_calc_pll - calculate PLL value82 * @fsb: FSB83 *84 *   Calculate PLL value for given FSB85 */86static int nforce2_calc_pll(unsigned int fsb)87{88	unsigned char xmul, xdiv;89	unsigned char mul = 0, div = 0;90	int tried = 0;91 92	/* Try to calculate multiplier and divider up to 4 times */93	while (((mul == 0) || (div == 0)) && (tried <= 3)) {94		for (xdiv = 2; xdiv <= 0x80; xdiv++)95			for (xmul = 1; xmul <= 0xfe; xmul++)96				if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) ==97				    fsb + tried) {98					mul = xmul;99					div = xdiv;100				}101		tried++;102	}103 104	if ((mul == 0) || (div == 0))105		return -1;106 107	return NFORCE2_PLL(mul, div);108}109 110/**111 * nforce2_write_pll - write PLL value to chipset112 * @pll: PLL value113 *114 *   Writes new FSB PLL value to chipset115 */116static void nforce2_write_pll(int pll)117{118	int temp;119 120	/* Set the pll addr. to 0x00 */121	pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0);122 123	/* Now write the value in all 64 registers */124	for (temp = 0; temp <= 0x3f; temp++)125		pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll);126}127 128/**129 * nforce2_fsb_read - Read FSB130 *131 *   Read FSB from chipset132 *   If bootfsb != 0, return FSB at boot-time133 */134static unsigned int nforce2_fsb_read(int bootfsb)135{136	struct pci_dev *nforce2_sub5;137	u32 fsb, temp = 0;138 139	/* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */140	nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF,141				PCI_ANY_ID, PCI_ANY_ID, NULL);142	if (!nforce2_sub5)143		return 0;144 145	pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);146	fsb /= 1000000;147 148	/* Check if PLL register is already set */149	pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);150 151	if (bootfsb || !temp)152		return fsb;153 154	/* Use PLL register FSB value */155	pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp);156	fsb = nforce2_calc_fsb(temp);157 158	return fsb;159}160 161/**162 * nforce2_set_fsb - set new FSB163 * @fsb: New FSB164 *165 *   Sets new FSB166 */167static int nforce2_set_fsb(unsigned int fsb)168{169	u32 temp = 0;170	unsigned int tfsb;171	int diff;172	int pll = 0;173 174	if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {175		pr_err("FSB %d is out of range!\n", fsb);176		return -EINVAL;177	}178 179	tfsb = nforce2_fsb_read(0);180	if (!tfsb) {181		pr_err("Error while reading the FSB\n");182		return -EINVAL;183	}184 185	/* First write? Then set actual value */186	pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);187	if (!temp) {188		pll = nforce2_calc_pll(tfsb);189 190		if (pll < 0)191			return -EINVAL;192 193		nforce2_write_pll(pll);194	}195 196	/* Enable write access */197	temp = 0x01;198	pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp);199 200	diff = tfsb - fsb;201 202	if (!diff)203		return 0;204 205	while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) {206		if (diff < 0)207			tfsb++;208		else209			tfsb--;210 211		/* Calculate the PLL reg. value */212		pll = nforce2_calc_pll(tfsb);213		if (pll == -1)214			return -EINVAL;215 216		nforce2_write_pll(pll);217#ifdef NFORCE2_DELAY218		mdelay(NFORCE2_DELAY);219#endif220	}221 222	temp = 0x40;223	pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp);224 225	return 0;226}227 228/**229 * nforce2_get - get the CPU frequency230 * @cpu: CPU number231 *232 * Returns the CPU frequency233 */234static unsigned int nforce2_get(unsigned int cpu)235{236	if (cpu)237		return 0;238	return nforce2_fsb_read(0) * fid * 100;239}240 241/**242 * nforce2_target - set a new CPUFreq policy243 * @policy: new policy244 * @target_freq: the target frequency245 * @relation: how that frequency relates to achieved frequency246 *  (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)247 *248 * Sets a new CPUFreq policy.249 */250static int nforce2_target(struct cpufreq_policy *policy,251			  unsigned int target_freq, unsigned int relation)252{253/*        unsigned long         flags; */254	struct cpufreq_freqs freqs;255	unsigned int target_fsb;256 257	if ((target_freq > policy->max) || (target_freq < policy->min))258		return -EINVAL;259 260	target_fsb = target_freq / (fid * 100);261 262	freqs.old = nforce2_get(policy->cpu);263	freqs.new = target_fsb * fid * 100;264 265	if (freqs.old == freqs.new)266		return 0;267 268	pr_debug("Old CPU frequency %d kHz, new %d kHz\n",269	       freqs.old, freqs.new);270 271	cpufreq_freq_transition_begin(policy, &freqs);272 273	/* Disable IRQs */274	/* local_irq_save(flags); */275 276	if (nforce2_set_fsb(target_fsb) < 0)277		pr_err("Changing FSB to %d failed\n", target_fsb);278	else279		pr_debug("Changed FSB successfully to %d\n",280			target_fsb);281 282	/* Enable IRQs */283	/* local_irq_restore(flags); */284 285	cpufreq_freq_transition_end(policy, &freqs, 0);286 287	return 0;288}289 290/**291 * nforce2_verify - verifies a new CPUFreq policy292 * @policy: new policy293 */294static int nforce2_verify(struct cpufreq_policy_data *policy)295{296	unsigned int fsb_pol_max;297 298	fsb_pol_max = policy->max / (fid * 100);299 300	if (policy->min < (fsb_pol_max * fid * 100))301		policy->max = (fsb_pol_max + 1) * fid * 100;302 303	cpufreq_verify_within_cpu_limits(policy);304	return 0;305}306 307static int nforce2_cpu_init(struct cpufreq_policy *policy)308{309	unsigned int fsb;310	unsigned int rfid;311 312	/* capability check */313	if (policy->cpu != 0)314		return -ENODEV;315 316	/* Get current FSB */317	fsb = nforce2_fsb_read(0);318 319	if (!fsb)320		return -EIO;321 322	/* FIX: Get FID from CPU */323	if (!fid) {324		if (!cpu_khz) {325			pr_warn("cpu_khz not set, can't calculate multiplier!\n");326			return -ENODEV;327		}328 329		fid = cpu_khz / (fsb * 100);330		rfid = fid % 5;331 332		if (rfid) {333			if (rfid > 2)334				fid += 5 - rfid;335			else336				fid -= rfid;337		}338	}339 340	pr_info("FSB currently at %i MHz, FID %d.%d\n",341		fsb, fid / 10, fid % 10);342 343	/* Set maximum FSB to FSB at boot time */344	max_fsb = nforce2_fsb_read(1);345 346	if (!max_fsb)347		return -EIO;348 349	if (!min_fsb)350		min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;351 352	if (min_fsb < NFORCE2_MIN_FSB)353		min_fsb = NFORCE2_MIN_FSB;354 355	/* cpuinfo and default policy values */356	policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100;357	policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100;358 359	return 0;360}361 362static struct cpufreq_driver nforce2_driver = {363	.name = "nforce2",364	.flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,365	.verify = nforce2_verify,366	.target = nforce2_target,367	.get = nforce2_get,368	.init = nforce2_cpu_init,369};370 371#ifdef MODULE372static const struct pci_device_id nforce2_ids[] = {373	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },374	{}375};376MODULE_DEVICE_TABLE(pci, nforce2_ids);377#endif378 379/**380 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic381 *382 * Detects nForce2 A2 and C1 stepping383 *384 */385static int nforce2_detect_chipset(void)386{387	nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,388					PCI_DEVICE_ID_NVIDIA_NFORCE2,389					PCI_ANY_ID, PCI_ANY_ID, NULL);390 391	if (nforce2_dev == NULL)392		return -ENODEV;393 394	pr_info("Detected nForce2 chipset revision %X\n",395		nforce2_dev->revision);396	pr_info("FSB changing is maybe unstable and can lead to crashes and data loss\n");397 398	return 0;399}400 401/**402 * nforce2_init - initializes the nForce2 CPUFreq driver403 *404 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported405 * devices, -EINVAL on problems during initialization, and zero on406 * success.407 */408static int __init nforce2_init(void)409{410	/* TODO: do we need to detect the processor? */411 412	/* detect chipset */413	if (nforce2_detect_chipset()) {414		pr_info("No nForce2 chipset\n");415		return -ENODEV;416	}417 418	return cpufreq_register_driver(&nforce2_driver);419}420 421/**422 * nforce2_exit - unregisters cpufreq module423 *424 *   Unregisters nForce2 FSB change support.425 */426static void __exit nforce2_exit(void)427{428	cpufreq_unregister_driver(&nforce2_driver);429}430 431module_init(nforce2_init);432module_exit(nforce2_exit);433