[CPUFREQ] Longhaul - Simplier minmult
[powerpc.git] / arch / i386 / kernel / cpu / cpufreq / longhaul.c
index c548daa..8f65ebe 100644 (file)
 #define        CPU_EZRA        3
 #define        CPU_EZRA_T      4
 #define        CPU_NEHEMIAH    5
+#define        CPU_NEHEMIAH_C  6
+
+/* Flags */
+#define USE_ACPI_C3            (1 << 1)
+#define USE_NORTHBRIDGE                (1 << 2)
+#define USE_VT8235             (1 << 3)
 
 static int cpu_model;
 static unsigned int numscales=16;
@@ -68,11 +74,10 @@ static unsigned int minmult, maxmult;
 static int can_scale_voltage;
 static struct acpi_processor *pr = NULL;
 static struct acpi_processor_cx *cx = NULL;
-static int port22_en;
+static u8 longhaul_flags;
 
 /* Module parameters */
 static int scale_voltage;
-static int ignore_latency;
 
 #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
 
@@ -80,7 +85,6 @@ static int ignore_latency;
 /* Clock ratios multiplied by 10 */
 static int clock_ratio[32];
 static int eblcr_table[32];
-static unsigned int highest_speed, lowest_speed; /* kHz */
 static int longhaul_version;
 static struct cpufreq_frequency_table *longhaul_table;
 
@@ -178,7 +182,7 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
        safe_halt();
        /* Change frequency on next halt or sleep */
        wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
-       if (port22_en) {
+       if (!cx_address) {
                ACPI_FLUSH_CPU_CACHE();
                /* Invoke C1 */
                halt();
@@ -189,7 +193,6 @@ static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
                /* Dummy op - must do something useless after P_LVL3 read */
                t = inl(acpi_fadt.xpm_tmr_blk.address);
        }
-
        /* Disable bus ratio bit */
        local_irq_disable();
        longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
@@ -243,15 +246,14 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
        outb(0xFF,0xA1);        /* Overkill */
        outb(0xFE,0x21);        /* TMR0 only */
 
-       if (pr->flags.bm_control) {
+       if (longhaul_flags & USE_NORTHBRIDGE) {
+               /* Disable AGP and PCI arbiters */
+               outb(3, 0x22);
+       } else if ((pr != NULL) && pr->flags.bm_control) {
                /* Disable bus master arbitration */
                acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1,
                                  ACPI_MTX_DO_NOT_LOCK);
-       } else if (port22_en) {
-               /* Disable AGP and PCI arbiters */
-               outb(3, 0x22);
        }
-
        switch (longhaul_version) {
 
        /*
@@ -278,22 +280,25 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
         * to work in practice.
         */
        case TYPE_POWERSAVER:
-               /* Don't allow wakeup */
-               acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0,
-                                 ACPI_MTX_DO_NOT_LOCK);
-               do_powersaver(cx->address, clock_ratio_index);
+               if (longhaul_flags & USE_ACPI_C3) {
+                       /* Don't allow wakeup */
+                       acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0,
+                                         ACPI_MTX_DO_NOT_LOCK);
+                       do_powersaver(cx->address, clock_ratio_index);
+               } else {
+                       do_powersaver(0, clock_ratio_index);
+               }
                break;
        }
 
-       if (pr->flags.bm_control) {
+       if (longhaul_flags & USE_NORTHBRIDGE) {
+               /* Enable arbiters */
+               outb(0, 0x22);
+       } else if ((pr != NULL) && pr->flags.bm_control) {
                /* Enable bus master arbitration */
                acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0,
                                  ACPI_MTX_DO_NOT_LOCK);
-       } else if (port22_en) {
-               /* Enable arbiters */
-               outb(0, 0x22);
        }
-
        outb(pic2_mask,0xA1);   /* restore mask */
        outb(pic1_mask,0x21);
 
@@ -314,31 +319,19 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
 
 #define ROUNDING       0xf
 
