linux/drivers/cpufreq/cpufreq-nforce2.c
<<
>>
Prefs
   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);
 439