linux/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c
<<
>>
Prefs
   1/*
   2 *      Cyrix MediaGX and NatSemi Geode Suspend Modulation
   3 *      (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
   4 *      (C) 2002 Hiroshi Miura   <miura@da-cha.org>
   5 *      All Rights Reserved
   6 *
   7 *      This program is free software; you can redistribute it and/or
   8 *      modify it under the terms of the GNU General Public License
   9 *      version 2 as published by the Free Software Foundation
  10 *
  11 *      The author(s) of this software shall not be held liable for damages
  12 *      of any nature resulting due to the use of this software. This
  13 *      software is provided AS-IS with no warranties.
  14 *
  15 * Theoretical note:
  16 *
  17 *      (see Geode(tm) CS5530 manual (rev.4.1) page.56)
  18 *
  19 *      CPU frequency control on NatSemi Geode GX1/GXLV processor and CS55x0
  20 *      are based on Suspend Modulation.
  21 *
  22 *      Suspend Modulation works by asserting and de-asserting the SUSP# pin
  23 *      to CPU(GX1/GXLV) for configurable durations. When asserting SUSP#
  24 *      the CPU enters an idle state. GX1 stops its core clock when SUSP# is
  25 *      asserted then power consumption is reduced.
  26 *
  27 *      Suspend Modulation's OFF/ON duration are configurable
  28 *      with 'Suspend Modulation OFF Count Register'
  29 *      and 'Suspend Modulation ON Count Register'.
  30 *      These registers are 8bit counters that represent the number of
  31 *      32us intervals which the SUSP# pin is asserted(ON)/de-asserted(OFF)
  32 *      to the processor.
  33 *
  34 *      These counters define a ratio which is the effective frequency
  35 *      of operation of the system.
  36 *
  37 *                             OFF Count
  38 *      F_eff = Fgx * ----------------------
  39 *                      OFF Count + ON Count
  40 *
  41 *      0 <= On Count, Off Count <= 255
  42 *
  43 *      From these limits, we can get register values
  44 *
  45 *      off_duration + on_duration <= MAX_DURATION
  46 *      on_duration = off_duration * (stock_freq - freq) / freq
  47 *
  48 *      off_duration  =  (freq * DURATION) / stock_freq
  49 *      on_duration = DURATION - off_duration
  50 *
  51 *
  52 *---------------------------------------------------------------------------
  53 *
  54 * ChangeLog:
  55 *      Dec. 12, 2003   Hiroshi Miura <miura@da-cha.org>
  56 *              - fix on/off register mistake
  57 *              - fix cpu_khz calc when it stops cpu modulation.
  58 *
  59 *      Dec. 11, 2002   Hiroshi Miura <miura@da-cha.org>
  60 *              - rewrite for Cyrix MediaGX Cx5510/5520 and
  61 *                NatSemi Geode Cs5530(A).
  62 *
  63 *      Jul. ??, 2002  Zwane Mwaikambo <zwane@commfireservices.com>
  64 *              - cs5530_mod patch for 2.4.19-rc1.
  65 *
  66 *---------------------------------------------------------------------------
  67 *
  68 * Todo
  69 *      Test on machines with 5510, 5530, 5530A
  70 */
  71
  72/************************************************************************
  73 *                      Suspend Modulation - Definitions                *
  74 ************************************************************************/
  75
  76#include <linux/kernel.h>
  77#include <linux/module.h>
  78#include <linux/init.h>
  79#include <linux/smp.h>
  80#include <linux/cpufreq.h>
  81#include <linux/pci.h>
  82#include <linux/errno.h>
  83
  84#include <asm/processor-cyrix.h>
  85
  86/* PCI config registers, all at F0 */
  87#define PCI_PMER1       0x80    /* power management enable register 1 */
  88#define PCI_PMER2       0x81    /* power management enable register 2 */
  89#define PCI_PMER3       0x82    /* power management enable register 3 */
  90#define PCI_IRQTC       0x8c    /* irq speedup timer counter register:typical 2 to 4ms */
  91#define PCI_VIDTC       0x8d    /* video speedup timer counter register: typical 50 to 100ms */
  92#define PCI_MODOFF      0x94    /* suspend modulation OFF counter register, 1 = 32us */
  93#define PCI_MODON       0x95    /* suspend modulation ON counter register */
  94#define PCI_SUSCFG      0x96    /* suspend configuration register */
  95
  96/* PMER1 bits */
  97#define GPM             (1<<0)  /* global power management */
  98#define GIT             (1<<1)  /* globally enable PM device idle timers */
  99#define GTR             (1<<2)  /* globally enable IO traps */
 100#define IRQ_SPDUP       (1<<3)  /* disable clock throttle during interrupt handling */
 101#define VID_SPDUP       (1<<4)  /* disable clock throttle during vga video handling */
 102
 103/* SUSCFG bits */
 104#define SUSMOD          (1<<0)  /* enable/disable suspend modulation */
 105/* the below is supported only with cs5530 (after rev.1.2)/cs5530A */
 106#define SMISPDUP        (1<<1)  /* select how SMI re-enable suspend modulation: */
 107                                /* IRQTC timer or read SMI speedup disable reg.(F1BAR[08-09h]) */
 108#define SUSCFG          (1<<2)  /* enable powering down a GXLV processor. "Special 3Volt Suspend" mode */
 109/* the below is supported only with cs5530A */
 110#define PWRSVE_ISA      (1<<3)  /* stop ISA clock  */
 111#define PWRSVE          (1<<4)  /* active idle */
 112
 113struct gxfreq_params {
 114        u8 on_duration;
 115        u8 off_duration;
 116        u8 pci_suscfg;
 117        u8 pci_pmer1;
 118        u8 pci_pmer2;
 119        struct pci_dev *cs55x0;
 120};
 121
 122static struct gxfreq_params *gx_params;
 123static int stock_freq;
 124
 125/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
 126static int pci_busclk;
 127module_param(pci_busclk, int, 0444);
 128
 129/* maximum duration for which the cpu may be suspended
 130 * (32us * MAX_DURATION). If no parameter is given, this defaults
 131 * to 255.
 132 * Note that this leads to a maximum of 8 ms(!) where the CPU clock
 133 * is suspended -- processing power is just 0.39% of what it used to be,
 134 * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
 135static int max_duration = 255;
 136module_param(max_duration, int, 0444);
 137
 138/* For the default policy, we want at least some processing power
 139 * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
 140 */
 141#define POLICY_MIN_DIV 20
 142
 143
 144#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
 145                "gx-suspmod", msg)
 146
 147/**
 148 * we can detect a core multipiler from dir0_lsb
 149 * from GX1 datasheet p.56,
 150 *      MULT[3:0]:
 151 *      0000 = SYSCLK multiplied by 4 (test only)
 152 *      0001 = SYSCLK multiplied by 10
 153 *      0010 = SYSCLK multiplied by 4
 154 *      0011 = SYSCLK multiplied by 6
 155 *      0100 = SYSCLK multiplied by 9
 156 *      0101 = SYSCLK multiplied by 5
 157 *      0110 = SYSCLK multiplied by 7
 158 *      0111 = SYSCLK multiplied by 8
 159 *              of 33.3MHz
 160 **/
 161static int gx_freq_mult[16] = {
 162                4, 10, 4, 6, 9, 5, 7, 8,
 163                0, 0, 0, 0, 0, 0, 0, 0
 164};
 165
 166
 167/****************************************************************
 168 *      Low Level chipset interface                             *
 169 ****************************************************************/
 170static struct pci_device_id gx_chipset_tbl[] __initdata = {
 171        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
 172                PCI_ANY_ID, PCI_ANY_ID },
 173        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520,
 174                PCI_ANY_ID, PCI_ANY_ID },
 175        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510,
 176                PCI_ANY_ID, PCI_ANY_ID },
 177        { 0, },
 178};
 179
 180static void gx_write_byte(int reg, int value)
 181{
 182        pci_write_config_byte(gx_params->cs55x0, reg, value);
 183}
 184
 185/**
 186 * gx_detect_chipset:
 187 *
 188 **/
 189static __init struct pci_dev *gx_detect_chipset(void)
 190{
 191        struct pci_dev *gx_pci = NULL;
 192
 193        /* check if CPU is a MediaGX or a Geode. */
 194        if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
 195            (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
 196                dprintk("error: no MediaGX/Geode processor found!\n");
 197                return NULL;
 198        }
 199
 200        /* detect which companion chip is used */
 201        while ((gx_pci = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, gx_pci)) != NULL) {
 202                if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
 203                        return gx_pci;
 204        }
 205
 206        dprintk("error: no supported chipset found!\n");
 207        return NULL;
 208}
 209
 210/**
 211 * gx_get_cpuspeed:
 212 *
 213 * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi
 214 * Geode CPU runs.
 215 */
 216static unsigned int gx_get_cpuspeed(unsigned int cpu)
 217{
 218        if ((gx_params->pci_suscfg & SUSMOD) == 0)
 219                return stock_freq;
 220
 221        return (stock_freq * gx_params->off_duration)
 222                / (gx_params->on_duration + gx_params->off_duration);
 223}
 224
 225/**
 226 *      gx_validate_speed:
 227 *      determine current cpu speed
 228 *
 229 **/
 230
 231static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration,
 232                u8 *off_duration)
 233{
 234        unsigned int i;
 235        u8 tmp_on, tmp_off;
 236        int old_tmp_freq = stock_freq;
 237        int tmp_freq;
 238
 239        *off_duration = 1;
 240        *on_duration = 0;
 241
 242        for (i = max_duration; i > 0; i--) {
 243                tmp_off = ((khz * i) / stock_freq) & 0xff;
 244                tmp_on = i - tmp_off;
 245                tmp_freq = (stock_freq * tmp_off) / i;
 246                /* if this relation is closer to khz, use this. If it's equal,
 247                 * prefer it, too - lower latency */
 248                if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) {
 249                        *on_duration = tmp_on;
 250                        *off_duration = tmp_off;
 251                        old_tmp_freq = tmp_freq;
 252                }
 253        }
 254
 255        return old_tmp_freq;
 256}
 257
 258
 259/**
 260 * gx_set_cpuspeed:
 261 * set cpu speed in khz.
 262 **/
 263
 264static void gx_set_cpuspeed(unsigned int khz)
 265{
 266        u8 suscfg, pmer1;
 267        unsigned int new_khz;
 268        unsigned long flags;
 269        struct cpufreq_freqs freqs;
 270
 271        freqs.cpu = 0;
 272        freqs.old = gx_get_cpuspeed(0);
 273
 274        new_khz = gx_validate_speed(khz, &gx_params->on_duration,
 275                        &gx_params->off_duration);
 276
 277        freqs.new = new_khz;
 278
 279        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 280        local_irq_save(flags);
 281
 282
 283
 284        if (new_khz != stock_freq) {
 285                /* if new khz == 100% of CPU speed, it is special case */
 286                switch (gx_params->cs55x0->device) {
 287                case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
 288                        pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
 289                        /* FIXME: need to test other values -- Zwane,Miura */
 290                        /* typical 2 to 4ms */
 291                        gx_write_byte(PCI_IRQTC, 4);
 292                        /* typical 50 to 100ms */
 293                        gx_write_byte(PCI_VIDTC, 100);
 294                        gx_write_byte(PCI_PMER1, pmer1);
 295
 296                        if (gx_params->cs55x0->revision < 0x10) {
 297                                /* CS5530(rev 1.2, 1.3) */
 298                                suscfg = gx_params->pci_suscfg|SUSMOD;
 299                        } else {
 300                                /* CS5530A,B.. */
 301                                suscfg = gx_params->pci_suscfg|SUSMOD|PWRSVE;
 302                        }
 303                        break;
 304                case PCI_DEVICE_ID_CYRIX_5520:
 305                case PCI_DEVICE_ID_CYRIX_5510:
 306                        suscfg = gx_params->pci_suscfg | SUSMOD;
 307                        break;
 308                default:
 309                        local_irq_restore(flags);
 310                        dprintk("fatal: try to set unknown chipset.\n");
 311                        return;
 312                }
 313        } else {
 314                suscfg = gx_params->pci_suscfg & ~(SUSMOD);
 315                gx_params->off_duration = 0;
 316                gx_params->on_duration = 0;
 317                dprintk("suspend modulation disabled: cpu runs 100%% speed.\n");
 318        }
 319
 320        gx_write_byte(PCI_MODOFF, gx_params->off_duration);
 321        gx_write_byte(PCI_MODON, gx_params->on_duration);
 322
 323        gx_write_byte(PCI_SUSCFG, suscfg);
 324        pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
 325
 326        local_irq_restore(flags);
 327
 328        gx_params->pci_suscfg = suscfg;
 329
 330        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 331
 332        dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
 333                gx_params->on_duration * 32, gx_params->off_duration * 32);
 334        dprintk("suspend modulation w/ clock speed: %d kHz.\n", freqs.new);
 335}
 336
 337/****************************************************************
 338 *             High level functions                             *
 339 ****************************************************************/
 340
 341/*
 342 *      cpufreq_gx_verify: test if frequency range is valid
 343 *
 344 *      This function checks if a given frequency range in kHz is valid
 345 *      for the hardware supported by the driver.
 346 */
 347
 348static int cpufreq_gx_verify(struct cpufreq_policy *policy)
 349{
 350        unsigned int tmp_freq = 0;
 351        u8 tmp1, tmp2;
 352
 353        if (!stock_freq || !policy)
 354                return -EINVAL;
 355
 356        policy->cpu = 0;
 357        cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
 358                        stock_freq);
 359
 360        /* it needs to be assured that at least one supported frequency is
 361         * within policy->min and policy->max. If it is not, policy->max
 362         * needs to be increased until one freuqency is supported.
 363         * policy->min may not be decreased, though. This way we guarantee a
 364         * specific processing capacity.
 365         */
 366        tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
 367        if (tmp_freq < policy->min)
 368                tmp_freq += stock_freq / max_duration;
 369        policy->min = tmp_freq;
 370        if (policy->min > policy->max)
 371                policy->max = tmp_freq;
 372        tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
 373        if (tmp_freq > policy->max)
 374                tmp_freq -= stock_freq / max_duration;
 375        policy->max = tmp_freq;
 376        if (policy->max < policy->min)
 377                policy->max = policy->min;
 378        cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
 379                        stock_freq);
 380
 381        return 0;
 382}
 383
 384/*
 385 *      cpufreq_gx_target:
 386 *
 387 */
 388static int cpufreq_gx_target(struct cpufreq_policy *policy,
 389                             unsigned int target_freq,
 390                             unsigned int relation)
 391{
 392        u8 tmp1, tmp2;
 393        unsigned int tmp_freq;
 394
 395        if (!stock_freq || !policy)
 396                return -EINVAL;
 397
 398        policy->cpu = 0;
 399
 400        tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
 401        while (tmp_freq < policy->min) {
 402                tmp_freq += stock_freq / max_duration;
 403                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
 404        }
 405        while (tmp_freq > policy->max) {
 406                tmp_freq -= stock_freq / max_duration;
 407                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
 408        }
 409
 410        gx_set_cpuspeed(tmp_freq);
 411
 412        return 0;
 413}
 414
 415static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
 416{
 417        unsigned int maxfreq, curfreq;
 418
 419        if (!policy || policy->cpu != 0)
 420                return -ENODEV;
 421
 422        /* determine maximum frequency */
 423        if (pci_busclk)
 424                maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
 425        else if (cpu_khz)
 426                maxfreq = cpu_khz;
 427        else
 428                maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
 429
 430        stock_freq = maxfreq;
 431        curfreq = gx_get_cpuspeed(0);
 432
 433        dprintk("cpu max frequency is %d.\n", maxfreq);
 434        dprintk("cpu current frequency is %dkHz.\n", curfreq);
 435
 436        /* setup basic struct for cpufreq API */
 437        policy->cpu = 0;
 438
 439        if (max_duration < POLICY_MIN_DIV)
 440                policy->min = maxfreq / max_duration;
 441        else
 442                policy->min = maxfreq / POLICY_MIN_DIV;
 443        policy->max = maxfreq;
 444        policy->cur = curfreq;
 445        policy->cpuinfo.min_freq = maxfreq / max_duration;
 446        policy->cpuinfo.max_freq = maxfreq;
 447        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 448
 449        return 0;
 450}
 451
 452/*
 453 * cpufreq_gx_init:
 454 *   MediaGX/Geode GX initialize cpufreq driver
 455 */
 456static struct cpufreq_driver gx_suspmod_driver = {
 457        .get            = gx_get_cpuspeed,
 458        .verify         = cpufreq_gx_verify,
 459        .target         = cpufreq_gx_target,
 460        .init           = cpufreq_gx_cpu_init,
 461        .name           = "gx-suspmod",
 462        .owner          = THIS_MODULE,
 463};
 464
 465static int __init cpufreq_gx_init(void)
 466{
 467        int ret;
 468        struct gxfreq_params *params;
 469        struct pci_dev *gx_pci;
 470
 471        /* Test if we have the right hardware */
 472        gx_pci = gx_detect_chipset();
 473        if (gx_pci == NULL)
 474                return -ENODEV;
 475
 476        /* check whether module parameters are sane */
 477        if (max_duration > 0xff)
 478                max_duration = 0xff;
 479
 480        dprintk("geode suspend modulation available.\n");
 481
 482        params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
 483        if (params == NULL)
 484                return -ENOMEM;
 485
 486        params->cs55x0 = gx_pci;
 487        gx_params = params;
 488
 489        /* keep cs55x0 configurations */
 490        pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg));
 491        pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
 492        pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
 493        pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
 494        pci_read_config_byte(params->cs55x0, PCI_MODOFF,
 495                        &(params->off_duration));
 496
 497        ret = cpufreq_register_driver(&gx_suspmod_driver);
 498        if (ret) {
 499                kfree(params);
 500                return ret;                   /* register error! */
 501        }
 502
 503        return 0;
 504}
 505
 506static void __exit cpufreq_gx_exit(void)
 507{
 508        cpufreq_unregister_driver(&gx_suspmod_driver);
 509        pci_dev_put(gx_params->cs55x0);
 510        kfree(gx_params);
 511}
 512
 513MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");
 514MODULE_DESCRIPTION("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
 515MODULE_LICENSE("GPL");
 516
 517module_init(cpufreq_gx_init);
 518module_exit(cpufreq_gx_exit);
 519
 520