-static int _guess(int guess)
-{
-       int target;
-
-       target = ((maxmult/10)*guess);
-       if (maxmult%10 != 0)
-               target += (guess/2);
-       target += ROUNDING/2;
-       target &= ~ROUNDING;
-       return target;
-}
-
-
-static int guess_fsb(void)
+static int guess_fsb(int mult)
 {
-       int speed = (cpu_khz/1000);
+       int speed = cpu_khz / 1000;
        int i;
-       int speeds[3] = { 66, 100, 133 };
-
-       speed += ROUNDING/2;
-       speed &= ~ROUNDING;
-
-       for (i=0; i<3; i++) {
-               if (_guess(speeds[i]) == speed)
-                       return speeds[i];
+       int speeds[] = { 666, 1000, 1333, 2000 };
+       int f_max, f_min;
+
+       for (i = 0; i < 4; i++) {
+               f_max = ((speeds[i] * mult) + 50) / 100;
+               f_max += (ROUNDING / 2);
+               f_min = f_max - ROUNDING;
+               if ((speed <= f_max) && (speed >= f_min))
+                       return speeds[i] / 10;
        }
        return 0;
 }
@@ -346,86 +339,40 @@ static int guess_fsb(void)
 
 static int __init longhaul_get_ranges(void)
 {
-       unsigned long invalue;
-       unsigned int ezra_t_multipliers[32]= {
-                       90,  30,  40, 100,  55,  35,  45,  95,
-                       50,  70,  80,  60, 120,  75,  85,  65,
-                       -1, 110, 120,  -1, 135, 115, 125, 105,
-                       130, 150, 160, 140,  -1, 155,  -1, 145 };
        unsigned int j, k = 0;
-       union msr_longhaul longhaul;
-       unsigned long lo, hi;
-       unsigned int eblcr_fsb_table_v1[] = { 66, 133, 100, -1 };
-       unsigned int eblcr_fsb_table_v2[] = { 133, 100, -1, 66 };
+       int mult;
 
-       switch (longhaul_version) {
-       case TYPE_LONGHAUL_V1:
-       case TYPE_LONGHAUL_V2:
-               /* Ugh, Longhaul v1 didn't have the min/max MSRs.
-                  Assume min=3.0x & max = whatever we booted at. */
+       /* Get current frequency */
+       mult = longhaul_get_cpu_mult();
+       if (mult == -1) {
+               printk(KERN_INFO PFX "Invalid (reserved) multiplier!\n");
+               return -EINVAL;
+       }
+       fsb = guess_fsb(mult);
+       if (fsb == 0) {
+               printk(KERN_INFO PFX "Invalid (reserved) FSB!\n");
+               return -EINVAL;
+       }
+       /* Get max multiplier - as we always did.
+        * Longhaul MSR is usefull only when voltage scaling is enabled.
+        * C3 is booting at max anyway. */
+       maxmult = mult;
+       /* Get min multiplier */
+       switch (cpu_model) {
+       case CPU_NEHEMIAH:
+               minmult = 50;
+               break;
+       case CPU_NEHEMIAH_C:
+               minmult = 40;
+               break;
+       default:
                minmult = 30;
-               maxmult = longhaul_get_cpu_mult();
-               rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
-               invalue = (lo & (1<<18|1<<19)) >>18;
-               if (cpu_model==CPU_SAMUEL || cpu_model==CPU_SAMUEL2)
-                       fsb = eblcr_fsb_table_v1[invalue];
-               else
-                       fsb = guess_fsb();
                break;
-
-       case TYPE_POWERSAVER:
-               /* Ezra-T */
-               if (cpu_model==CPU_EZRA_T) {
-                       rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
-                       invalue = longhaul.bits.MaxMHzBR;
-                       if (longhaul.bits.MaxMHzBR4)
-                               invalue += 16;
-                       maxmult=ezra_t_multipliers[invalue];
-
-                       invalue = longhaul.bits.MinMHzBR;
-                       if (longhaul.bits.MinMHzBR4 == 1)
-                               minmult = 30;
-                       else
-                               minmult = ezra_t_multipliers[invalue];
-                       fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB];
-                       break;
-               }
-
-               /* Nehemiah */
-               if (cpu_model==CPU_NEHEMIAH) {
-                       rdmsrl (MSR_VIA_LONGHAUL, longhaul.val);
-
-                       /*
-                        * TODO: This code works, but raises a lot of questions.
-                        * - Some Nehemiah's seem to have broken Min/MaxMHzBR's.
-                        *   We get around this by using a hardcoded multiplier of 4.0x
-                        *   for the minimimum speed, and the speed we booted up at for the max.
-                        *   This is done in longhaul_get_cpu_mult() by reading the EBLCR register.
-                        * - According to some VIA documentation EBLCR is only
-                        *   in pre-Nehemiah C3s. How this still works is a mystery.
-                        *   We're possibly using something undocumented and unsupported,
-                        *   But it works, so we don't grumble.
-                        */
-                       minmult=40;
-                       maxmult=longhaul_get_cpu_mult();
-
-                       /* Starting with the 1.2GHz parts, theres a 200MHz bus. */
-                       if ((cpu_khz/maxmult) > 13400)
-                               fsb = 200;
-                       else
-                               fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB];
-                       break;
-               }
        }
 
        dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n",
                 minmult/10, minmult%10, maxmult/10, maxmult%10);
 
