Fix timer frequency detection on Sandybridge
[coreboot.git] / src / cpu / x86 / lapic / apic_timer.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2007 Advanced Micro Devices, Inc.
5  * Copyright (C) 2009 coresystems GmbH
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20
21 #include <stdint.h>
22 #include <delay.h>
23 #include <arch/cpu.h>
24 #include <cpu/x86/msr.h>
25 #include <cpu/x86/lapic.h>
26
27 /* NOTE: This code uses global variables, so it can not be used during
28  * memory init.
29  */
30
31 static u32 timer_fsb = 0;
32
33 static int set_timer_fsb(void)
34 {
35         struct cpuinfo_x86 c;
36         int core_fsb[8] = { -1, 133, -1, 166, -1, 100, -1, -1 };
37         int core2_fsb[8] = { 266, 133, 200, 166, -1, 100, -1, -1 };
38
39         get_fms(&c, cpuid_eax(1));
40         if (c.x86 != 6)
41                 return -1;
42
43         switch (c.x86_model) {
44         case 0xe:  /* Core Solo/Duo */
45         case 0x1c: /* Atom */
46                 timer_fsb = core_fsb[rdmsr(0xcd).lo & 7];
47                 break;
48         case 0xf:  /* Core 2*/
49         case 0x17: /* Enhanced Core */
50                 timer_fsb = core2_fsb[rdmsr(0xcd).lo & 7];
51                 break;
52         case 0x2a: /* SandyBridge BCLK fixed at 100MHz*/
53                 timer_fsb = 100;
54                 break;
55         default:
56                 timer_fsb = 200;
57                 break;
58         }
59
60         return 0;
61 }
62
63 void init_timer(void)
64 {
65         /* Set the apic timer to no interrupts and periodic mode */
66         lapic_write(LAPIC_LVTT, (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED));
67
68         /* Set the divider to 1, no divider */
69         lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1);
70
71         /* Set the initial counter to 0xffffffff */
72         lapic_write(LAPIC_TMICT, 0xffffffff);
73
74         /* Set FSB frequency to a reasonable value */
75         set_timer_fsb();
76 }
77
78 void udelay(u32 usecs)
79 {
80         u32 start, value, ticks;
81
82         if (!timer_fsb)
83                 init_timer();
84
85         /* Calculate the number of ticks to run, our FSB runs at timer_fsb Mhz */
86         ticks = usecs * timer_fsb;
87         start = lapic_read(LAPIC_TMCCT);
88         do {
89                 value = lapic_read(LAPIC_TMCCT);
90         } while((start - value) < ticks);
91 }