cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cpufreq-nforce2.c (9185B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * (C) 2004-2006  Sebastian Witt <se.witt@gmx.net>
      4 *
      5 *  Based upon reverse engineered information
      6 *
      7 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
      8 */
      9
     10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     11
     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 25
     21#define NFORCE2_BOOTFSB 0x48
     22#define NFORCE2_PLLENABLE 0xa8
     23#define NFORCE2_PLLREG 0xa4
     24#define NFORCE2_PLLADR 0xa0
     25#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div)
     26
     27#define NFORCE2_MIN_FSB 50
     28#define NFORCE2_SAFE_DISTANCE 50
     29
     30/* Delay in ms between FSB changes */
     31/* #define NFORCE2_DELAY 10 */
     32
     33/*
     34 * nforce2_chipset:
     35 * FSB is changed using the chipset
     36 */
     37static struct pci_dev *nforce2_dev;
     38
     39/* fid:
     40 * multiplier * 10
     41 */
     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 FSB
     63 * @pll: PLL value
     64 *
     65 *   Calculates FSB from PLL value
     66 */
     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 value
     82 * @fsb: FSB
     83 *
     84 *   Calculate PLL value for given FSB
     85 */
     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 chipset
    112 * @pll: PLL value
    113 *
    114 *   Writes new FSB PLL value to chipset
    115 */
    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 FSB
    130 *
    131 *   Read FSB from chipset
    132 *   If bootfsb != 0, return FSB at boot-time
    133 */
    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 FSB
    163 * @fsb: New FSB
    164 *
    165 *   Sets new FSB
    166 */
    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		else
    209			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_DELAY
    218		mdelay(NFORCE2_DELAY);
    219#endif
    220	}
    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 frequency
    230 * @cpu: CPU number
    231 *
    232 * Returns the CPU frequency
    233 */
    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 policy
    243 * @policy: new policy
    244 * @target_freq: the target frequency
    245 * @relation: how that frequency relates to achieved frequency
    246 *  (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	else
    279		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 policy
    292 * @policy: new policy
    293 */
    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			else
    336				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 int nforce2_cpu_exit(struct cpufreq_policy *policy)
    363{
    364	return 0;
    365}
    366
    367static struct cpufreq_driver nforce2_driver = {
    368	.name = "nforce2",
    369	.flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
    370	.verify = nforce2_verify,
    371	.target = nforce2_target,
    372	.get = nforce2_get,
    373	.init = nforce2_cpu_init,
    374	.exit = nforce2_cpu_exit,
    375};
    376
    377#ifdef MODULE
    378static const struct pci_device_id nforce2_ids[] = {
    379	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
    380	{}
    381};
    382MODULE_DEVICE_TABLE(pci, nforce2_ids);
    383#endif
    384
    385/**
    386 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
    387 *
    388 * Detects nForce2 A2 and C1 stepping
    389 *
    390 */
    391static int nforce2_detect_chipset(void)
    392{
    393	nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
    394					PCI_DEVICE_ID_NVIDIA_NFORCE2,
    395					PCI_ANY_ID, PCI_ANY_ID, NULL);
    396
    397	if (nforce2_dev == NULL)
    398		return -ENODEV;
    399
    400	pr_info("Detected nForce2 chipset revision %X\n",
    401		nforce2_dev->revision);
    402	pr_info("FSB changing is maybe unstable and can lead to crashes and data loss\n");
    403
    404	return 0;
    405}
    406
    407/**
    408 * nforce2_init - initializes the nForce2 CPUFreq driver
    409 *
    410 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
    411 * devices, -EINVAL on problems during initialization, and zero on
    412 * success.
    413 */
    414static int __init nforce2_init(void)
    415{
    416	/* TODO: do we need to detect the processor? */
    417
    418	/* detect chipset */
    419	if (nforce2_detect_chipset()) {
    420		pr_info("No nForce2 chipset\n");
    421		return -ENODEV;
    422	}
    423
    424	return cpufreq_register_driver(&nforce2_driver);
    425}
    426
    427/**
    428 * nforce2_exit - unregisters cpufreq module
    429 *
    430 *   Unregisters nForce2 FSB change support.
    431 */
    432static void __exit nforce2_exit(void)
    433{
    434	cpufreq_unregister_driver(&nforce2_driver);
    435}
    436
    437module_init(nforce2_init);
    438module_exit(nforce2_exit);