Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2006 Jim Cromie
0004  *
0005  * This is a clocksource driver for the Geode SCx200's 1 or 27 MHz
0006  * high-resolution timer.  The Geode SC-1100 (at least) has a buggy
0007  * time stamp counter (TSC), which loses time unless 'idle=poll' is
0008  * given as a boot-arg. In its absence, the Generic Timekeeping code
0009  * will detect and de-rate the bad TSC, allowing this timer to take
0010  * over timekeeping duties.
0011  *
0012  * Based on work by John Stultz, and Ted Phelps (in a 2.6.12-rc6 patch)
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);    /* load time only */
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);  /* load time only */
0029 MODULE_PARM_DESC(ppm, "+-adjust to actual XO freq (ppm)");
0030 
0031 /* HiRes Timer configuration register address */
0032 #define SCx200_TMCNFG_OFFSET (SCx200_TIMER_OFFSET + 5)
0033 
0034 /* and config settings */
0035 #define HR_TMEN (1 << 0)    /* timer interrupt enable */
0036 #define HR_TMCLKSEL (1 << 1)    /* 1|0 counts at 27|1 MHz */
0037 #define HR_TM27MPD (1 << 2) /* 1 turns off input clock (power-down) */
0038 
0039 /* The base timer frequency, * 27 if selected */
0040 #define HRT_FREQ   1000000
0041 
0042 static u64 read_hrt(struct clocksource *cs)
0043 {
0044     /* Read the timer value */
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     /* mult, shift are set based on mhz27 flag */
0055 };
0056 
0057 static int __init init_hrt_clocksource(void)
0058 {
0059     u32 freq;
0060     /* Make sure scx200 has initialized the configuration block */
0061     if (!scx200_cb_present())
0062         return -ENODEV;
0063 
0064     /* Reserve the timer's ISA io-region for ourselves */
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     /* write timer config */
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");