0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/clocksource.h>
0016 #include <linux/init.h>
0017 #include <linux/module.h>
0018 #include <linux/ioport.h>
0019 #include <linux/scx200.h>
0020
0021 #define NAME "scx200_hrt"
0022
0023 static int mhz27;
0024 module_param(mhz27, int, 0);
0025 MODULE_PARM_DESC(mhz27, "count at 27.0 MHz (default is 1.0 MHz)");
0026
0027 static int ppm;
0028 module_param(ppm, int, 0);
0029 MODULE_PARM_DESC(ppm, "+-adjust to actual XO freq (ppm)");
0030
0031
0032 #define SCx200_TMCNFG_OFFSET (SCx200_TIMER_OFFSET + 5)
0033
0034
0035 #define HR_TMEN (1 << 0)
0036 #define HR_TMCLKSEL (1 << 1)
0037 #define HR_TM27MPD (1 << 2)
0038
0039
0040 #define HRT_FREQ 1000000
0041
0042 static u64 read_hrt(struct clocksource *cs)
0043 {
0044
0045 return (u64) inl(scx200_cb_base + SCx200_TIMER_OFFSET);
0046 }
0047
0048 static struct clocksource cs_hrt = {
0049 .name = "scx200_hrt",
0050 .rating = 250,
0051 .read = read_hrt,
0052 .mask = CLOCKSOURCE_MASK(32),
0053 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
0054
0055 };
0056
0057 static int __init init_hrt_clocksource(void)
0058 {
0059 u32 freq;
0060
0061 if (!scx200_cb_present())
0062 return -ENODEV;
0063
0064
0065 if (!request_region(scx200_cb_base + SCx200_TIMER_OFFSET,
0066 SCx200_TIMER_SIZE,
0067 "NatSemi SCx200 High-Resolution Timer")) {
0068 pr_warn("unable to lock timer region\n");
0069 return -ENODEV;
0070 }
0071
0072
0073 outb(HR_TMEN | (mhz27 ? HR_TMCLKSEL : 0),
0074 scx200_cb_base + SCx200_TMCNFG_OFFSET);
0075
0076 freq = (HRT_FREQ + ppm);
0077 if (mhz27)
0078 freq *= 27;
0079
0080 pr_info("enabling scx200 high-res timer (%s MHz +%d ppm)\n", mhz27 ? "27":"1", ppm);
0081
0082 return clocksource_register_hz(&cs_hrt, freq);
0083 }
0084
0085 module_init(init_hrt_clocksource);
0086
0087 MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
0088 MODULE_DESCRIPTION("clocksource on SCx200 HiRes Timer");
0089 MODULE_LICENSE("GPL");