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#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/moduleparam.h>
  13#include <linux/init.h>
  14#include <linux/cpufreq.h>
  15#include <linux/pci.h>
  16#include <linux/delay.h>
  17
  18#define NFORCE2_XTAL 25
  19#define NFORCE2_BOOTFSB 0x48
  20#define NFORCE2_PLLENABLE 0xa8
  21#define NFORCE2_PLLREG 0xa4
  22#define NFORCE2_PLLADR 0xa0
  23#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div)
  24
  25#define NFORCE2_MIN_FSB 50
  26#define NFORCE2_SAFE_DISTANCE 50
  27
  28/* Delay in ms between FSB changes */
  29/* #define NFORCE2_DELAY 10 */
  30
  31/*
  32 * nforce2_chipset:
  33 * FSB is changed using the chipset
  34 */
  35static struct pci_dev *nforce2_dev;
  36
  37/* fid:
  38 * multiplier * 10
  39 */
  40static int fid;
  41
  42/* min_fsb, max_fsb:
  43 * minimum and maximum FSB (= FSB at boot time)
  44 */
  45static int min_fsb;
  46static int max_fsb;
  47
  48MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
  49MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver");
  50MODULE_LICENSE("GPL");
  51
  52module_param(fid, int, 0444);
  53module_param(min_fsb, int, 0444);
  54
  55MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");
  56MODULE_PARM_DESC(min_fsb,
  57                "Minimum FSB to use, if not defined: current FSB - 50");
  58
  59#define PFX "cpufreq-nforce2: "
  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                printk(KERN_ERR PFX "FSB %d is out of range!\n", fsb);
 178                return -EINVAL;
 179        }
 180
 181        tfsb = nforce2_fsb_read(0);
 182        if (!tfsb) {
 183                printk(KERN_ERR PFX "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        freqs.cpu = 0;          /* Only one CPU on nForce2 platforms */
 267
 268        if (freqs.old == freqs.new)
 269                return 0;
 270
 271        pr_debug("Old CPU frequency %d kHz, new %d kHz\n",
 272               freqs.old, freqs.new);
 273
 274        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 275
 276        /* Disable IRQs */
 277        /* local_irq_save(flags); */
 278
 279        if (nforce2_set_fsb(target_fsb) < 0)
 280                printk(KERN_ERR PFX "Changing FSB to %d failed\n",
 281                        target_fsb);
 282        else
 283                pr_debug("Changed FSB successfully to %d\n",
 284                        target_fsb);
 285
 286        /* Enable IRQs */
 287        /* local_irq_restore(flags); */
 288
 289        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 290
 291        return 0;
 292}
 293
 294/**
 295 * nforce2_verify - verifies a new CPUFreq policy
 296 * @policy: new policy
 297 */
 298static int nforce2_verify(struct cpufreq_policy *policy)
 299{
 300        unsigned int fsb_pol_max;
 301
 302        fsb_pol_max = policy->max / (fid * 100);
 303
 304        if (policy->min < (fsb_pol_max * fid * 100))
 305                policy->max = (fsb_pol_max + 1) * fid * 100;
 306
 307        cpufreq_verify_within_limits(policy,
 308                                     policy->cpuinfo.min_freq,
 309                                     policy->cpuinfo.max_freq);
 310        return 0;
 311}
 312
 313static int nforce2_cpu_init(struct cpufreq_policy *policy)
 314{
 315        unsigned int fsb;
 316        unsigned int rfid;
 317
 318        /* capability check */
 319        if (policy->cpu != 0)
 320                return -ENODEV;
 321
 322        /* Get current FSB */
 323        fsb = nforce2_fsb_read(0);
 324
 325        if (!fsb)
 326                return -EIO;
 327
 328        /* FIX: Get FID from CPU */
 329        if (!fid) {
 330                if (!cpu_khz) {
 331                        printk(KERN_WARNING PFX
 332                        "cpu_khz not set, can't calculate multiplier!\n");
 333                        return -ENODEV;
 334                }
 335
 336                fid = cpu_khz / (fsb * 100);
 337                rfid = fid % 5;
 338
 339                if (rfid) {
 340                        if (rfid > 2)
 341                                fid += 5 - rfid;
 342                        else
 343                                fid -= rfid;
 344                }
 345        }
 346
 347        printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb,
 348               fid / 10, fid % 10);
 349
 350        /* Set maximum FSB to FSB at boot time */
 351        max_fsb = nforce2_fsb_read(1);
 352
 353        if (!max_fsb)
 354                return -EIO;
 355
 356        if (!min_fsb)
 357                min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
 358
 359        if (min_fsb < NFORCE2_MIN_FSB)
 360                min_fsb = NFORCE2_MIN_FSB;
 361
 362        /* cpuinfo and default policy values */
 363        policy->cpuinfo.min_freq = min_fsb * fid * 100;
 364        policy->cpuinfo.max_freq = max_fsb * fid * 100;
 365        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 366        policy->cur = nforce2_get(policy->cpu);
 367        policy->min = policy->cpuinfo.min_freq;
 368        policy->max = policy->cpuinfo.max_freq;
 369
 370        return 0;
 371}
 372
 373static int nforce2_cpu_exit(struct cpufreq_policy *policy)
 374{
 375        return 0;
 376}
 377
 378static struct cpufreq_driver nforce2_driver = {
 379        .name = "nforce2",
 380        .verify = nforce2_verify,
 381        .target = nforce2_target,
 382        .get = nforce2_get,
 383        .init = nforce2_cpu_init,
 384        .exit = nforce2_cpu_exit,
 385        .owner = THIS_MODULE,
 386};
 387
 388#ifdef MODULE
 389static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
 390        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
 391        {}
 392};
 393MODULE_DEVICE_TABLE(pci, nforce2_ids);
 394#endif
 395
 396/**
 397 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
 398 *
 399 * Detects nForce2 A2 and C1 stepping
 400 *
 401 */
 402static int nforce2_detect_chipset(void)
 403{
 404        nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
 405                                        PCI_DEVICE_ID_NVIDIA_NFORCE2,
 406                                        PCI_ANY_ID, PCI_ANY_ID, NULL);
 407
 408        if (nforce2_dev == NULL)
 409                return -ENODEV;
 410
 411        printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n",
 412               nforce2_dev->revision);
 413        printk(KERN_INFO PFX
 414               "FSB changing is maybe unstable and can lead to "
 415               "crashes and data loss.\n");
 416
 417        return 0;
 418}
 419
 420/**
 421 * nforce2_init - initializes the nForce2 CPUFreq driver
 422 *
 423 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
 424 * devices, -EINVAL on problems during initiatization, and zero on
 425 * success.
 426 */
 427static int __init nforce2_init(void)
 428{
 429        /* TODO: do we need to detect the processor? */
 430
 431        /* detect chipset */
 432        if (nforce2_detect_chipset()) {
 433                printk(KERN_INFO PFX "No nForce2 chipset.\n");
 434                return -ENODEV;
 435        }
 436
 437        return cpufreq_register_driver(&nforce2_driver);
 438}
 439
 440/**
 441 * nforce2_exit - unregisters cpufreq module
 442 *
 443 *   Unregisters nForce2 FSB change support.
 444 */
 445static void __exit nforce2_exit(void)
 446{
 447        cpufreq_unregister_driver(&nforce2_driver);
 448}
 449
 450module_init(nforce2_init);
 451module_exit(nforce2_exit);
 452
 453
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.