linux-old/drivers/char/amd76x_pm.c
<<
>>
Prefs
   1/*
   2 * ACPI sytle PM for SMP AMD-760MP(X) based systems.
   3 * For use until the ACPI project catches up. :-)
   4 *
   5 * Copyright (C) 2002 Johnathan Hicks <thetech@folkwolf.net>
   6 *
   7 * History:
   8 * 
   9 *   20020702 - amd-smp-idle: Tony Lindgren <tony@atomide.com>
  10 *      Influenced by Vcool, and LVCool. Rewrote everything from scratch to
  11 *      use the PCI features in Linux, and to support SMP systems. Provides
  12 *      C2 idling on SMP AMD-760MP systems.
  13 *      
  14 *   20020722: JH
  15 *      I adapted Tony's code for the AMD-765/766 southbridge and adapted it
  16 *      according to the AMD-768 data sheet to provide the same capability for
  17 *      SMP AMD-760MPX systems. Posted to acpi-devel list.
  18 *      
  19 *   20020722: Alan Cox
  20 *      Replaces non-functional amd76x_pm code in -ac tree.
  21 *      
  22 *   20020730: JH
  23 *      Added ability to do normal throttling (the non-thermal kind), C3 idling
  24 *      and Power On Suspend (S1 sleep). It would be very easy to tie swsusp
  25 *      into activate_amd76x_SLP(). C3 idling doesn't happen yet; see my note
  26 *      in amd76x_smp_idle(). I've noticed that when NTH and idling are both
  27 *      enabled, my hardware locks and requires a hard reset, so I have
  28 *      #ifndefed around the idle loop setting to prevent this. POS locks it up
  29 *      too, both ought to be fixable. I've also noticed that idling and NTH
  30 *      make some interference that is picked up by the onboard sound chip on
  31 *      my ASUS A7M266-D motherboard.
  32 *
  33 *
  34 * TODO: Thermal throttling (TTH).
  35 *       /proc interface for normal throttling level.
  36 *       /proc interface for POS.
  37 *
  38 *
  39 *    <Notes from 20020722-ac revision>
  40 *
  41 * Processor idle mode module for AMD SMP 760MP(X) based systems
  42 *
  43 * Copyright (C) 2002 Tony Lindgren <tony@atomide.com>
  44 *                    Johnathan Hicks (768 support)
  45 *
  46 * Using this module saves about 70 - 90W of energy in the idle mode compared
  47 * to the default idle mode. Waking up from the idle mode is fast to keep the
  48 * system response time good. Currently no CPU load calculation is done, the
  49 * system exits the idle mode if the idle function runs twice on the same
  50 * processor in a row. This only works on SMP systems, but maybe the idle mode
  51 * enabling can be integrated to ACPI to provide C2 mode at some point.
  52 *
  53 * NOTE: Currently there's a bug somewhere where the reading the
  54 *       P_LVL2 for the first time causes the system to sleep instead of 
  55 *       idling. This means that you need to hit the power button once to
  56 *       wake the system after loading the module for the first time after
  57 *       reboot. After that the system idles as supposed.
  58 *
  59 *
  60 * Influenced by Vcool, and LVCool. Rewrote everything from scratch to
  61 * use the PCI features in Linux, and to support SMP systems.
  62 * 
  63 * Currently only tested on a TYAN S2460 (760MP) system (Tony) and an
  64 * ASUS A7M266-D (760MPX) system (Johnathan). Adding support for other Athlon
  65 * SMP or single processor systems should be easy if desired.
  66 *
  67 * This software is licensed under GNU General Public License Version 2 
  68 * as specified in file COPYING in the Linux kernel source tree main 
  69 * directory.
  70 * 
  71 *   </Notes from 20020722-ac revision>
  72 */
  73
  74
  75#include <linux/config.h>
  76#include <linux/module.h>
  77#include <linux/slab.h>
  78#include <linux/pci.h>
  79#include <linux/delay.h>
  80#include <linux/pm.h>
  81#include <linux/init.h>
  82
  83#include "amd76x_pm.h"
  84
  85#define VERSION "20020730"
  86
  87// #define AMD76X_C3  1
  88// #define AMD76X_NTH 1
  89// #define AMD76X_POS 1
  90
  91
  92extern void default_idle(void);
  93static void amd76x_smp_idle(void);
  94static int amd76x_pm_main(void);
  95static int __devinit amd_nb_init(struct pci_dev *pdev,
  96                                 const struct pci_device_id *ent);
  97static void amd_nb_remove(struct pci_dev *pdev);
  98static int __devinit amd_sb_init(struct pci_dev *pdev,
  99                                 const struct pci_device_id *ent);
 100static void amd_sb_remove(struct pci_dev *pdev);
 101
 102
 103static struct pci_dev *pdev_nb;
 104static struct pci_dev *pdev_sb;
 105
 106struct PM_cfg {
 107        unsigned int status_reg;
 108        unsigned int C2_reg;
 109        unsigned int C3_reg;
 110        unsigned int NTH_reg;
 111        unsigned int slp_reg;
 112        unsigned int resume_reg;
 113        void (*orig_idle) (void);
 114        void (*curr_idle) (void);
 115        unsigned long C2_cnt, C3_cnt;
 116        int last_pr;
 117};
 118static struct PM_cfg amd76x_pm_cfg;
 119
 120struct cpu_idle_state {
 121        int idle;
 122        int count;
 123};
 124static struct cpu_idle_state prs[2];
 125
 126static struct pci_device_id amd_nb_tbl[] __devinitdata = {
 127        {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, PCI_ANY_ID, PCI_ANY_ID,},
 128        {0,}
 129};
 130
 131static struct pci_device_id amd_sb_tbl[] __devinitdata = {
 132        {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, PCI_ANY_ID, PCI_ANY_ID,},
 133        {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7443, PCI_ANY_ID, PCI_ANY_ID,},
 134        {0,}
 135};
 136
 137static struct pci_driver amd_nb_driver = {
 138        name:"amd76x_pm-nb",
 139        id_table:amd_nb_tbl,
 140        probe:amd_nb_init,
 141        remove:__devexit_p(amd_nb_remove),
 142};
 143
 144static struct pci_driver amd_sb_driver = {
 145        name:"amd76x_pm-sb",
 146        id_table:amd_sb_tbl,
 147        probe:amd_sb_init,
 148        remove:__devexit_p(amd_sb_remove),
 149};
 150
 151
 152static int __devinit
 153amd_nb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 154{
 155        pdev_nb = pdev;
 156        printk(KERN_INFO "amd76x_pm: Initializing northbridge %s\n",
 157               pdev_nb->name);
 158
 159        return 0;
 160}
 161
 162
 163static void __devexit
 164amd_nb_remove(struct pci_dev *pdev)
 165{
 166}
 167
 168
 169static int __devinit
 170amd_sb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
 171{
 172        pdev_sb = pdev;
 173        printk(KERN_INFO "amd76x_pm: Initializing southbridge %s\n",
 174               pdev_sb->name);
 175
 176        return 0;
 177}
 178
 179
 180static void __devexit
 181amd_sb_remove(struct pci_dev *pdev)
 182{
 183}
 184
 185
 186/*
 187 * Configures the AMD-762 northbridge to support PM calls
 188 */
 189static int
 190config_amd762(int enable)
 191{
 192        unsigned int regdword;
 193
 194        /* Enable STPGNT in BIU Status/Control for cpu0 */
 195        pci_read_config_dword(pdev_nb, 0x60, &regdword);
 196        regdword |= (1 << 17);
 197        pci_write_config_dword(pdev_nb, 0x60, regdword);
 198
 199        /* Enable STPGNT in BIU Status/Control for cpu1 */
 200        pci_read_config_dword(pdev_nb, 0x68, &regdword);
 201        regdword |= (1 << 17);
 202        pci_write_config_dword(pdev_nb, 0x68, regdword);
 203
 204        /* DRAM refresh enable */
 205        pci_read_config_dword(pdev_nb, 0x58, &regdword);
 206        regdword &= ~(1 << 19);
 207        pci_write_config_dword(pdev_nb, 0x58, regdword);
 208
 209        /* Self refresh enable */
 210        pci_read_config_dword(pdev_nb, 0x70, &regdword);
 211        regdword |= (1 << 18);
 212        pci_write_config_dword(pdev_nb, 0x70, regdword);
 213
 214        return 0;
 215}
 216
 217
 218/*
 219 * Get the base PMIO address and set the pm registers in amd76x_pm_cfg.
 220 */
 221static void
 222amd76x_get_PM(void)
 223{
 224        unsigned int regdword;
 225
 226        /* Get the address for pm status, P_LVL2, etc */
 227        pci_read_config_dword(pdev_sb, 0x58, &regdword);
 228        regdword &= 0xff80;
 229        amd76x_pm_cfg.status_reg = (regdword + 0x00);
 230        amd76x_pm_cfg.slp_reg =    (regdword + 0x04);
 231        amd76x_pm_cfg.NTH_reg =    (regdword + 0x10);
 232        amd76x_pm_cfg.C2_reg =     (regdword + 0x14);
 233        amd76x_pm_cfg.C3_reg =     (regdword + 0x15);
 234        amd76x_pm_cfg.resume_reg = (regdword + 0x16); /* N/A for 768 */
 235}
 236
 237
 238/*
 239 * En/Disable PMIO and configure W4SG & STPGNT.
 240 */
 241static int
 242config_PMIO_amd76x(int is_766, int enable)
 243{
 244        unsigned char regbyte;
 245
 246        /* Clear W4SG, and set PMIOEN, if using a 765/766 set STPGNT as well.
 247         * AMD-766: C3A41; page 59 in AMD-766 doc
 248         * AMD-768: DevB:3x41C; page 94 in AMD-768 doc */
 249        pci_read_config_byte(pdev_sb, 0x41, &regbyte);
 250        if(enable) {
 251                regbyte |= ((0 << 0) | (is_766?1:0 << 1) | (1 << 7));
 252        }
 253        else {
 254                regbyte |= (0 << 7);
 255        }
 256        pci_write_config_byte(pdev_sb, 0x41, regbyte);
 257
 258        return 0;
 259}
 260
 261/*
 262 * C2 idle support for AMD-766.
 263 */
 264static void
 265config_amd766_C2(int enable)
 266{
 267        unsigned int regdword;
 268
 269        /* Set C2 options in C3A50, page 63 in AMD-766 doc */
 270        pci_read_config_dword(pdev_sb, 0x50, &regdword);
 271        if(enable) {
 272                regdword &= ~((DCSTOP_EN | CPUSTP_EN | PCISTP_EN | SUSPND_EN |
 273                                        CPURST_EN) << C2_REGS);
 274                regdword |= (STPCLK_EN  /* ~ 20 Watt savings max */
 275                         |  CPUSLP_EN)  /* Additional ~ 70 Watts max! */
 276                         << C2_REGS;
 277        }
 278        else
 279                regdword &= ~((STPCLK_EN | CPUSLP_EN) << C2_REGS);
 280        pci_write_config_dword(pdev_sb, 0x50, regdword);
 281}
 282
 283
 284#ifdef AMD76X_C3
 285/*
 286 * Untested C3 idle support for AMD-766.
 287 */
 288static void
 289config_amd766_C3(int enable)
 290{
 291        unsigned int regdword;
 292
 293        /* Set C3 options in C3A50, page 63 in AMD-766 doc */
 294        pci_read_config_dword(pdev_sb, 0x50, &regdword);
 295        if(enable) {
 296                regdword &= ~((DCSTOP_EN | PCISTP_EN | SUSPND_EN | CPURST_EN)
 297                                << C3_REGS);
 298                regdword |= (STPCLK_EN  /* ~ 20 Watt savings max */
 299                         |  CPUSLP_EN   /* Additional ~ 70 Watts max! */
 300                         |  CPUSTP_EN)  /* yet more savings! */
 301                         << C3_REGS;
 302        }
 303        else
 304                regdword &= ~((STPCLK_EN | CPUSLP_EN | CPUSTP_EN) << C3_REGS);
 305        pci_write_config_dword(pdev_sb, 0x50, regdword);
 306}
 307#endif
 308
 309
 310#ifdef AMD76X_POS
 311static void
 312config_amd766_POS(int enable)
 313{
 314        unsigned int regdword;
 315
 316        /* Set C3 options in C3A50, page 63 in AMD-766 doc */
 317        pci_read_config_dword(pdev_sb, 0x50, &regdword);
 318        if(enable) {
 319                regdword &= ~((ZZ_CACHE_EN | CPURST_EN) << POS_REGS);
 320                regdword |= ((DCSTOP_EN | STPCLK_EN | CPUSTP_EN | PCISTP_EN |
 321                                        CPUSLP_EN | SUSPND_EN) << POS_REGS);
 322        }
 323        else
 324                regdword ^= (0xff << POS_REGS);
 325        pci_write_config_dword(pdev_sb, 0x50, regdword);
 326}
 327#endif
 328
 329
 330/*
 331 * Configures the 765 & 766 southbridges.
 332 */
 333static int
 334config_amd766(int enable)
 335{
 336        amd76x_get_PM();
 337        config_PMIO_amd76x(1, 1);
 338
 339        config_amd766_C2(enable);
 340#ifdef AMD76X_C3
 341        config_amd766_C3(enable);
 342#endif
 343#ifdef AMD76X_POS
 344        config_amd766_POS(enable);
 345#endif
 346
 347        return 0;
 348}
 349
 350
 351/*
 352 * C2 idling support for AMD-768.
 353 */
 354static void
 355config_amd768_C2(int enable)
 356{
 357        unsigned char regbyte;
 358        
 359        /* Set C2 options in DevB:3x4F, page 100 in AMD-768 doc */
 360        pci_read_config_byte(pdev_sb, 0x4F, &regbyte);
 361        if(enable)
 362                regbyte |= C2EN;
 363        else
 364                regbyte ^= C2EN;
 365        pci_write_config_byte(pdev_sb, 0x4F, regbyte);
 366}
 367
 368
 369#ifdef AMD76X_C3
 370/*
 371 * C3 idle support for AMD-768. The idle loop would need some extra
 372 * handling for C3, but it would make more sense for ACPI to handle CX level
 373 * transitions like it is supposed to. Unfortunately ACPI doesn't do CX
 374 * levels on SMP systems yet.
 375 */
 376static void
 377config_amd768_C3(int enable)
 378{
 379        unsigned char regbyte;
 380        
 381        /* Set C3 options in DevB:3x4F, page 100 in AMD-768 doc */
 382        pci_read_config_byte(pdev_sb, 0x4F, &regbyte);
 383        if(enable)
 384                regbyte |= (C3EN /* | ZZ_C3EN | CSLP_C3EN | CSTP_C3EN */);
 385        else
 386                regbyte ^= C3EN;
 387        pci_write_config_byte(pdev_sb, 0x4F, regbyte);
 388}
 389#endif
 390
 391
 392#ifdef AMD76X_POS
 393/*
 394 * Untested Power On Suspend support for AMD-768. This should also be handled
 395 * by ACPI.
 396 */
 397static void
 398config_amd768_POS(int enable)
 399{
 400        unsigned int regdword;
 401
 402        /* Set POS options in DevB:3x50, page 101 in AMD-768 doc */
 403        pci_read_config_dword(pdev_sb, 0x50, &regdword);
 404        if(enable) 
 405                regdword |= (POSEN | CSTP | PSTP | ASTP | DCSTP | CSLP | SUSP);
 406        else
 407                regdword ^= POSEN;
 408        pci_write_config_dword(pdev_sb, 0x50, regdword);
 409}
 410#endif
 411
 412
 413#ifdef AMD76X_NTH
 414/*
 415 * Normal Throttling support for AMD-768. There are several settings
 416 * that can be set depending on how long you want some of the delays to be.
 417 * I'm not sure if this is even neccessary at all as the 766 doesn't need this.
 418 */
 419static void
 420config_amd768_NTH(int enable, int ntper, int thminen)
 421{
 422        unsigned char regbyte;
 423
 424        /* DevB:3x40, pg 93 of 768 doc */
 425        pci_read_config_byte(pdev_sb, 0x40, &regbyte);
 426        /* Is it neccessary to use THMINEN at ANY time? */
 427        regbyte |= (NTPER(ntper) | THMINEN(thminen));
 428        pci_write_config_byte(pdev_sb, 0x40, regbyte);
 429}
 430#endif
 431
 432
 433/*
 434 * Configures the 768 southbridge to support idle calls, and gets
 435 * the processor idle call register location.
 436 */
 437static int
 438config_amd768(int enable)
 439{
 440        amd76x_get_PM();
 441        config_PMIO_amd76x(0, 1);
 442
 443        config_amd768_C2(enable);
 444#ifdef AMD76X_C3
 445        config_amd768_C3(enable);
 446#endif
 447#ifdef AMD76X_POS
 448        config_amd768_POS(enable);
 449#endif
 450#ifdef AMD76X_NTH
 451        config_amd768_NTH(enable, 1, 2);
 452#endif
 453
 454        return 0;
 455}
 456
 457
 458#ifdef AMD76X_NTH
 459/*
 460 * Activate normal throttling via its ACPI register (P_CNT).
 461 */
 462static void
 463activate_amd76x_NTH(int enable, int ratio)
 464{
 465        unsigned int regdword;
 466
 467        /* PM10, pg 110 of 768 doc, pg 70 of 766 doc */
 468        regdword=inl(amd76x_pm_cfg.NTH_reg);
 469        if(enable)
 470                regdword |= (NTH_EN | NTH_RATIO(ratio));
 471        else
 472                regdword ^= NTH_EN;
 473        outl(regdword, amd76x_pm_cfg.NTH_reg);
 474}
 475#endif
 476
 477#ifdef AMD76X_POS
 478/*
 479 * Activate sleep state via its ACPI register (PM1_CNT).
 480 */
 481static void
 482activate_amd76x_SLP(int type)
 483{
 484        unsigned short regshort;
 485
 486        /* PM04, pg 109 of 768 doc, pg 69 of 766 doc */
 487        regshort=inw(amd76x_pm_cfg.slp_reg);
 488        regshort |= (SLP_EN | SLP_TYP(type)) ;
 489        outw(regshort, amd76x_pm_cfg.slp_reg);
 490}
 491
 492/*
 493 * Wrapper function to activate POS sleep state.
 494 */
 495static void
 496activate_amd76x_POS(void)
 497{
 498        activate_amd76x_SLP(1);
 499}
 500#endif
 501
 502
 503#if 0
 504/*
 505 * Idle loop for single processor systems
 506 */
 507void
 508amd76x_up_idle(void)
 509{
 510        // FIXME: Optionally add non-smp idle loop here
 511}
 512#endif
 513
 514
 515/*
 516 * Idle loop for SMP systems, supports currently only 2 processors.
 517 *
 518 * Note; for 2.5 folks - not pre-empt safe
 519 */
 520static void
 521amd76x_smp_idle(void)
 522{
 523
 524        /*
 525         * Exit idle mode immediately if the CPU does not change.
 526         * Usually that means that we have some load on another CPU.
 527         */
 528        if (prs[0].idle && prs[1].idle && amd76x_pm_cfg.last_pr == smp_processor_id()) {
 529                prs[0].idle = 0;
 530                prs[1].idle = 0;
 531                /* This looks redundent as it was just checked in the if() */
 532                /* amd76x_pm_cfg.last_pr = smp_processor_id(); */
 533                return;
 534        }
 535
 536        prs[smp_processor_id()].count++;
 537
 538        /* Don't start the idle mode immediately */
 539        if (prs[smp_processor_id()].count >= LAZY_IDLE_DELAY) {
 540
 541                /* Put the current processor into idle mode */
 542                prs[smp_processor_id()].idle =
 543                        (prs[smp_processor_id()].idle ? 2 : 1);
 544
 545                /* Only idle if both processors are idle */
 546                if ((prs[0].idle==1) && (prs[1].idle==1)) {
 547                        amd76x_pm_cfg.C2_cnt++;
 548                        inb(amd76x_pm_cfg.C2_reg);
 549                }
 550        #ifdef AMD76X_C3
 551                /*
 552                 * JH: I've not been able to get into here. Could this have
 553                 * something to do with the way the kernel handles the idle
 554                 * loop, or and error that I've made?
 555                 */
 556                else if ((prs[0].idle==2) && (prs[1].idle==2)) {
 557                        amd76x_pm_cfg.C3_cnt++;
 558                        inb(amd76x_pm_cfg.C3_reg);
 559                }
 560        #endif
 561
 562                prs[smp_processor_id()].count = 0;
 563
 564        }
 565        amd76x_pm_cfg.last_pr = smp_processor_id();
 566}
 567
 568
 569/*
 570 * Finds and initializes the bridges, and then sets the idle function
 571 */
 572static int
 573amd76x_pm_main(void)
 574{
 575        int found;
 576
 577        /* Find northbridge */
 578        found = pci_register_driver(&amd_nb_driver);
 579        if (found <= 0) {
 580                printk(KERN_ERR "amd76x_pm: Could not find northbridge\n");
 581                pci_unregister_driver(&amd_nb_driver);
 582                return 1;
 583        }
 584
 585        /* Find southbridge */
 586        found = pci_register_driver(&amd_sb_driver);
 587        if (found <= 0) {
 588                printk(KERN_ERR "amd76x_pm: Could not find southbridge\n");
 589                pci_unregister_driver(&amd_sb_driver);
 590                pci_unregister_driver(&amd_nb_driver);
 591                return 1;
 592        }
 593
 594        /* Init southbridge */
 595        switch (pdev_sb->device) {
 596        case PCI_DEVICE_ID_AMD_VIPER_7413:      /* AMD-765 or 766 */
 597                config_amd766(1);
 598                break;
 599        case PCI_DEVICE_ID_AMD_VIPER_7443:      /* AMD-768 */
 600                config_amd768(1);
 601                break;
 602        default:
 603                printk(KERN_ERR "amd76x_pm: No southbridge to initialize\n");
 604                break;
 605        }
 606
 607        /* Init northbridge and queue the new idle function */
 608        switch (pdev_nb->device) {
 609        case PCI_DEVICE_ID_AMD_FE_GATE_700C:    /* AMD-762 */
 610                config_amd762(1);
 611#ifndef AMD76X_NTH
 612                amd76x_pm_cfg.curr_idle = amd76x_smp_idle;
 613#endif
 614                break;
 615        default:
 616                printk(KERN_ERR "amd76x_pm: No northbridge to initialize\n");
 617                break;
 618        }
 619
 620#ifndef AMD76X_NTH
 621        if (!amd76x_pm_cfg.curr_idle) {
 622                printk(KERN_ERR "amd76x_pm: Idle function not changed\n");
 623                pci_unregister_driver(&amd_nb_driver);
 624                pci_unregister_driver(&amd_sb_driver);
 625                return 1;
 626        }
 627
 628        amd76x_pm_cfg.orig_idle = pm_idle;
 629        pm_idle = amd76x_pm_cfg.curr_idle;
 630#endif
 631
 632#ifdef AMD76X_NTH
 633        /* Turn NTH on with maxium throttling for testing. */
 634        activate_amd76x_NTH(1, 1);
 635#endif
 636
 637#ifdef AMD76X_POS
 638        /* Testing here only. */
 639        activate_amd76x_POS();
 640#endif
 641
 642        return 0;
 643}
 644
 645
 646static int __init
 647amd76x_pm_init(void)
 648{
 649        printk(KERN_INFO "amd76x_pm: Version %s\n", VERSION);
 650        return amd76x_pm_main();
 651}
 652
 653
 654static void __exit
 655amd76x_pm_cleanup(void)
 656{
 657#ifndef AMD76X_NTH
 658        pm_idle = amd76x_pm_cfg.orig_idle;
 659
 660        /* This isn't really needed. */
 661        printk(KERN_INFO "amd76x_pm: %lu C2 calls\n", amd76x_pm_cfg.C2_cnt);
 662#ifdef AMD76X_C3
 663        printk(KERN_INFO "amd76x_pm: %lu C3 calls\n", amd76x_pm_cfg.C3_cnt);
 664#endif
 665
 666        /* 
 667         * FIXME: We want to wait until all CPUs have set the new
 668         * idle function, otherwise we will oops. This may not be
 669         * the right way to do it, but seems to work.
 670         *
 671         * - Best answer is going to be to ban unload, but when its debugged
 672         *   --- Alan
 673         */
 674        schedule();
 675        mdelay(1000);
 676#endif
 677
 678#ifdef AMD76X_NTH
 679        /* Turn NTH off*/
 680        activate_amd76x_NTH(0, 0);
 681#endif
 682
 683        pci_unregister_driver(&amd_nb_driver);
 684        pci_unregister_driver(&amd_sb_driver);
 685
 686}
 687
 688
 689MODULE_LICENSE("GPL");
 690module_init(amd76x_pm_init);
 691module_exit(amd76x_pm_cleanup);
 692
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.