linux/arch/x86/kernel/cpu/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#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
  61                "cpufreq-nforce2", msg)
  62
  63/**
  64 * nforce2_calc_fsb - calculate FSB
  65 * @pll: PLL value
  66 *
  67 *   Calculates FSB from PLL value
  68 */
  69static int nforce2_calc_fsb(int pll)
  70{
  71        unsigned char mul, div;
  72
  73        mul = (pll >> 8) & 0xff;
  74        div = pll & 0xff;
  75
  76        if (div > 0)
  77                return NFORCE2_XTAL * mul / div;
  78
  79        return 0;
  80}
  81
  82/**
  83 * nforce2_calc_pll - calculate PLL value
  84 * @fsb: FSB
  85 *
  86 *   Calculate PLL value for given FSB
  87 */
  88static int nforce2_calc_pll(unsigned int fsb)
  89{
  90        unsigned char xmul, xdiv;
  91        unsigned char mul = 0, div = 0;
  92        int tried = 0;
  93
  94        /* Try to calculate multiplier and divider up to 4 times */
  95        while (((mul == 0) || (div == 0)) && (tried <= 3)) {
  96                for (xdiv = 2; xdiv <= 0x80; xdiv++)
  97                        for (xmul = 1; xmul <= 0xfe; xmul++)
  98                                if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) ==
  99                                    fsb + tried) {
 100                                        mul = xmul;
 101                                        div = xdiv;
 102                                }
 103                tried++;
 104        }
 105
 106        if ((mul == 0) || (div == 0))
 107                return -1;
 108
 109        return NFORCE2_PLL(mul, div);
 110}
 111
 112/**
 113 * nforce2_write_pll - write PLL value to chipset
 114 * @pll: PLL value
 115 *
 116 *   Writes new FSB PLL value to chipset
 117 */
 118static void nforce2_write_pll(int pll)
 119{
 120        int temp;
 121
 122        /* Set the pll addr. to 0x00 */
 123        pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0);
 124
 125        /* Now write the value in all 64 registers */
 126        for (temp = 0; temp <= 0x3f; temp++)
 127                pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll);
 128
 129        return;
 130}
 131
 132/**
 133 * nforce2_fsb_read - Read FSB
 134 *
 135 *   Read FSB from chipset
 136 *   If bootfsb != 0, return FSB at boot-time
 137 */
 138static unsigned int nforce2_fsb_read(int bootfsb)
 139{
 140        struct pci_dev *nforce2_sub5;
 141        u32 fsb, temp = 0;
 142
 143        /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
 144        nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF,
 145                                PCI_ANY_ID, PCI_ANY_ID, NULL);
 146        if (!nforce2_sub5)
 147                return 0;
 148
 149        pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
 150        fsb /= 1000000;
 151
 152        /* Check if PLL register is already set */
 153        pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
 154
 155        if (bootfsb || !temp)
 156                return fsb;
 157
 158        /* Use PLL register FSB value */
 159        pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp);
 160        fsb = nforce2_calc_fsb(temp);
 161
 162        return fsb;
 163}
 164
 165/**
 166 * nforce2_set_fsb - set new FSB
 167 * @fsb: New FSB
 168 *
 169 *   Sets new FSB
 170 */
 171static int nforce2_set_fsb(unsigned int fsb)
 172{
 173        u32 temp = 0;
 174        unsigned int tfsb;
 175        int diff;
 176        int pll = 0;
 177
 178        if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
 179                printk(KERN_ERR PFX "FSB %d is out of range!\n", fsb);
 180                return -EINVAL;
 181        }
 182
 183        tfsb = nforce2_fsb_read(0);
 184        if (!tfsb) {
 185                printk(KERN_ERR PFX "Error while reading the FSB\n");
 186                return -EINVAL;
 187        }
 188
 189        /* First write? Then set actual value */
 190        pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
 191        if (!temp) {
 192                pll = nforce2_calc_pll(tfsb);
 193
 194                if (pll < 0)
 195                        return -EINVAL;
 196
 197                nforce2_write_pll(pll);
 198        }
 199
 200        /* Enable write access */
 201        temp = 0x01;
 202        pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp);
 203
 204        diff = tfsb - fsb;
 205
 206        if (!diff)
 207                return 0;
 208
 209        while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) {
 210                if (diff < 0)
 211                        tfsb++;
 212                else
 213                        tfsb--;
 214
 215                /* Calculate the PLL reg. value */
 216                pll = nforce2_calc_pll(tfsb);
 217                if (pll == -1)
 218                        return -EINVAL;
 219
 220                nforce2_write_pll(pll);
 221#ifdef NFORCE2_DELAY
 222                mdelay(NFORCE2_DELAY);
 223#endif
 224        }
 225
 226        temp = 0x40;
 227        pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp);
 228
 229        return 0;
 230}
 231
 232/**
 233 * nforce2_get - get the CPU frequency
 234 * @cpu: CPU number
 235 *
 236 * Returns the CPU frequency
 237 */
 238static unsigned int nforce2_get(unsigned int cpu)
 239{
 240        if (cpu)
 241                return 0;
 242        return nforce2_fsb_read(0) * fid * 100;
 243}
 244
 245/**
 246 * nforce2_target - set a new CPUFreq policy
 247 * @policy: new policy
 248 * @target_freq: the target frequency
 249 * @relation: how that frequency relates to achieved frequency
 250 *  (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
 251 *
 252 * Sets a new CPUFreq policy.
 253 */
 254static int nforce2_target(struct cpufreq_policy *policy,
 255                          unsigned int target_freq, unsigned int relation)
 256{
 257/*        unsigned long         flags; */
 258        struct cpufreq_freqs freqs;
 259        unsigned int target_fsb;
 260
 261        if ((target_freq > policy->max) || (target_freq < policy->min))
 262                return -EINVAL;
 263
 264        target_fsb = target_freq / (fid * 100);
 265
 266        freqs.old = nforce2_get(policy->cpu);
 267        freqs.new = target_fsb * fid * 100;
 268        freqs.cpu = 0;          /* Only one CPU on nForce2 platforms */
 269
 270        if (freqs.old == freqs.new)
 271                return 0;
 272
 273        dprintk("Old CPU frequency %d kHz, new %d kHz\n",
 274               freqs.old, freqs.new);
 275
 276        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 277
 278        /* Disable IRQs */
 279        /* local_irq_save(flags); */
 280
 281        if (nforce2_set_fsb(target_fsb) < 0)
 282                printk(KERN_ERR PFX "Changing FSB to %d failed\n",
 283                        target_fsb);
 284        else
 285                dprintk("Changed FSB successfully to %d\n",
 286                        target_fsb);
 287
 288        /* Enable IRQs */
 289        /* local_irq_restore(flags); */
 290
 291        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 292
 293        return 0;
 294}
 295
 296/**
 297 * nforce2_verify - verifies a new CPUFreq policy
 298 * @policy: new policy
 299 */
 300static int nforce2_verify(struct cpufreq_policy *policy)
 301{
 302        unsigned int fsb_pol_max;
 303
 304        fsb_pol_max = policy->max / (fid * 100);
 305
 306        if (policy->min < (fsb_pol_max * fid * 100))
 307                policy->max = (fsb_pol_max + 1) * fid * 100;
 308
 309        cpufreq_verify_within_limits(policy,
 310                                     policy->cpuinfo.min_freq,
 311                                     policy->cpuinfo.max_freq);
 312        return 0;
 313}
 314
 315static int nforce2_cpu_init(struct cpufreq_policy *policy)
 316{
 317        unsigned int fsb;
 318        unsigned int rfid;
 319
 320        /* capability check */
 321        if (policy->cpu != 0)
 322                return -ENODEV;
 323
 324        /* Get current FSB */
 325        fsb = nforce2_fsb_read(0);
 326
 327        if (!fsb)
 328                return -EIO;
 329
 330        /* FIX: Get FID from CPU */
 331        if (!fid) {
 332                if (!cpu_khz) {
 333                        printk(KERN_WARNING PFX
 334                        "cpu_khz not set, can't calculate multiplier!\n");
 335                        return -ENODEV;
 336                }
 337
 338                fid = cpu_khz / (fsb * 100);
 339                rfid = fid % 5;
 340
 341                if (rfid) {
 342                        if (rfid > 2)
 343                                fid += 5 - rfid;
 344                        else
 345                                fid -= rfid;
 346                }
 347        }
 348
 349        printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb,
 350               fid / 10, fid % 10);
 351
 352        /* Set maximum FSB to FSB at boot time */
 353        max_fsb = nforce2_fsb_read(1);
 354
 355        if (!max_fsb)
 356                return -EIO;
 357
 358        if (!min_fsb)
 359                min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
 360
 361        if (min_fsb < NFORCE2_MIN_FSB)
 362                min_fsb = NFORCE2_MIN_FSB;
 363
 364        /* cpuinfo and default policy values */
 365        policy->cpuinfo.min_freq = min_fsb * fid * 100;
 366        policy->cpuinfo.max_freq = max_fsb * fid * 100;
 367        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 368        policy->cur = nforce2_get(policy->cpu);
 369        policy->min = policy->cpuinfo.min_freq;
 370        policy->max = policy->cpuinfo.max_freq;
 371
 372        return 0;
 373}
 374
 375static int nforce2_cpu_exit(struct cpufreq_policy *policy)
 376{
 377        return 0;
 378}
 379
 380static struct cpufreq_driver nforce2_driver = {
 381        .name = "nforce2",
 382        .verify = nforce2_verify,
 383        .target = nforce2_target,
 384        .get = nforce2_get,
 385        .init = nforce2_cpu_init,
 386        .exit = nforce2_cpu_exit,
 387        .owner = THIS_MODULE,
 388};
 389
 390/**
 391 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
 392 *
 393 * Detects nForce2 A2 and C1 stepping
 394 *
 395 */
 396static unsigned int nforce2_detect_chipset(void)
 397{
 398        nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
 399                                        PCI_DEVICE_ID_NVIDIA_NFORCE2,
 400                                        PCI_ANY_ID, PCI_ANY_ID, NULL);
 401
 402        if (nforce2_dev == NULL)
 403                return -ENODEV;
 404
 405        printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n",
 406               nforce2_dev->revision);
 407        printk(KERN_INFO PFX
 408               "FSB changing is maybe unstable and can lead to "
 409               "crashes and data loss.\n");
 410
 411        return 0;
 412}
 413
 414/**
 415 * nforce2_init - initializes the nForce2 CPUFreq driver
 416 *
 417 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
 418 * devices, -EINVAL on problems during initiatization, and zero on
 419 * success.
 420 */
 421static int __init nforce2_init(void)
 422{
 423        /* TODO: do we need to detect the processor? */
 424
 425        /* detect chipset */
 426        if (nforce2_detect_chipset()) {
 427                printk(KERN_INFO PFX "No nForce2 chipset.\n");
 428                return -ENODEV;
 429        }
 430
 431        return cpufreq_register_driver(&nforce2_driver);
 432}
 433
 434/**
 435 * nforce2_exit - unregisters cpufreq module
 436 *
 437 *   Unregisters nForce2 FSB change support.
 438 */
 439static void __exit nforce2_exit(void)
 440{
 441        cpufreq_unregister_driver(&nforce2_driver);
 442}
 443
 444module_init(nforce2_init);
 445module_exit(nforce2_exit);
 446
 447