summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2003-01-15 20:15:33 +0000
committerphk <phk@FreeBSD.org>2003-01-15 20:15:33 +0000
commit8f6000a750e0051af7354333f8f1edc6e46a9405 (patch)
tree599afa8b7627da158de097a297ac0b9ded4f6a9b /sys
parenta7f59810e1b32b1a343c945ea8c1ddd5d10d098f (diff)
downloadFreeBSD-src-8f6000a750e0051af7354333f8f1edc6e46a9405.zip
FreeBSD-src-8f6000a750e0051af7354333f8f1edc6e46a9405.tar.gz
Add machdep.elan_freq sysctl which can be used to set the CPU clock
frequency in Hz. The default is still 33.333 MHz. Please notice that the number is round to a multiple of four internally so it may not read back exactly the same as written. Add compile time ELAN_XTAL option to override the 33.333 MHz default. Add compile time ELAN_PPS option to enable code for high precision (250 nanoseconds) timestamping of external signals.
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/options.i3864
-rw-r--r--sys/i386/conf/NOTES4
-rw-r--r--sys/i386/i386/elan-mmcr.c91
3 files changed, 96 insertions, 3 deletions
diff --git a/sys/conf/options.i386 b/sys/conf/options.i386
index ad5e46b..81fd7db 100644
--- a/sys/conf/options.i386
+++ b/sys/conf/options.i386
@@ -64,6 +64,10 @@ CPU_ATHLON_SSE_HACK opt_cpu.h
CPU_DISABLE_SSE opt_cpu.h
CPU_DISABLE_CMPXCHG opt_global.h
+# Options for the AMD Elan CPU
+ELAN_PPS opt_cpu.h
+ELAN_XTAL opt_cpu.h
+
# The CPU type affects the endian conversion functions all over the kernel.
I386_CPU opt_global.h
I486_CPU opt_global.h
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 07a5e25..9f63519 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -90,6 +90,8 @@ cpu I686_CPU # aka Pentium Pro(tm)
# I/O device(s).
#
# CPU_ELAN enables support for AMDs ElanSC520 CPU.
+# ELAN_XTAL sets the clock crystal frequency in Hz
+# ELAN_PPS enables precision timestamp code.
#
# CPU_ENABLE_SSE enables SSE/MMX2 instructions support. This is default
# on I686_CPU and above.
@@ -165,6 +167,8 @@ options CPU_BTB_EN
options CPU_DIRECT_MAPPED_CACHE
options CPU_DISABLE_5X86_LSSER
options CPU_ELAN
+options ELAN_XTAL=32768000
+options ELAN_PPS
options CPU_ENABLE_SSE
#options CPU_DISABLE_SSE
options CPU_FASTER_5X86_FPU
diff --git a/sys/i386/i386/elan-mmcr.c b/sys/i386/i386/elan-mmcr.c
index 3c60bdb..7ddd90c 100644
--- a/sys/i386/i386/elan-mmcr.c
+++ b/sys/i386/i386/elan-mmcr.c
@@ -17,6 +17,14 @@
*
* So instead we recognize the on-chip host-PCI bridge and call back from
* sys/i386/pci/pci_bus.c to here if we find it.
+ *
+ * #ifdef ELAN_PPS
+ * The Elan has three general purpose counters, which when used just right
+ * can hardware timestamp external events with approx 250 nanoseconds
+ * resolution _and_ precision. Connect the signal to TMR1IN and PIO7.
+ * (You can use any PIO pin, look for PIO7 to change this). Use the
+ * PPS-API on the /dev/elan-mmcr device.
+ * #endif ELAN_PPS
*/
#include <sys/param.h>
@@ -30,6 +38,8 @@
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/timepps.h>
#include <machine/md_var.h>
@@ -44,20 +54,68 @@ static char *errled;
static struct callout_handle errled_h = CALLOUT_HANDLE_INITIALIZER(&errled_h);
static void timeout_errled(void *);
+#ifdef ELAN_PPS
+/* Relating to the PPS-api */
+static struct pps_state elan_pps;
+
+static void
+elan_poll_pps(struct timecounter *tc)
+{
+ static int state;
+ int i;
+
+ /* XXX: This is PIO7, change to your preference */
+ i = elan_mmcr[0xc30 / 2] & 0x80;
+ if (i == state)
+ return;
+ state = i;
+ if (!state)
+ return;
+ pps_capture(&elan_pps);
+ elan_pps.capcount =
+ (elan_mmcr[0xc84 / 2] - elan_mmcr[0xc7c / 2]) & 0xffff;
+ pps_event(&elan_pps, PPS_CAPTUREASSERT);
+}
+#endif /* ELAN_PPS */
+
static unsigned
elan_get_timecount(struct timecounter *tc)
{
return (elan_mmcr[0xc84 / 2]);
}
+/*
+ * The Elan CPU can be run from a number of clock frequencies, this
+ * allows you to override the default 33.3 MHZ.
+ */
+#ifndef ELAN_XTAL
+#define ELAN_XTAL 33333333
+#endif
+
static struct timecounter elan_timecounter = {
elan_get_timecount,
- 0,
+ NULL,
0xffff,
- 33333333 / 4,
+ ELAN_XTAL / 4,
"ELAN"
};
+static int
+sysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS)
+{
+ u_int f;
+ int error;
+
+ f = elan_timecounter.tc_frequency * 4;
+ error = sysctl_handle_int(oidp, &f, sizeof(f), req);
+ if (error == 0 && req->newptr != NULL)
+ elan_timecounter.tc_frequency = (f + 3) / 4;
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW,
+ 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", "");
+
void
init_AMD_Elan_sc520(void)
{
@@ -85,6 +143,15 @@ init_AMD_Elan_sc520(void)
/* Start GP timer #2 and use it as timecounter, hz permitting */
elan_mmcr[0xc82 / 2] = 0xc001;
+
+#ifdef ELAN_PPS
+ /* Set up GP timer #1 as pps counter */
+ elan_mmcr[0xc24 / 2] &= ~0x10;
+ elan_mmcr[0xc7a / 2] = 0x8000 | 0x4000 | 0x10 | 0x1;
+ elan_pps.ppscap |= PPS_CAPTUREASSERT;
+ pps_init(&elan_pps);
+#endif
+
tc_init(&elan_timecounter);
}
@@ -254,6 +321,24 @@ elan_mmap(dev_t dev, vm_offset_t offset, int nprot)
static int
elan_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr)
{
- return(ENOENT);
+ int error;
+
+ error = ENOTTY;
+#ifdef ELAN_PPS
+ error = pps_ioctl(cmd, arg, &elan_pps);
+ /*
+ * We only want to incur the overhead of the PPS polling if we
+ * are actually asked to timestamp.
+ */
+ if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT)
+ elan_timecounter.tc_poll_pps = elan_poll_pps;
+ else
+ elan_timecounter.tc_poll_pps = NULL;
+ if (error != ENOTTY)
+ return (error);
+#endif /* ELAN_PPS */
+
+ /* Other future ioctl handling here */
+ return(error);
}
OpenPOWER on IntegriCloud