-       if (fsb == -1) {
-               printk (KERN_INFO PFX "Invalid (reserved) FSB!\n");
-               return -EINVAL;
-       }
-
        highest_speed = calc_speed(maxmult);
        lowest_speed = calc_speed(minmult);
        dprintk ("FSB:%dMHz  Lowest speed: %s   Highest speed:%s\n", fsb,
@@ -591,20 +538,50 @@ static int enable_arbiter_disable(void)
        if (dev != NULL) {
                /* Enable access to port 0x22 */
                pci_read_config_byte(dev, reg, &pci_cmd);
-               if ( !(pci_cmd & 1<<7) ) {
+               if (!(pci_cmd & 1<<7)) {
                        pci_cmd |= 1<<7;
                        pci_write_config_byte(dev, reg, pci_cmd);
+                       pci_read_config_byte(dev, reg, &pci_cmd);
+                       if (!(pci_cmd & 1<<7)) {
+                               printk(KERN_ERR PFX
+                                       "Can't enable access to port 0x22.\n");
+                               return 0;
+                       }
                }
                return 1;
        }
        return 0;
 }
 
+static int longhaul_setup_vt8235(void)
+{
+       struct pci_dev *dev;
+       u8 pci_cmd;
+
+       /* Find VT8235 southbridge */
+       dev = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
+       if (dev != NULL) {
+               /* Set transition time to max */
+               pci_read_config_byte(dev, 0xec, &pci_cmd);
+               pci_cmd &= ~(1 << 2);
+               pci_write_config_byte(dev, 0xec, pci_cmd);
+               pci_read_config_byte(dev, 0xe4, &pci_cmd);
+               pci_cmd &= ~(1 << 7);
+               pci_write_config_byte(dev, 0xe4, pci_cmd);
+               pci_read_config_byte(dev, 0xe5, &pci_cmd);
+               pci_cmd |= 1 << 7;
+               pci_write_config_byte(dev, 0xe5, pci_cmd);
+               return 1;
+       }
+       return 0;
+}
+
 static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
 {
        struct cpuinfo_x86 *c = cpu_data;
        char *cpuname=NULL;
        int ret;
+       int vt8235_present;
 
        /* Check what we have on this motherboard */
        switch (c->x86_model) {
@@ -650,24 +627,24 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                break;
 
        case 9:
-               cpu_model = CPU_NEHEMIAH;
                longhaul_version = TYPE_POWERSAVER;
-               numscales=32;
+               numscales = 32;
+               memcpy(clock_ratio,
+                      nehemiah_clock_ratio,
+                      sizeof(nehemiah_clock_ratio));
+               memcpy(eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
                switch (c->x86_mask) {
                case 0 ... 1:
+                       cpu_model = CPU_NEHEMIAH;
                        cpuname = "C3 'Nehemiah A' [C5N]";
-                       memcpy (clock_ratio, nehemiah_a_clock_ratio, sizeof(nehemiah_a_clock_ratio));
-                       memcpy (eblcr_table, nehemiah_a_eblcr, sizeof(nehemiah_a_eblcr));
                        break;
                case 2 ... 4:
+                       cpu_model = CPU_NEHEMIAH;
                        cpuname = "C3 'Nehemiah B' [C5N]";
-                       memcpy (clock_ratio, nehemiah_b_clock_ratio, sizeof(nehemiah_b_clock_ratio));
-                       memcpy (eblcr_table, nehemiah_b_eblcr, sizeof(nehemiah_b_eblcr));
                        break;
                case 5 ... 15:
+                       cpu_model = CPU_NEHEMIAH_C;
                        cpuname = "C3 'Nehemiah C' [C5N]";
-                       memcpy (clock_ratio, nehemiah_c_clock_ratio, sizeof(nehemiah_c_clock_ratio));
-                       memcpy (eblcr_table, nehemiah_c_eblcr, sizeof(nehemiah_c_eblcr));
                        break;
                }
                break;
@@ -688,41 +665,52 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
                break;
        };
 
+       /* Doesn't hurt */
+       vt8235_present = longhaul_setup_vt8235();
+
        /* Find ACPI data for processor */
-       acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
-                           &longhaul_walk_callback, NULL, (void *)&pr);
-       if (pr == NULL)
-               goto err_acpi;
+       acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+                               ACPI_UINT32_MAX, &longhaul_walk_callback,
+                               NULL, (void *)&pr);
 
-       if (longhaul_version == TYPE_POWERSAVER) {
-               /* Check ACPI support for C3 state */
+       /* Check ACPI support for C3 state */
+       if (pr != NULL && longhaul_version == TYPE_POWERSAVER) {
                cx = &pr->power.states[ACPI_STATE_C3];
-               if (cx->address > 0 &&
-                  (cx->latency <= 1000 || ignore_latency != 0) ) {
+               if (cx->address > 0 && cx->latency <= 1000) {
+                       longhaul_flags |= USE_ACPI_C3;
                        goto print_support_type;
                }
        }
+       /* Check if northbridge is friendly */
+       if (enable_arbiter_disable()) {
+               longhaul_flags |= USE_NORTHBRIDGE;
+               goto print_support_type;
+       }
+       /* Use VT8235 southbridge if present */
+       if (longhaul_version == TYPE_POWERSAVER && vt8235_present) {
+               longhaul_flags |= USE_VT8235;
+               goto print_support_type;
+       }
        /* Check ACPI support for bus master arbiter disable */
-       if (!pr->flags.bm_control) {
-               if (enable_arbiter_disable()) {
-                       port22_en = 1;
-               } else {
-                       goto err_acpi;
-               }
+       if ((pr == NULL) || !(pr->flags.bm_control)) {
+               printk(KERN_ERR PFX
+                       "No ACPI support. Unsupported northbridge.\n");
+               return -ENODEV;
        }
+
 print_support_type:
-       if (!port22_en) {
-               printk (KERN_INFO PFX "Using ACPI support.\n");
-       } else {
+       if (longhaul_flags & USE_NORTHBRIDGE)
                printk (KERN_INFO PFX "Using northbridge support.\n");
-       }
+       else if (longhaul_flags & USE_VT8235)
+               printk (KERN_INFO PFX "Using VT8235 support.\n");
+       else
+               printk (KERN_INFO PFX "Using ACPI support.\n");
 
        ret = longhaul_get_ranges();
        if (ret != 0)
                return ret;
 
-       if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) &&
-                (scale_voltage != 0))
+       if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
                longhaul_setup_voltagescaling();
 
        policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
@@ -736,10 +724,6 @@ print_support_type:
        cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
 
        return 0;
-
-err_acpi:
-       printk(KERN_ERR PFX "No ACPI support. Unsupported northbridge. Aborting.\n");
-       return -ENODEV;
 }
 
 static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
@@ -774,8 +758,8 @@ static int __init longhaul_init(void)
 
 #ifdef CONFIG_SMP
        if (num_online_cpus() > 1) {
-               return -ENODEV;
                printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n");
+               return -ENODEV;
        }
 #endif
 #ifdef CONFIG_X86_IO_APIC
@@ -787,8 +771,10 @@ static int __init longhaul_init(void)
        switch (c->x86_model) {
        case 6 ... 9:
                return cpufreq_register_driver(&longhaul_driver);
+       case 10:
+               printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7\n");
        default:
-               printk (KERN_INFO PFX "Unknown VIA CPU. Contact davej@codemonkey.org.uk\n");
+               ;;
        }
 
        return -ENODEV;
@@ -812,8 +798,6 @@ static void __exit longhaul_exit(void)
 
 module_param (scale_voltage, int, 0644);
 MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
-module_param(ignore_latency, int, 0644);
-MODULE_PARM_DESC(ignore_latency, "Skip ACPI C3 latency test");
 
 MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
 MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");