~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/i386/kernel/time_hpet.c

Version: ~ [ linux-5.2-rc1 ] ~ [ linux-5.1.2 ] ~ [ linux-5.0.16 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.43 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.119 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.176 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.179 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.139 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.67 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/arch/i386/kernel/time_hpet.c
  3  *  This code largely copied from arch/x86_64/kernel/time.c
  4  *  See that file for credits.
  5  *
  6  *  2003-06-30    Venkatesh Pallipadi - Additional changes for HPET support
  7  */
  8 
  9 #include <linux/errno.h>
 10 #include <linux/kernel.h>
 11 #include <linux/param.h>
 12 #include <linux/string.h>
 13 #include <linux/init.h>
 14 #include <linux/smp.h>
 15 
 16 #include <asm/timer.h>
 17 #include <asm/fixmap.h>
 18 #include <asm/apic.h>
 19 
 20 #include <linux/timex.h>
 21 #include <linux/config.h>
 22 
 23 #include <asm/hpet.h>
 24 
 25 unsigned long hpet_period;      /* fsecs / HPET clock */
 26 unsigned long hpet_tick;        /* hpet clks count per tick */
 27 unsigned long hpet_address;     /* hpet memory map physical address */
 28 
 29 static int use_hpet;            /* can be used for runtime check of hpet */
 30 static int boot_hpet_disable;   /* boottime override for HPET timer */
 31 static unsigned long hpet_virt_address; /* hpet kernel virtual address */
 32 
 33 #define FSEC_TO_USEC (1000000000UL)
 34 
 35 int hpet_readl(unsigned long a)
 36 {
 37         return readl(hpet_virt_address + a);
 38 }
 39 
 40 void hpet_writel(unsigned long d, unsigned long a)
 41 {
 42         writel(d, hpet_virt_address + a);
 43 }
 44 
 45 #ifdef CONFIG_X86_LOCAL_APIC
 46 /*
 47  * HPET counters dont wrap around on every tick. They just change the
 48  * comparator value and continue. Next tick can be caught by checking
 49  * for a change in the comparator value. Used in apic.c.
 50  */
 51 void __init wait_hpet_tick(void)
 52 {
 53         unsigned int start_cmp_val, end_cmp_val;
 54 
 55         start_cmp_val = hpet_readl(HPET_T0_CMP);
 56         do {
 57                 end_cmp_val = hpet_readl(HPET_T0_CMP);
 58         } while (start_cmp_val == end_cmp_val);
 59 }
 60 #endif
 61 
 62 /*
 63  * Check whether HPET was found by ACPI boot parse. If yes setup HPET
 64  * counter 0 for kernel base timer.
 65  */
 66 int __init hpet_enable(void)
 67 {
 68         unsigned int cfg, id;
 69         unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */
 70         unsigned long hpet_tick_rem;
 71 
 72         if (boot_hpet_disable)
 73                 return -1;
 74 
 75         if (!hpet_address) {
 76                 return -1;
 77         }
 78         hpet_virt_address = (unsigned long) ioremap_nocache(hpet_address,
 79                                                             HPET_MMAP_SIZE);
 80         /*
 81          * Read the period, compute tick and quotient.
 82          */
 83         id = hpet_readl(HPET_ID);
 84 
 85         /*
 86          * We are checking for value '1' or more in number field.
 87          * So, we are OK with HPET_EMULATE_RTC part too, where we need
 88          * to have atleast 2 timers.
 89          */
 90         if (!(id & HPET_ID_NUMBER) ||
 91             !(id & HPET_ID_LEGSUP))
 92                 return -1;
 93 
 94         if (((id & HPET_ID_VENDOR) >> HPET_ID_VENDOR_SHIFT) !=
 95                                 HPET_ID_VENDOR_8086)
 96                 return -1;
 97 
 98         hpet_period = hpet_readl(HPET_PERIOD);
 99         if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
100                 return -1;
101 
102         /*
103          * 64 bit math
104          * First changing tick into fsec
105          * Then 64 bit div to find number of hpet clk per tick
106          */
107         ASM_MUL64_REG(tick_fsec_low, tick_fsec_high,
108                         KERNEL_TICK_USEC, FSEC_TO_USEC);
109         ASM_DIV64_REG(hpet_tick, hpet_tick_rem,
110                         hpet_period, tick_fsec_low, tick_fsec_high);
111 
112         if (hpet_tick_rem > (hpet_period >> 1))
113                 hpet_tick++; /* rounding the result */
114 
115         /*
116          * Stop the timers and reset the main counter.
117          */
118         cfg = hpet_readl(HPET_CFG);
119         cfg &= ~HPET_CFG_ENABLE;
120         hpet_writel(cfg, HPET_CFG);
121         hpet_writel(0, HPET_COUNTER);
122         hpet_writel(0, HPET_COUNTER + 4);
123 
124         /*
125          * Set up timer 0, as periodic with first interrupt to happen at
126          * hpet_tick, and period also hpet_tick.
127          */
128         cfg = hpet_readl(HPET_T0_CFG);
129         cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
130                HPET_TN_SETVAL | HPET_TN_32BIT;
131         hpet_writel(cfg, HPET_T0_CFG);
132         hpet_writel(hpet_tick, HPET_T0_CMP);
133 
134         /*
135          * Go!
136          */
137         cfg = hpet_readl(HPET_CFG);
138         cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
139         hpet_writel(cfg, HPET_CFG);
140 
141         use_hpet = 1;
142 #ifdef CONFIG_X86_LOCAL_APIC
143         wait_timer_tick = wait_hpet_tick;
144 #endif
145         return 0;
146 }
147 
148 int is_hpet_enabled(void)
149 {
150         return use_hpet;
151 }
152 
153 int is_hpet_capable(void)
154 {
155         if (!boot_hpet_disable && hpet_address)
156                 return 1;
157         return 0;
158 }
159 
160 static int __init hpet_setup(char* str)
161 {
162         if (str) {
163                 if (!strncmp("disable", str, 7))
164                         boot_hpet_disable = 1;
165         }
166         return 1;
167 }
168 
169 __setup("hpet=", hpet_setup);
170 
171 #ifdef CONFIG_HPET_EMULATE_RTC
172 /* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
173  * is enabled, we support RTC interrupt functionality in software.
174  * RTC has 3 kinds of interrupts:
175  * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock
176  *    is updated
177  * 2) Alarm Interrupt - generate an interrupt at a specific time of day
178  * 3) Periodic Interrupt - generate periodic interrupt, with frequencies
179  *    2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2)
180  * (1) and (2) above are implemented using polling at a frequency of
181  * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt
182  * overhead. (DEFAULT_RTC_INT_FREQ)
183  * For (3), we use interrupts at 64Hz or user specified periodic
184  * frequency, whichever is higher.
185  */
186 #include <linux/mc146818rtc.h>
187 #include <linux/rtc.h>
188 
189 extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
190 
191 #define DEFAULT_RTC_INT_FREQ    64
192 #define RTC_NUM_INTS            1
193 
194 static unsigned long UIE_on;
195 static unsigned long prev_update_sec;
196 
197 static unsigned long AIE_on;
198 static struct rtc_time alarm_time;
199 
200 static unsigned long PIE_on;
201 static unsigned long PIE_freq = DEFAULT_RTC_INT_FREQ;
202 static unsigned long PIE_count;
203 
204 static unsigned long hpet_rtc_int_freq; /* RTC interrupt frequency */
205 
206 /*
207  * Timer 1 for RTC, we do not use periodic interrupt feature,
208  * even if HPET supports periodic interrupts on Timer 1.
209  * The reason being, to set up a periodic interrupt in HPET, we need to
210  * stop the main counter. And if we do that everytime someone diables/enables
211  * RTC, we will have adverse effect on main kernel timer running on Timer 0.
212  * So, for the time being, simulate the periodic interrupt in software.
213  *
214  * hpet_rtc_timer_init() is called for the first time and during subsequent
215  * interuppts reinit happens through hpet_rtc_timer_reinit().
216  */
217 int hpet_rtc_timer_init(void)
218 {
219         unsigned int cfg, cnt;
220         unsigned long flags;
221 
222         if (!is_hpet_enabled())
223                 return 0;
224         /*
225          * Set the counter 1 and enable the interrupts.
226          */
227         if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
228                 hpet_rtc_int_freq = PIE_freq;
229         else
230                 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
231 
232         local_irq_save(flags);
233         cnt = hpet_readl(HPET_COUNTER);
234         cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
235         hpet_writel(cnt, HPET_T1_CMP);
236         local_irq_restore(flags);
237 
238         cfg = hpet_readl(HPET_T1_CFG);
239         cfg |= HPET_TN_ENABLE | HPET_TN_SETVAL | HPET_TN_32BIT;
240         hpet_writel(cfg, HPET_T1_CFG);
241 
242         return 1;
243 }
244 
245 static void hpet_rtc_timer_reinit(void)
246 {
247         unsigned int cfg, cnt;
248 
249         if (!(PIE_on | AIE_on | UIE_on))
250                 return;
251 
252         if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
253                 hpet_rtc_int_freq = PIE_freq;
254         else
255                 hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
256 
257         /* It is more accurate to use the comparator value than current count.*/
258         cnt = hpet_readl(HPET_T1_CMP);
259         cnt += hpet_tick*HZ/hpet_rtc_int_freq;
260         hpet_writel(cnt, HPET_T1_CMP);
261 
262         cfg = hpet_readl(HPET_T1_CFG);
263         cfg |= HPET_TN_ENABLE | HPET_TN_SETVAL | HPET_TN_32BIT;
264         hpet_writel(cfg, HPET_T1_CFG);
265 
266         return;
267 }
268 
269 /*
270  * The functions below are called from rtc driver.
271  * Return 0 if HPET is not being used.
272  * Otherwise do the necessary changes and return 1.
273  */
274 int hpet_mask_rtc_irq_bit(unsigned long bit_mask)
275 {
276         if (!is_hpet_enabled())
277                 return 0;
278 
279         if (bit_mask & RTC_UIE)
280                 UIE_on = 0;
281         if (bit_mask & RTC_PIE)
282                 PIE_on = 0;
283         if (bit_mask & RTC_AIE)
284                 AIE_on = 0;
285 
286         return 1;
287 }
288 
289 int hpet_set_rtc_irq_bit(unsigned long bit_mask)
290 {
291         int timer_init_reqd = 0;
292 
293         if (!is_hpet_enabled())
294                 return 0;
295 
296         if (!(PIE_on | AIE_on | UIE_on))
297                 timer_init_reqd = 1;
298 
299         if (bit_mask & RTC_UIE) {
300                 UIE_on = 1;
301         }
302         if (bit_mask & RTC_PIE) {
303                 PIE_on = 1;
304                 PIE_count = 0;
305         }
306         if (bit_mask & RTC_AIE) {
307                 AIE_on = 1;
308         }
309 
310         if (timer_init_reqd)
311                 hpet_rtc_timer_init();
312 
313         return 1;
314 }
315 
316 int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
317 {
318         if (!is_hpet_enabled())
319                 return 0;
320 
321         alarm_time.tm_hour = hrs;
322         alarm_time.tm_min = min;
323         alarm_time.tm_sec = sec;
324 
325         return 1;
326 }
327 
328 int hpet_set_periodic_freq(unsigned long freq)
329 {
330         if (!is_hpet_enabled())
331                 return 0;
332 
333         PIE_freq = freq;
334         PIE_count = 0;
335 
336         return 1;
337 }
338 
339 int hpet_rtc_dropped_irq(void)
340 {
341         if (!is_hpet_enabled())
342                 return 0;
343 
344         return 1;
345 }
346 
347 irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
348 {
349         struct rtc_time curr_time;
350         unsigned long rtc_int_flag = 0;
351         int call_rtc_interrupt = 0;
352 
353         hpet_rtc_timer_reinit();
354 
355         if (UIE_on | AIE_on) {
356                 rtc_get_rtc_time(&curr_time);
357         }
358         if (UIE_on) {
359                 if (curr_time.tm_sec != prev_update_sec) {
360                         /* Set update int info, call real rtc int routine */
361                         call_rtc_interrupt = 1;
362                         rtc_int_flag = RTC_UF;
363                         prev_update_sec = curr_time.tm_sec;
364                 }
365         }
366         if (PIE_on) {
367                 PIE_count++;
368                 if (PIE_count >= hpet_rtc_int_freq/PIE_freq) {
369                         /* Set periodic int info, call real rtc int routine */
370                         call_rtc_interrupt = 1;
371                         rtc_int_flag |= RTC_PF;
372                         PIE_count = 0;
373                 }
374         }
375         if (AIE_on) {
376                 if ((curr_time.tm_sec == alarm_time.tm_sec) &&
377                     (curr_time.tm_min == alarm_time.tm_min) &&
378                     (curr_time.tm_hour == alarm_time.tm_hour)) {
379                         /* Set alarm int info, call real rtc int routine */
380                         call_rtc_interrupt = 1;
381                         rtc_int_flag |= RTC_AF;
382                 }
383         }
384         if (call_rtc_interrupt) {
385                 rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
386                 rtc_interrupt(rtc_int_flag, dev_id, regs);
387         }
388         return IRQ_HANDLED;
389 }
390 #endif
391 
392 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp