Working with the modified ThinkPad 600E on Linux

Kihwal Lee

When you upgrade the Pentium II processor in your ThinkPad 600E to a Pentium III processor, you need to change some settings in the BIOS to make them work. But there are two major issues that cannot be resolved by simply changing settings in the BIOS: level 2 cache and SpeedStep.

[BIOS: the hardest software]

Although the BIOS is nothing but a collection of code that does initialization and diagnosis on system power on, it is not easy to change the code itself. It is not impossible to reverse and re-engineer the BIOS content, but it will be a extremely cumbersome process. You also need a set of equipment to deal with "mistakes". If you burn the BIOS flash chip with a buggy image, you might need to desolder the chip and reprogram. This is especially true for laptops. This is why several attempts reported at Wim's BIOS page on ThinkPad 600E mods didn't succeed.

Nevertheless, the ideal solution will be a modified BIOS that turns on the level 2 cache and set the processor speed correctly on power on and resume. But people have given up on this and settled for software solutions suck as PowerLeap control and Marcus Miller's DeepSleep utility.

[Linux can be helped]

Turning on or off of L2 cache is a simple process that can be implemented inside kernel with minimal effort. For example, enabling the L2 cache in early_cpu_init() ensures the L2 cache on the CPU to be recognized correctly. But how about the CPU speed?

We need to run Marcus Miller's code to transition from the low speed to the high speed. (Eg. 600MHZ -> 750MHz) Sharedoc had provided the hardware mod that hardwires GHI# pin to logical 1. When this is 1 and DeepSleep is performed, the processor transitions to the high speed.

when a transition occurs, some of the kernel's internal data have to be updated as well. Normally, "cpufreq" portion of the kernel takes care of it, but with the modified processor circuitry, it does not work anymore. And with the mod, we can only do low to high transition, not the other way around. So there is no point making a new cpufreq component for this mod.

[TSC]

All the processor families released ever since Pentium has a special register that stores the number of clock cycles since power-on. Linux uses it as a high resolution time reference. Since the rate at which the register is updated depends on the processor's speed, a speed change must require a recalibration of the reference. The following variables must be updated in Linux 2.6: fast_gettimeoffset, loops_per_jiffy, current_cpu_data.loops_per_jiffy, cpu_khz, and after all these, set_cyc2ns_scale() has to be invoked. Since some of the variables are not globals, the kernel code has to be changed to either insert a function and export its name or export all the variables and the set_cyc2ns_scale(). Neither is a clean solution, but the former will be a bit better, since it minimized the unnecessary exposure of kernel's internal data structure.

[Bus throttling]

The above scenario works well if the kernel uses tsc. But what if it does not? That's actually what happened to me. In my modified TP600e, the kernel reports strangely low cpu_khz on boot up. The Bogomips value is also strange. (It is independently calculated in init.c) It turns out the CPU was running slower than the low speed mode. How can this happen? Well, that's because of the Bus throttling, a power saving feature. I do not know why it is enabled on Linux, but it is, almost all the time. A shot of DeepSleep disables it, so I guess the BX chipset is not properly initialized by BIOS. The BX chipset is a PCI device so the kernel does not know how to access it before the pci bus is initialized. Well, we could hardcode some of the values, but that's not the right way. The BIOS should take care of it before booting.

With a variable cpu speed, Linux determines that tsc is unreliable and abandons it. So in my case, there is no point changing the TSC-related variables. I put "clock=pit" to force the kernel to use the good old PIT instead of TSC. An added benefit is that we no longer need to modify the kernel. We only need to update loops_per_jiffy (current_cpu_data.loops_per_jiffy is used for calculating the Bogomips), and, if you want, cpu_khz. Since we do not touch the kernel source tree, it is easy to add the module to the currently running kernel.

[TSC Vs PIT]

If the PIT is used, the kernel has to program 8254 timer, read the counter value, etc. With the TSC, reading is fast and it offers much higher resolutions. The 8254 timer still can offer microsecond precision. The user-level time resolution doesn't go below 1 ms, so it only affects kernel's internal time keeping. I do not know how adversely the kernel is affected by this in terms of performance.

The code will be released soon.