linux/drivers/cpufreq/cpufreq-nforce2.c
<<
>>
Prefs
   1/*
   2 * (C) 2004-2006  Sebastian Witt <se.witt@gmx.net>
   3 *
   4 *  Licensed under the terms of the GNU GPL License version 2.
   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        return;
 128}
 129
 130/**
 131 * nforce2_fsb_read - Read FSB
 132 *
 133 *   Read FSB from chipset
 134 *   If bootfsb != 0, return FSB at boot-time
 135 */
 136static unsigned int nforce2_fsb_read(int bootfsb)
 137{
 138        struct pci_dev *nforce2_sub5;
 139        u32 fsb, temp = 0;
 140
 141        /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
 142        nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF,
 143                                PCI_ANY_ID, PCI_ANY_ID, NULL);
 144        if (!nforce2_sub5)
 145                return 0;
 146
 147        pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
 148        fsb /= 1000000;
 149
 150        /* Check if PLL register is already set */
 151        pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
 152
 153        if (bootfsb || !temp)
 154                return fsb;
 155
 156        /* Use PLL register FSB value */
 157        pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp);
 158        fsb = nforce2_calc_fsb(temp);
 159
 160        return fsb;
 161}
 162
 163/**
 164 * nforce2_set_fsb - set new FSB
 165 * @fsb: New FSB
 166 *
 167 *   Sets new FSB
 168 */
 169static int nforce2_set_fsb(unsigned int fsb)
 170{
 171        u32 temp = 0;
 172        unsigned int tfsb;
 173        int diff;
 174        int pll = 0;
 175
 176        if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
 177                pr_err("FSB %d is out of range!\n", fsb);
 178                return -EINVAL;
 179        }
 180
 181        tfsb = nforce2_fsb_read(0);
 182        if (!tfsb) {
 183                pr_err("Error while reading the FSB\n");
 184                return -EINVAL;
 185        }
 186
 187        /* First write? Then set actual value */
 188        pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
 189        if (!temp) {
 190                pll = nforce2_calc_pll(tfsb);
 191
 192                if (pll < 0)
 193                        return -EINVAL;
 194
 195                nforce2_write_pll(pll);
 196        }
 197
 198        /* Enable write access */
 199        temp = 0x01;
 200        pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp);
 201
 202        diff = tfsb - fsb;
 203
 204        if (!diff)
 205                return 0;
 206
 207        while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) {
 208                if (diff < 0)
 209                        tfsb++;
 210                else
 211                        tfsb--;
 212
 213                /* Calculate the PLL reg. value */
 214                pll = nforce2_calc_pll(tfsb);
 215                if (pll == -1)
 216                        return -EINVAL;
 217
 218                nforce2_write_pll(pll);
 219#ifdef NFORCE2_DELAY
 220                mdelay(NFORCE2_DELAY);
 221#endif
 222        }
 223
 224        temp = 0x40;
 225        pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp);
 226
 227        return 0;
 228}
 229
 230/**
 231 * nforce2_get - get the CPU frequency
 232 * @cpu: CPU number
 233 *
 234 * Returns the CPU frequency
 235 */
 236static unsigned int nforce2_get(unsigned int cpu)
 237{
 238        if (cpu)
 239                return 0;
 240        return nforce2_fsb_read(0) * fid * 100;
 241}
 242
 243/**
 244 * nforce2_target - set a new CPUFreq policy
 245 * @policy: new policy
 246 * @target_freq: the target frequency
 247 * @relation: how that frequency relates to achieved frequency
 248 *  (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 249 *
 250 * Sets a new CPUFreq policy.
 251 */
 252static int nforce2_target(struct cpufreq_policy *policy,
 253                          unsigned int target_freq, unsigned int relation)
 254{
 255/*        unsigned long         flags; */
 256        struct cpufreq_freqs freqs;
 257        unsigned int target_fsb;
 258
 259        if ((target_freq > policy->max) || (target_freq < policy->min))
 260                return -EINVAL;
 261
 262        target_fsb = target_freq / (fid * 100);
 263
 264        freqs.old = nforce2_get(policy->cpu);
 265        freqs.new = target_fsb * fid * 100;
 266
 267        if (freqs.old == freqs.new)
 268                return 0;
 269
 270        pr_debug("Old CPU frequency %d kHz, new %d kHz\n",
 271               freqs.old, freqs.new);
 272
 273        cpufreq_freq_transition_begin(policy, &freqs);
 274
 275        /* Disable IRQs */
 276        /* local_irq_save(flags); */
 277
 278        if (nforce2_set_fsb(target_fsb) < 0)
 279                pr_err("Changing FSB to %d failed\n", target_fsb);
 280        else
 281                pr_debug("Changed FSB successfully to %d\n",
 282                        target_fsb);
 283
 284        /* Enable IRQs */
 285        /* local_irq_restore(flags); */
 286
 287        cpufreq_freq_transition_end(policy, &freqs, 0);
 288
 289        return 0;
 290}
 291
 292/**
 293 * nforce2_verify - verifies a new CPUFreq policy
 294 * @policy: new policy
 295 */
 296static int nforce2_verify(struct cpufreq_policy *policy)
 297{
 298        unsigned int fsb_pol_max;
 299
 300        fsb_pol_max = policy->max / (fid * 100);
 301
 302        if (policy->min < (fsb_pol_max * fid * 100))
 303                policy->max = (fsb_pol_max + 1) * fid * 100;
 304
 305        cpufreq_verify_within_cpu_limits(policy);
 306        return 0;
 307}
 308
 309static int nforce2_cpu_init(struct cpufreq_policy *policy)
 310{
 311        unsigned int fsb;
 312        unsigned int rfid;
 313
 314        /* capability check */
 315        if (policy->cpu != 0)
 316                return -ENODEV;
 317
 318        /* Get current FSB */
 319        fsb = nforce2_fsb_read(0);
 320
 321        if (!fsb)
 322                return -EIO;
 323
 324        /* FIX: Get FID from CPU */
 325        if (!fid) {
 326                if (!cpu_khz) {
 327                        pr_warn("cpu_khz not set, can't calculate multiplier!\n");
 328                        return -ENODEV;
 329                }
 330
 331                fid = cpu_khz / (fsb * 100);
 332                rfid = fid % 5;
 333
 334                if (rfid) {
 335                        if (rfid > 2)
 336                                fid += 5 - rfid;
 337                        else
 338                                fid -= rfid;
 339                }
 340        }
 341
 342        pr_info("FSB currently at %i MHz, FID %d.%d\n",
 343                fsb, fid / 10, fid % 10);
 344
 345        /* Set maximum FSB to FSB at boot time */
 346        max_fsb = nforce2_fsb_read(1);
 347
 348        if (!max_fsb)
 349                return -EIO;
 350
 351        if (!min_fsb)
 352                min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
 353
 354        if (min_fsb < NFORCE2_MIN_FSB)
 355                min_fsb = NFORCE2_MIN_FSB;
 356
 357        /* cpuinfo and default policy values */
 358        policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100;
 359        policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100;
 360        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 361
 362        return 0;
 363}
 364
 365static int nforce2_cpu_exit(struct cpufreq_policy *policy)
 366{
 367        return 0;
 368}
 369
 370static struct cpufreq_driver nforce2_driver = {
 371        .name = "nforce2",
 372        .verify = nforce2_verify,
 373        .target = nforce2_target,
 374        .get = nforce2_get,
 375        .init = nforce2_cpu_init,
 376        .exit = nforce2_cpu_exit,
 377};
 378
 379#ifdef MODULE
 380static const struct pci_device_id nforce2_ids[] = {
 381        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
 382        {}
 383};
 384MODULE_DEVICE_TABLE(pci, nforce2_ids);
 385#endif
 386
 387/**
 388 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
 389 *
 390 * Detects nForce2 A2 and C1 stepping
 391 *
 392 */
 393static int nforce2_detect_chipset(void)
 394{
 395        nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
 396                                        PCI_DEVICE_ID_NVIDIA_NFORCE2,
 397                                        PCI_ANY_ID, PCI_ANY_ID, NULL);
 398
 399        if (nforce2_dev == NULL)
 400                return -ENODEV;
 401
 402        pr_info("Detected nForce2 chipset revision %X\n",
 403                nforce2_dev->revision);
 404        pr_info("FSB changing is maybe unstable and can lead to crashes and data loss\n");
 405
 406        return 0;
 407}
 408
 409/**
 410 * nforce2_init - initializes the nForce2 CPUFreq driver
 411 *
 412 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
 413 * devices, -EINVAL on problems during initialization, and zero on
 414 * success.
 415 */
 416static int __init nforce2_init(void)
 417{
 418        /* TODO: do we need to detect the processor? */
 419
 420        /* detect chipset */
 421        if (nforce2_detect_chipset()) {
 422                pr_info("No nForce2 chipset\n");
 423                return -ENODEV;
 424        }
 425
 426        return cpufreq_register_driver(&nforce2_driver);
 427}
 428
 429/**
 430 * nforce2_exit - unregisters cpufreq module
 431 *
 432 *   Unregisters nForce2 FSB change support.
 433 */
 434static void __exit nforce2_exit(void)
 435{
 436        cpufreq_unregister_driver(&nforce2_driver);
 437}
 438
 439module_init(nforce2_init);
 440module_exit(nforce2_exit);
 441
 442
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.