CINXE.COM

LKML: john stultz: [PATCH 11/11] Time: i386 Clocksource Drivers

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>LKML: john stultz: [PATCH 11/11] Time: i386 Clocksource Drivers</title><link href="/css/message.css" rel="stylesheet" type="text/css" /><link href="/css/wrap.css" rel="alternate stylesheet" type="text/css" title="wrap" /><link href="/css/nowrap.css" rel="stylesheet" type="text/css" title="nowrap" /><link href="/favicon.ico" rel="shortcut icon" /><script src="/js/simple-calendar.js" type="text/javascript"></script><script src="/js/styleswitcher.js" type="text/javascript"></script><link rel="alternate" type="application/rss+xml" title="lkml.org : last 100 messages" href="/rss.php" /><link rel="alternate" type="application/rss+xml" title="lkml.org : last messages by john stultz" href="/groupie.php?aid=1263" /><!--Matomo--><script> var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setDoNotTrack", true]); _paq.push(["disableCookies"]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="//m.lkml.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script><!--End Matomo Code--></head><body onload="es.jasper.simpleCalendar.init();" itemscope="itemscope" itemtype="http://schema.org/BlogPosting"><table border="0" cellpadding="0" cellspacing="0"><tr><td width="180" align="center"><a href="/"><img style="border:0;width:135px;height:32px" src="/images/toprowlk.gif" alt="lkml.org" /></a></td><td width="32">聽</td><td class="nb"><div><a class="nb" href="/lkml"> [lkml]</a> 聽 <a class="nb" href="/lkml/2005"> [2005]</a> 聽 <a class="nb" href="/lkml/2005/12"> [Dec]</a> 聽 <a class="nb" href="/lkml/2005/12/15"> [15]</a> 聽 <a class="nb" href="/lkml/last100"> [last100]</a> 聽 <a href="/rss.php"><img src="/images/rss-or.gif" border="0" alt="RSS Feed" /></a></div><div>Views: <a href="#" class="nowrap" onclick="setActiveStyleSheet('wrap');return false;">[wrap]</a><a href="#" class="wrap" onclick="setActiveStyleSheet('nowrap');return false;">[no wrap]</a> 聽 <a class="nb" href="/lkml/mheaders/2005/12/15/414" onclick="this.href='/lkml/headers'+'/2005/12/15/414';">[headers]</a>聽 <a href="/lkml/bounce/2005/12/15/414">[forward]</a>聽 </div></td><td width="32">聽</td></tr><tr><td valign="top"><div class="es-jasper-simpleCalendar" baseurl="/lkml/"></div><div class="threadlist">Messages in this thread</div><ul class="threadlist"><li class="root"><a href="/lkml/2005/12/15/405">First message in thread</a></li><li><a href="/lkml/2005/12/15/405">john stultz</a><ul><li><a href="/lkml/2005/12/15/404">john stultz</a></li><li><a href="/lkml/2005/12/15/406">john stultz</a></li><li><a href="/lkml/2005/12/15/407">john stultz</a><ul><li><a href="/lkml/2006/1/3/509">Andrew Morton</a><ul><li><a href="/lkml/2006/1/4/72">john stultz</a><ul><li><a href="/lkml/2006/1/4/76">Andrew Morton</a></li></ul></li></ul></li></ul></li><li><a href="/lkml/2005/12/15/408">john stultz</a></li><li><a href="/lkml/2005/12/15/409">john stultz</a></li><li><a href="/lkml/2005/12/15/410">john stultz</a></li><li><a href="/lkml/2005/12/15/411">john stultz</a></li><li><a href="/lkml/2005/12/15/412">john stultz</a></li><li><a href="/lkml/2005/12/15/413">john stultz</a></li><li class="origin"><a href="">john stultz</a></li><li><a href="/lkml/2005/12/15/415">john stultz</a></li></ul></li></ul><div class="threadlist">Patch in this message</div><ul class="threadlist"><li><a href="/lkml/diff/2005/12/15/414/1">Get diff 1</a></li></ul></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerl.gif" width="32" height="32" alt="/" /></td><td class="c" rowspan="2" valign="top" style="padding-top: 1em"><table><tr><td><table><tr><td class="lp">Date</td><td class="rp" itemprop="datePublished">Thu, 15 Dec 2005 18:08:32 -0700</td></tr><tr><td class="lp">From</td><td class="rp" itemprop="author">john stultz &lt;&gt;</td></tr><tr><td class="lp">Subject</td><td class="rp" itemprop="name">[PATCH 11/11] Time: i386 Clocksource Drivers</td></tr></table></td><td></td></tr></table><pre itemprop="articleBody">Andrew, All,<br /> This patch implements the time sources for i386 (acpi_pm, <br />cyclone, hpet, pit, and tsc). The patch should apply on top of the <br />timeofday-arch-i386-part6 patch.<br /><br />The patch should be fairly straight forward, only adding the new <br />clocksources.<br /><br />Andrew, please consider for inclusion into your tree.<br /><br />thanks<br />-john<br /><br />Signed-off-by: John Stultz &lt;johnstul&#64;us.ibm.com&gt;<br /><br /> arch/i386/kernel/Makefile | 1 <br /> arch/i386/kernel/hpet.c | 69 +++++++++++++++++++++++<br /> arch/i386/kernel/i8253.c | 60 ++++++++++++++++++++<br /> arch/i386/kernel/tsc.c | 85 ++++++++++++++++++++++++++++-<br /> drivers/Makefile | 1 <br /> drivers/clocksource/Makefile | 2 <br /> drivers/clocksource/acpi_pm.c | 123 ++++++++++++++++++++++++++++++++++++++++++<br /> drivers/clocksource/cyclone.c | 121 +++++++++++++++++++++++++++++++++++++++++<br /> 8 files changed, 461 insertions(+), 1 deletion(-)<br /><br />linux-2.6.15-rc5_timeofday-clocks-i386_B14.patch<br />============================================<br />diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile<br />index 5d1d9c9..984d488 100644<br />--- a/arch/i386/kernel/Makefile<br />+++ b/arch/i386/kernel/Makefile<br />&#64;&#64; -33,6 +33,7 &#64;&#64; obj-$(CONFIG_ACPI_SRAT) += srat.o<br /> obj-$(CONFIG_HPET_TIMER) += time_hpet.o<br /> obj-$(CONFIG_EFI) += efi.o efi_stub.o<br /> obj-$(CONFIG_EARLY_PRINTK) += early_printk.o<br />+obj-$(CONFIG_HPET_TIMER) += hpet.o<br /> <br /> EXTRA_AFLAGS := -traditional<br /> <br />diff --git a/arch/i386/kernel/hpet.c b/arch/i386/kernel/hpet.c<br />new file mode 100644<br />index 0000000..a620d15<br />--- /dev/null<br />+++ b/arch/i386/kernel/hpet.c<br />&#64;&#64; -0,0 +1,69 &#64;&#64;<br />+#include &lt;linux/clocksource.h&gt;<br />+#include &lt;linux/errno.h&gt;<br />+#include &lt;linux/hpet.h&gt;<br />+#include &lt;linux/init.h&gt;<br />+<br />+#include &lt;asm/hpet.h&gt;<br />+#include &lt;asm/io.h&gt;<br />+<br />+#define HPET_MASK 0xFFFFFFFF<br />+#define HPET_SHIFT 22<br />+<br />+/* FSEC = 10^-15 NSEC = 10^-9 */<br />+#define FSEC_PER_NSEC 1000000<br />+<br />+static void *hpet_ptr;<br />+<br />+static cycle_t read_hpet(void)<br />+{<br />+ return (cycle_t)readl(hpet_ptr);<br />+}<br />+<br />+struct clocksource clocksource_hpet = {<br />+ .name = "hpet",<br />+ .rating = 250,<br />+ .read = read_hpet,<br />+ .mask = (cycle_t)HPET_MASK,<br />+ .mult = 0, /* set below */<br />+ .shift = HPET_SHIFT,<br />+ .is_continuous = 1,<br />+};<br />+<br />+static int __init init_hpet_clocksource(void)<br />+{<br />+ unsigned long hpet_period;<br />+ void __iomem* hpet_base;<br />+ u64 tmp;<br />+<br />+ if (!hpet_address)<br />+ return -ENODEV;<br />+<br />+ /* calculate the hpet address: */<br />+ hpet_base =<br />+ (void __iomem*)ioremap_nocache(hpet_address, HPET_MMAP_SIZE);<br />+ hpet_ptr = hpet_base + HPET_COUNTER;<br />+<br />+ /* calculate the frequency: */<br />+ hpet_period = readl(hpet_base + HPET_PERIOD);<br />+<br />+ /*<br />+ * hpet period is in femto seconds per cycle<br />+ * so we need to convert this to ns/cyc units<br />+ * aproximated by mult/2^shift<br />+ *<br />+ * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift<br />+ * fsec/cyc * 1ns/1000000fsec * 2^shift = mult<br />+ * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult<br />+ * (fsec/cyc &lt;&lt; shift)/1000000 = mult<br />+ * (hpet_period &lt;&lt; shift)/FSEC_PER_NSEC = mult<br />+ */<br />+ tmp = (u64)hpet_period &lt;&lt; HPET_SHIFT;<br />+ do_div(tmp, FSEC_PER_NSEC);<br />+ clocksource_hpet.mult = (u32)tmp;<br />+<br />+ register_clocksource(&amp;clocksource_hpet);<br />+<br />+ return 0;<br />+}<br />+<br />+module_init(init_hpet_clocksource);<br />diff --git a/arch/i386/kernel/i8253.c b/arch/i386/kernel/i8253.c<br />index e4b7f7d..5e62edd 100644<br />--- a/arch/i386/kernel/i8253.c<br />+++ b/arch/i386/kernel/i8253.c<br />&#64;&#64; -2,6 +2,7 &#64;&#64;<br /> * i8253.c 8253/PIT functions<br /> *<br /> */<br />+#include &lt;linux/clocksource.h&gt;<br /> #include &lt;linux/spinlock.h&gt;<br /> #include &lt;linux/jiffies.h&gt;<br /> #include &lt;linux/sysdev.h&gt;<br />&#64;&#64; -57,3 +58,62 &#64;&#64; static int __init init_timer_sysfs(void)<br /> }<br /> <br /> device_initcall(init_timer_sysfs);<br />+<br />+<br />+/*<br />+ * Since the PIT overflows every tick, its not very useful<br />+ * to just read by itself. So use jiffies to emulate a free<br />+ * running counter:<br />+ */<br />+static cycle_t pit_read(void)<br />+{<br />+ unsigned long flags, seq;<br />+ int count;<br />+ u64 jifs;<br />+<br />+ do {<br />+ seq = read_seqbegin(&amp;xtime_lock);<br />+<br />+ spin_lock_irqsave(&amp;i8253_lock, flags);<br />+ outb_p(0x00, PIT_MODE); /* latch the count ASAP */<br />+ count = inb_p(PIT_CH0); /* read the latched count */<br />+ count |= inb_p(PIT_CH0) &lt;&lt; 8;<br />+<br />+ /* VIA686a test code... reset the latch if count &gt; max + 1 */<br />+ if (count &gt; LATCH) {<br />+ outb_p(0x34, PIT_MODE);<br />+ outb_p(LATCH &amp; 0xff, PIT_CH0);<br />+ outb(LATCH &gt;&gt; 8, PIT_CH0);<br />+ count = LATCH - 1;<br />+ }<br />+ spin_unlock_irqrestore(&amp;i8253_lock, flags);<br />+<br />+ jifs = jiffies_64;<br />+ } while (read_seqretry(&amp;xtime_lock, seq));<br />+<br />+ jifs -= INITIAL_JIFFIES;<br />+ count = (LATCH-1) - count;<br />+<br />+ return (cycle_t)(jifs * LATCH) + count;<br />+}<br />+<br />+static struct clocksource clocksource_pit = {<br />+ .name = "pit",<br />+ .rating = 110,<br />+ .read = pit_read,<br />+ .mask = (cycle_t)-1,<br />+ .mult = 0,<br />+ .shift = 20,<br />+};<br />+<br />+static int __init init_pit_clocksource(void)<br />+{<br />+ if (num_possible_cpus() &gt; 4) /* PIT does not scale! */<br />+ return 0;<br />+<br />+ clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20);<br />+ register_clocksource(&amp;clocksource_pit);<br />+<br />+ return 0;<br />+}<br />+module_init(init_pit_clocksource);<br />diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c<br />index bd04f92..d9004dc 100644<br />--- a/arch/i386/kernel/tsc.c<br />+++ b/arch/i386/kernel/tsc.c<br />&#64;&#64; -4,13 +4,14 &#64;&#64;<br /> * See comments there for proper credits.<br /> */<br /> <br />+#include &lt;linux/clocksource.h&gt;<br /> #include &lt;linux/workqueue.h&gt;<br /> #include &lt;linux/cpufreq.h&gt;<br /> #include &lt;linux/jiffies.h&gt;<br /> #include &lt;linux/init.h&gt;<br /> <br />-#include &lt;asm/tsc.h&gt;<br /> #include &lt;asm/delay.h&gt;<br />+#include &lt;asm/tsc.h&gt;<br /> #include &lt;asm/io.h&gt;<br /> <br /> #include "mach_timer.h"<br />&#64;&#64; -302,3 +303,85 &#64;&#64; static int __init cpufreq_tsc(void)<br /> core_initcall(cpufreq_tsc);<br /> <br /> #endif<br />+<br />+/* clock source code */<br />+<br />+static unsigned long current_tsc_khz = 0;<br />+static int tsc_update_callback(void);<br />+<br />+static cycle_t read_tsc(void)<br />+{<br />+ cycle_t ret;<br />+<br />+ rdtscll(ret);<br />+<br />+ return ret;<br />+}<br />+<br />+static struct clocksource clocksource_tsc = {<br />+ .name = "tsc",<br />+ .rating = 300,<br />+ .read = read_tsc,<br />+ .mask = (cycle_t)-1,<br />+ .mult = 0, /* to be set */<br />+ .shift = 22,<br />+ .update_callback = tsc_update_callback,<br />+ .is_continuous = 1,<br />+};<br />+<br />+static int tsc_update_callback(void)<br />+{<br />+ int change = 0;<br />+<br />+ /* check to see if we should switch to the safe clocksource: */<br />+ if (clocksource_tsc.rating != 50 &amp;&amp; check_tsc_unstable()) {<br />+ clocksource_tsc.rating = 50;<br />+ reselect_clocksource();<br />+ change = 1;<br />+ }<br />+<br />+ /* only update if tsc_khz has changed: */<br />+ if (current_tsc_khz != tsc_khz) {<br />+ current_tsc_khz = tsc_khz;<br />+ clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,<br />+ clocksource_tsc.shift);<br />+ change = 1;<br />+ }<br />+<br />+ return change;<br />+}<br />+<br />+/*<br />+ * Make an educated guess if the TSC is trustworthy and synchronized<br />+ * over all CPUs.<br />+ */<br />+static __init int unsynchronized_tsc(void)<br />+{<br />+ /*<br />+ * Intel systems are normally all synchronized.<br />+ * Exceptions must mark TSC as unstable:<br />+ */<br />+ if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)<br />+ return 0;<br />+<br />+ /* assume multi socket systems are not synchronized: */<br />+ return num_possible_cpus() &gt; 1;<br />+}<br />+<br />+/* NUMAQ can't use TSC: */<br />+static int __init init_tsc_clocksource(void)<br />+{<br />+ /* TSC initialization is done in arch/i386/kernel/tsc.c */<br />+ if (cpu_has_tsc &amp;&amp; tsc_khz &amp;&amp; !tsc_disable) {<br />+ if (unsynchronized_tsc()) /* lower rating if unsynced */<br />+ mark_tsc_unstable();<br />+ current_tsc_khz = tsc_khz;<br />+ clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,<br />+ clocksource_tsc.shift);<br />+ register_clocksource(&amp;clocksource_tsc);<br />+ }<br />+<br />+ return 0;<br />+}<br />+<br />+module_init(init_tsc_clocksource);<br />diff --git a/drivers/Makefile b/drivers/Makefile<br />index ea410b6..b250984 100644<br />--- a/drivers/Makefile<br />+++ b/drivers/Makefile<br />&#64;&#64; -70,3 +70,4 &#64;&#64; obj-$(CONFIG_SGI_IOC4) += sn/<br /> obj-y += firmware/<br /> obj-$(CONFIG_CRYPTO) += crypto/<br /> obj-$(CONFIG_SUPERH) += sh/<br />+obj-$(CONFIG_GENERIC_TIME) += clocksource/<br />diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile<br />new file mode 100644<br />index 0000000..6e98a5e<br />--- /dev/null<br />+++ b/drivers/clocksource/Makefile<br />&#64;&#64; -0,0 +1,2 &#64;&#64;<br />+obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o<br />+obj-$(CONFIG_ACPI) += acpi_pm.o<br />diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c<br />new file mode 100644<br />index 0000000..e5bc091<br />--- /dev/null<br />+++ b/drivers/clocksource/acpi_pm.c<br />&#64;&#64; -0,0 +1,123 &#64;&#64;<br />+/*<br />+ * linux/drivers/clocksource/acpi_pm.c<br />+ *<br />+ * This file contains the ACPI PM based clocksource.<br />+ *<br />+ * This code was largely moved from the i386 timer_pm.c file<br />+ * which was (C) Dominik Brodowski &lt;linux&#64;brodo.de&gt; 2003<br />+ * and contained the following comments:<br />+ *<br />+ * Driver to use the Power Management Timer (PMTMR) available in some<br />+ * southbridges as primary timing source for the Linux kernel.<br />+ *<br />+ * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,<br />+ * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.<br />+ *<br />+ * This file is licensed under the GPL v2.<br />+ */<br />+<br />+#include &lt;linux/clocksource.h&gt;<br />+#include &lt;linux/errno.h&gt;<br />+#include &lt;linux/init.h&gt;<br />+#include &lt;asm/io.h&gt;<br />+<br />+/* Number of PMTMR ticks expected during calibration run */<br />+#define PMTMR_TICKS_PER_SEC 3579545<br />+<br />+#if (defined(CONFIG_X86) &amp;&amp; (!defined(CONFIG_X86_64)))<br />+# include "mach_timer.h"<br />+# define PMTMR_EXPECTED_RATE ((PMTMR_TICKS_PER_SEC*CALIBRATE_TIME_MSEC)/1000)<br />+#endif<br />+<br />+/*<br />+ * The I/O port the PMTMR resides at.<br />+ * The location is detected during setup_arch(),<br />+ * in arch/i386/acpi/boot.c<br />+ */<br />+extern u32 acpi_pmtmr_ioport;<br />+extern int acpi_pmtmr_buggy;<br />+<br />+#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */<br />+<br />+static inline u32 read_pmtmr(void)<br />+{<br />+ /* mask the output to 24 bits */<br />+ return inl(acpi_pmtmr_ioport) &amp; ACPI_PM_MASK;<br />+}<br />+<br />+static cycle_t acpi_pm_read_verified(void)<br />+{<br />+ u32 v1 = 0, v2 = 0, v3 = 0;<br />+<br />+ /*<br />+ * It has been reported that because of various broken<br />+ * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock<br />+ * source is not latched, so you must read it multiple<br />+ * times to ensure a safe value is read:<br />+ */<br />+ do {<br />+ v1 = read_pmtmr();<br />+ v2 = read_pmtmr();<br />+ v3 = read_pmtmr();<br />+ } while ((v1 &gt; v2 &amp;&amp; v1 &lt; v3) || (v2 &gt; v3 &amp;&amp; v2 &lt; v1)<br />+ || (v3 &gt; v1 &amp;&amp; v3 &lt; v2));<br />+<br />+ return (cycle_t)v2;<br />+}<br />+<br />+static cycle_t acpi_pm_read(void)<br />+{<br />+ return (cycle_t)read_pmtmr();<br />+}<br />+<br />+struct clocksource clocksource_acpi_pm = {<br />+ .name = "acpi_pm",<br />+ .rating = 200,<br />+ .read = acpi_pm_read,<br />+ .mask = (cycle_t)ACPI_PM_MASK,<br />+ .mult = 0, /*to be caluclated*/<br />+ .shift = 22,<br />+ .is_continuous = 1,<br />+};<br />+<br />+static int __init init_acpi_pm_clocksource(void)<br />+{<br />+ u32 value1, value2;<br />+ unsigned int i;<br />+<br />+ if (!acpi_pmtmr_ioport)<br />+ return -ENODEV;<br />+<br />+ clocksource_acpi_pm.mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC,<br />+ clocksource_acpi_pm.shift);<br />+<br />+ /* "verify" this timing source: */<br />+ value1 = read_pmtmr();<br />+ for (i = 0; i &lt; 10000; i++) {<br />+ value2 = read_pmtmr();<br />+ if (value2 == value1)<br />+ continue;<br />+ if (value2 &gt; value1)<br />+ goto pm_good;<br />+ if ((value2 &lt; value1) &amp;&amp; ((value2) &lt; 0xFFF))<br />+ goto pm_good;<br />+ printk(KERN_INFO "PM-Timer had inconsistent results: 0x%#x, 0x%#x - aborting.\n", value1, value2);<br />+ return -EINVAL;<br />+ }<br />+ printk(KERN_INFO "PM-Timer had no reasonable result: 0x%#x - aborting.\n", value1);<br />+ return -ENODEV;<br />+<br />+pm_good:<br />+<br />+ /* check to see if pmtmr is known buggy: */<br />+ if (acpi_pmtmr_buggy) {<br />+ clocksource_acpi_pm.read = acpi_pm_read_verified;<br />+ clocksource_acpi_pm.rating = 110;<br />+ }<br />+<br />+ register_clocksource(&amp;clocksource_acpi_pm);<br />+<br />+ return 0;<br />+}<br />+<br />+module_init(init_acpi_pm_clocksource);<br />diff --git a/drivers/clocksource/cyclone.c b/drivers/clocksource/cyclone.c<br />new file mode 100644<br />index 0000000..168e78b<br />--- /dev/null<br />+++ b/drivers/clocksource/cyclone.c<br />&#64;&#64; -0,0 +1,121 &#64;&#64;<br />+#include &lt;linux/clocksource.h&gt;<br />+#include &lt;linux/string.h&gt;<br />+#include &lt;linux/errno.h&gt;<br />+#include &lt;linux/timex.h&gt;<br />+#include &lt;linux/init.h&gt;<br />+<br />+#include &lt;asm/pgtable.h&gt;<br />+#include &lt;asm/io.h&gt;<br />+<br />+#include "mach_timer.h"<br />+<br />+#define CYCLONE_CBAR_ADDR 0xFEB00CD0 /* base address ptr */<br />+#define CYCLONE_PMCC_OFFSET 0x51A0 /* offset to control register */<br />+#define CYCLONE_MPCS_OFFSET 0x51A8 /* offset to select register */<br />+#define CYCLONE_MPMC_OFFSET 0x51D0 /* offset to count register */<br />+#define CYCLONE_TIMER_FREQ 99780000 /* 100Mhz, but not really */<br />+#define CYCLONE_TIMER_MASK 0xFFFFFFFF /* 32 bit mask */<br />+<br />+int use_cyclone = 0;<br />+static void __iomem *cyclone_ptr;<br />+<br />+static cycle_t read_cyclone(void)<br />+{<br />+ return (cycle_t)readl(cyclone_ptr);<br />+}<br />+<br />+struct clocksource clocksource_cyclone = {<br />+ .name = "cyclone",<br />+ .rating = 250,<br />+ .read = read_cyclone,<br />+ .mask = (cycle_t)CYCLONE_TIMER_MASK,<br />+ .mult = 10,<br />+ .shift = 0,<br />+ .is_continuous = 1,<br />+};<br />+<br />+static int __init init_cyclone_clocksource(void)<br />+{<br />+ unsigned long base; /* saved value from CBAR */<br />+ unsigned long offset;<br />+ u32 __iomem* volatile cyclone_timer; /* Cyclone MPMC0 register */<br />+ u32 __iomem* reg;<br />+ int i;<br />+<br />+ /* make sure we're on a summit box: */<br />+ if (!use_cyclone)<br />+ return -ENODEV;<br />+<br />+ printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");<br />+<br />+ /* find base address: */<br />+ offset = CYCLONE_CBAR_ADDR;<br />+ reg = ioremap_nocache(offset, sizeof(reg));<br />+ if (!reg) {<br />+ printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");<br />+ return -ENODEV;<br />+ }<br />+ /* even on 64bit systems, this is only 32bits: */<br />+ base = readl(reg);<br />+ if (!base) {<br />+ printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");<br />+ return -ENODEV;<br />+ }<br />+ iounmap(reg);<br />+<br />+ /* setup PMCC: */<br />+ offset = base + CYCLONE_PMCC_OFFSET;<br />+ reg = ioremap_nocache(offset, sizeof(reg));<br />+ if (!reg) {<br />+ printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");<br />+ return -ENODEV;<br />+ }<br />+ writel(0x00000001,reg);<br />+ iounmap(reg);<br />+<br />+ /* setup MPCS: */<br />+ offset = base + CYCLONE_MPCS_OFFSET;<br />+ reg = ioremap_nocache(offset, sizeof(reg));<br />+ if (!reg) {<br />+ printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");<br />+ return -ENODEV;<br />+ }<br />+ writel(0x00000001,reg);<br />+ iounmap(reg);<br />+<br />+ /* map in cyclone_timer: */<br />+ offset = base + CYCLONE_MPMC_OFFSET;<br />+ cyclone_timer = ioremap_nocache(offset, sizeof(u64));<br />+ if (!cyclone_timer) {<br />+ printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");<br />+ return -ENODEV;<br />+ }<br />+<br />+ /* quick test to make sure its ticking: */<br />+ for (i = 0; i &lt; 3; i++){<br />+ u32 old = readl(cyclone_timer);<br />+ int stall = 100;<br />+<br />+ while (stall--)<br />+ barrier();<br />+<br />+ if (readl(cyclone_timer) == old) {<br />+ printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");<br />+ iounmap(cyclone_timer);<br />+ cyclone_timer = NULL;<br />+ return -ENODEV;<br />+ }<br />+ }<br />+ cyclone_ptr = cyclone_timer;<br />+<br />+ /* sort out mult/shift values: */<br />+ clocksource_cyclone.shift = 22;<br />+ clocksource_cyclone.mult = clocksource_hz2mult(CYCLONE_TIMER_FREQ,<br />+ clocksource_cyclone.shift);<br />+<br />+ register_clocksource(&amp;clocksource_cyclone);<br />+<br />+ return 0;<br />+}<br />+<br />+module_init(init_cyclone_clocksource);<br />-<br />To unsubscribe from this list: send the line "unsubscribe linux-kernel" in<br />the body of a message to majordomo&#64;vger.kernel.org<br />More majordomo info at <a href="http://vger.kernel.org/majordomo-info.html">http://vger.kernel.org/majordomo-info.html</a><br />Please read the FAQ at <a href="http://www.tux.org/lkml/">http://www.tux.org/lkml/</a><br /></pre></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerr.gif" width="32" height="32" alt="\" /></td></tr><tr><td align="right" valign="bottom"> 聽 </td></tr><tr><td align="right" valign="bottom">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerl.gif" width="32" height="32" alt="\" /></td><td class="c">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerr.gif" width="32" height="32" alt="/" /></td></tr><tr><td align="right" valign="top" colspan="2"> 聽 </td><td class="lm">Last update: 2005-12-16 02:15 聽聽 [from the cache]<br />漏2003-2020 <a href="http://blog.jasper.es/"><span itemprop="editor">Jasper Spaans</span></a>|hosted at <a href="https://www.digitalocean.com/?refcode=9a8e99d24cf9">Digital Ocean</a> and my Meterkast|<a href="http://blog.jasper.es/categories.html#lkml-ref">Read the blog</a></td><td>聽</td></tr></table><script language="javascript" src="/js/styleswitcher.js" type="text/javascript"></script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10