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
 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_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 274
 275        /* Disable IRQs */
 276        /* local_irq_save(flags); */
 277
 278        if (nforce2_set_fsb(target_fsb) < 0)
 279                printk(KERN_ERR PFX "Changing FSB to %d failed\n",
 280                        target_fsb);
 281        else
 282                pr_debug("Changed FSB successfully to %d\n",
 283                        target_fsb);
 284
 285        /* Enable IRQs */
 286        /* local_irq_restore(flags); */
 287
 288        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
 289
 290        return 0;
 291}
 292
 293/**
 294 * nforce2_verify - verifies a new CPUFreq policy
 295 * @policy: new policy
 296 */
 297static int nforce2_verify(struct cpufreq_policy *policy)
 298{
 299        unsigned int fsb_pol_max;
 300
 301        fsb_pol_max = policy->max / (fid * 100);
 302
 303        if (policy->min < (fsb_pol_max * fid * 100))
 304                policy->max = (fsb_pol_max + 1) * fid * 100;
 305
 306        cpufreq_verify_within_limits(policy,
 307                                     policy->cpuinfo.min_freq,
 308                                     policy->cpuinfo.max_freq);
 309        return 0;
 310}
 311
 312static int nforce2_cpu_init(struct cpufreq_policy *policy)
 313{
 314        unsigned int fsb;
 315        unsigned int rfid;
 316
 317        /* capability check */
 318        if (policy->cpu != 0)
 319                return -ENODEV;
 320
 321        /* Get current FSB */
 322        fsb = nforce2_fsb_read(0);
 323
 324        if (!fsb)
 325                return -EIO;
 326
 327        /* FIX: Get FID from CPU */
 328        if (!fid) {
 329                if (!cpu_khz) {
 330                        printk(KERN_WARNING PFX
 331                        "cpu_khz not set, can't calculate multiplier!\n");
 332                        return -ENODEV;
 333                }
 334
 335                fid = cpu_khz / (fsb * 100);
 336                rfid = fid % 5;
 337
 338                if (rfid) {
 339                        if (rfid > 2)
 340                                fid += 5 - rfid;
 341                        else
 342                                fid -= rfid;
 343                }
 344        }
 345
 346        printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb,
 347               fid / 10, fid % 10);
 348
 349        /* Set maximum FSB to FSB at boot time */
 350        max_fsb = nforce2_fsb_read(1);
 351
 352        if (!max_fsb)
 353                return -EIO;
 354
 355        if (!min_fsb)
 356                min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
 357
 358        if (min_fsb < NFORCE2_MIN_FSB)
 359                min_fsb = NFORCE2_MIN_FSB;
 360
 361        /* cpuinfo and default policy values */
 362        policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100;
 363        policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100;
 364        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 365        policy->cur = nforce2_get(policy->cpu);
 366
 367        return 0;
 368}
 369
 370static int nforce2_cpu_exit(struct cpufreq_policy *policy)
 371{
 372        return 0;
 373}
 374
 375static struct cpufreq_driver nforce2_driver = {
 376        .name = "nforce2",
 377        .verify = nforce2_verify,
 378        .target = nforce2_target,
 379        .get = nforce2_get,
 380        .init = nforce2_cpu_init,
 381        .exit = nforce2_cpu_exit,
 382};
 383
 384#ifdef MODULE
 385static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
 386        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
 387        {}
 388};
 389MODULE_DEVICE_TABLE(pci, nforce2_ids);
 390#endif
 391
 392/**
 393 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
 394 *
 395 * Detects nForce2 A2 and C1 stepping
 396 *
 397 */
 398static int nforce2_detect_chipset(void)
 399{
 400        nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
 401                                        PCI_DEVICE_ID_NVIDIA_NFORCE2,
 402                                        PCI_ANY_ID, PCI_ANY_ID, NULL);
 403
 404        if (nforce2_dev == NULL)
 405                return -ENODEV;
 406
 407        printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n",
 408               nforce2_dev->revision);
 409        printk(KERN_INFO PFX
 410               "FSB changing is maybe unstable and can lead to "
 411               "crashes and data loss.\n");
 412
 413        return 0;
 414}
 415
 416/**
 417 * nforce2_init - initializes the nForce2 CPUFreq driver
 418 *
 419 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
 420 * devices, -EINVAL on problems during initiatization, and zero on
 421 * success.
 422 */
 423static int __init nforce2_init(void)
 424{
 425        /* TODO: do we need to detect the processor? */
 426
 427        /* detect chipset */
 428        if (nforce2_detect_chipset()) {
 429                printk(KERN_INFO PFX "No nForce2 chipset.\n");
 430                return -ENODEV;
 431        }
 432
 433        return cpufreq_register_driver(&nforce2_driver);
 434}
 435
 436/**
 437 * nforce2_exit - unregisters cpufreq module
 438 *
 439 *   Unregisters nForce2 FSB change support.
 440 */
 441static void __exit nforce2_exit(void)
 442{
 443        cpufreq_unregister_driver(&nforce2_driver);
 444}
 445
 446module_init(nforce2_init);
 447module_exit(nforce2_exit);
 448
 449