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