Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3168
-gerrit
commit f64c6a715c716adc9236c2157141b3259fca5e5a
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Wed May 1 15:27:09 2013 -0500
x86: add TSC_CONSTANT_RATE option
Some boards use the local apic for udelay(), but they also provide their
own implementation of udelay() for SMM. The reason for using the local
apic for udelay() in ramstage is to not have to pay the penalty of
calibrating the TSC frequency. Therefore provide a TSC_CONSTANT_RATE
option to indicate that TSC calibration is not needed. Instead it relies
on the presence of a tsc_freq_mhz() function provided by the cpu/board.
Additionally, assume that if TSC_CONSTANT_RATE is selected the udelay()
function in SMM will be the tsc.
Change-Id: I1629c2fbe3431772b4e80495160584fb6f599e9e
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/cpu/x86/Kconfig | 7 +++++++
src/cpu/x86/tsc/Makefile.inc | 4 ++++
src/cpu/x86/tsc/delay_tsc.c | 27 ++++++++++++++++++++++++---
src/include/cpu/x86/tsc.h | 4 ++++
4 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/src/cpu/x86/Kconfig b/src/cpu/x86/Kconfig
index 5cf40fa..c64a8e4 100644
--- a/src/cpu/x86/Kconfig
+++ b/src/cpu/x86/Kconfig
@@ -25,6 +25,13 @@ config UDELAY_TSC
bool
default n
+config TSC_CONSTANT_RATE
+ def_bool n
+ depends on UDELAY_TSC
+ help
+ This option asserts that the TSC ticks at a known constant rate.
+ Therefore, no TSC calibration is required.
+
config TSC_MONOTONIC_TIMER
def_bool n
depends on UDELAY_TSC
diff --git a/src/cpu/x86/tsc/Makefile.inc b/src/cpu/x86/tsc/Makefile.inc
index 44bfe85..3bbae84 100644
--- a/src/cpu/x86/tsc/Makefile.inc
+++ b/src/cpu/x86/tsc/Makefile.inc
@@ -1,2 +1,6 @@
ramstage-$(CONFIG_UDELAY_TSC) += delay_tsc.c
+romstage-$(CONFIG_TSC_CONSTANT_RATE) += delay_tsc.c
+ifeq ($(CONFIG_HAVE_SMI_HANDLER),y)
+smm-$(CONFIG_TSC_CONSTANT_RATE) += delay_tsc.c
+endif
diff --git a/src/cpu/x86/tsc/delay_tsc.c b/src/cpu/x86/tsc/delay_tsc.c
index e4993d0..0540496 100644
--- a/src/cpu/x86/tsc/delay_tsc.c
+++ b/src/cpu/x86/tsc/delay_tsc.c
@@ -5,8 +5,16 @@
#include <smp/spinlock.h>
#include <delay.h>
+#if !defined(__PRE_RAM__)
+
static unsigned long clocks_per_usec;
+#if CONFIG_TSC_CONSTANT_RATE
+static unsigned long calibrate_tsc(void)
+{
+ return tsc_freq_mhz();
+}
+#else /* CONFIG_TSC_CONSTANT_RATE */
#if !CONFIG_TSC_CALIBRATE_WITH_IO
#define CLOCK_TICK_RATE 1193180U /* Underlying HZ */
@@ -139,6 +147,7 @@ static unsigned long long calibrate_tsc(void)
#endif /* CONFIG_TSC_CALIBRATE_WITH_IO */
+#endif /* CONFIG_TSC_CONSTANT_RATE */
void init_timer(void)
{
@@ -148,15 +157,27 @@ void init_timer(void)
}
}
+static inline unsigned long get_clocks_per_usec(void)
+{
+ init_timer();
+ return clocks_per_usec;
+}
+#else /* !defined(__PRE_RAM__) */
+/* romstage calls into cpu/board specific function every time. */
+static inline unsigned long get_clocks_per_usec(void)
+{
+ return tsc_freq_mhz();
+}
+#endif /* !defined(__PRE_RAM__) */
+
void udelay(unsigned us)
{
unsigned long long count;
unsigned long long stop;
unsigned long long clocks;
- init_timer();
clocks = us;
- clocks *= clocks_per_usec;
+ clocks *= get_clocks_per_usec();
count = rdtscll();
stop = clocks + count;
while(stop > count) {
@@ -165,7 +186,7 @@ void udelay(unsigned us)
}
}
-#if CONFIG_TSC_MONOTONIC_TIMER
+#if CONFIG_TSC_MONOTONIC_TIMER && !defined(__PRE_RAM__) && !defined(__SMM__)
#include <timer.h>
static struct monotonic_counter {
diff --git a/src/include/cpu/x86/tsc.h b/src/include/cpu/x86/tsc.h
index 6ce7f5f..8e49a66 100644
--- a/src/include/cpu/x86/tsc.h
+++ b/src/include/cpu/x86/tsc.h
@@ -40,4 +40,8 @@ static inline unsigned long long rdtscll(void)
}
#endif
+#if CONFIG_TSC_CONSTANT_RATE
+unsigned long tsc_freq_mhz(void);
+#endif
+
#endif /* CPU_X86_TSC_H */
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3171
-gerrit
commit 54c54297f582c3187a1f5f6c357b817901996ed0
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Wed May 1 15:55:14 2013 -0500
x86: harden tsc udelay() function
Since the TSC udelay() fucntion can be used in SMM that means the
TSC can count up to whatever value. The current loop was not handling
TSC rollover properly. In most cases this should not matter as the TSC
typically starts ticking at value 0, and it would take a very long time
to roll it over. However, it is my understanding that this behavior is
not guaranteed. Theoretically the TSC could start or be be written to
with a large value that would cause the rollover.
Change-Id: I2f11a5bc4f27d5543e74f8224811fa91e4a55484
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/cpu/x86/tsc/delay_tsc.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/cpu/x86/tsc/delay_tsc.c b/src/cpu/x86/tsc/delay_tsc.c
index 0540496..0e2a9c0 100644
--- a/src/cpu/x86/tsc/delay_tsc.c
+++ b/src/cpu/x86/tsc/delay_tsc.c
@@ -172,18 +172,18 @@ static inline unsigned long get_clocks_per_usec(void)
void udelay(unsigned us)
{
- unsigned long long count;
- unsigned long long stop;
- unsigned long long clocks;
+ unsigned long long start;
+ unsigned long long current;
+ unsigned long long clocks;
+ start = rdtscll();
clocks = us;
clocks *= get_clocks_per_usec();
- count = rdtscll();
- stop = clocks + count;
- while(stop > count) {
+ current = rdtscll();
+ while((current - start) < clocks) {
cpu_relax();
- count = rdtscll();
- }
+ current = rdtscll();
+ }
}
#if CONFIG_TSC_MONOTONIC_TIMER && !defined(__PRE_RAM__) && !defined(__SMM__)
David Hendricks (dhendrix(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3190
-gerrit
commit 2151feacbf67dd3ce50e254903e28274f89399e3
Author: David Hendricks <dhendrix(a)chromium.org>
Date: Fri May 3 12:28:11 2013 -0700
timer.h: add mono_time_diff_microseconds()
The current way to get a simple mono_time difference is:
1. Declare a rela_time struct
2. Assign it the value of mono_time_diff(t1, t2)
3. Get microseconds from it using rela_time_in_microseconds().
This patch adds a simpler method. Now one only needs to call
mono_time_diff_microseconds(t1, t2) to obtain the same value which
is produced from the above three steps.
Change-Id: Ibfc9cd211e48e8e60a0a7703bff09cee3250e88b
Signed-off-by: David Hendricks <dhendrix(a)chromium.org>
---
src/include/timer.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/include/timer.h b/src/include/timer.h
index e950c81..c32effe 100644
--- a/src/include/timer.h
+++ b/src/include/timer.h
@@ -155,4 +155,12 @@ static inline long rela_time_in_microseconds(const struct rela_time *rt)
return rt->microseconds;
}
+static inline long mono_time_diff_microseconds(const struct mono_time *t1,
+ const struct mono_time *t2)
+{
+ struct rela_time rt;
+ rt = mono_time_diff(t1, t2);
+ return rela_time_in_microseconds(&rt);
+}
+
#endif /* TIMER_H */
David Hendricks (dhendrix(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3190
-gerrit
commit eb8b8199b803774d5e65ca66d2e54db94bbc5c57
Author: David Hendricks <dhendrix(a)chromium.org>
Date: Fri May 3 12:28:11 2013 -0700
timer.h: add mono_time_diff_microseconds()
The current way to get a simple mono_time difference is:
1. Declare a rela_time struct
2. Assign it the value of mono_time_diff(t1, t2)
3. Get microseconds from it using rela_time_in_microseconds().
This patch adds a simpler method. Now one only needs to call
mono_time_diff_microseconds(t1, t2) to obtain the same value which
is produced from the above three steps.
Change-Id: Ibfc9cd211e48e8e60a0a7703bff09cee3250e88b
Signed-off-by: David Hendricks <dhendrix(a)chromium.org>
---
src/include/timer.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/include/timer.h b/src/include/timer.h
index e950c81..e755a90 100644
--- a/src/include/timer.h
+++ b/src/include/timer.h
@@ -153,6 +153,15 @@ static inline struct rela_time current_time_from(const struct mono_time *t)
static inline long rela_time_in_microseconds(const struct rela_time *rt)
{
return rt->microseconds;
+
+}
+
+static inline long mono_time_diff_microseconds(const struct mono_time *t1,
+ const struct mono_time *t2)
+{
+ struct rela_time rt;
+ rt = mono_time_diff(t1, t2);
+ return rela_time_in_microseconds(&rt);
}
#endif /* TIMER_H */