Merge from vendor branch SENDMAIL:
[dragonfly.git] / sys / platform / vkernel / platform / systimer.c
1 /*
2  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/platform/vkernel/platform/systimer.c,v 1.15 2007/07/02 15:18:51 dillon Exp $
35  */
36
37 #include <sys/types.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/systimer.h>
41 #include <sys/sysctl.h>
42 #include <sys/signal.h>
43 #include <sys/interrupt.h>
44 #include <sys/bus.h>
45 #include <sys/time.h>
46 #include <machine/cpu.h>
47 #include <machine/globaldata.h>
48 #include <machine/md_var.h>
49
50 #include <sys/thread2.h>
51
52 #include <unistd.h>
53 #include <signal.h>
54
55 static void cputimer_intr(void *dummy, struct intrframe *frame);
56
57 int disable_rtc_set;
58 SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
59            CTLFLAG_RW, &disable_rtc_set, 0, "");
60
61 int adjkerntz;
62 int wall_cmos_clock = 0;
63 static struct kqueue_info *kqueue_timer_info;
64
65 static int cputimer_mib[16];
66 static int cputimer_miblen;
67
68
69 /*
70  * SYSTIMER IMPLEMENTATION
71  */
72 static sysclock_t vkernel_timer_get_timecount(void);
73 static void vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock);
74
75 static struct cputimer vkernel_cputimer = {
76         SLIST_ENTRY_INITIALIZER,
77         "VKERNEL",
78         CPUTIMER_PRI_VKERNEL,
79         CPUTIMER_VKERNEL,
80         vkernel_timer_get_timecount,
81         cputimer_default_fromhz,
82         cputimer_default_fromus,
83         vkernel_timer_construct,
84         cputimer_default_destruct,
85         1000000,                        /* 1us granularity */
86         0, 0, 0
87 };
88
89 /*
90  * Initialize the systimer subsystem, called from MI code in early boot.
91  */
92 void
93 cpu_initclocks(void *arg __unused)
94 {
95         int len;
96         kprintf("initclocks\n");
97         len = sizeof(vkernel_cputimer.freq);
98         if (sysctlbyname("kern.cputimer.freq", &vkernel_cputimer.freq, &len,
99                          NULL, NULL) < 0) {
100                 panic("cpu_initclocks: can't get kern.cputimer.freq!");
101         }
102         len = sizeof(cputimer_mib)/sizeof(cputimer_mib[0]);
103         if (sysctlnametomib("kern.cputimer.clock", cputimer_mib, &len) < 0)
104                 panic("cpu_initclocks: can't get kern.cputimer.clock!");
105         cputimer_miblen = len;
106         cputimer_register(&vkernel_cputimer);
107         cputimer_select(&vkernel_cputimer, 0);
108 }
109 SYSINIT(clocksvk, SI_BOOT2_CLOCKREG, SI_ORDER_FIRST, cpu_initclocks, NULL)
110
111 /*
112  * Constructor to initialize timer->base and get an initial count.
113  */
114 static void
115 vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock)
116 {
117         timer->base = 0;
118         timer->base = oclock - vkernel_timer_get_timecount();
119 }
120
121 /*
122  * Get the current counter, with 2's complement rollover.
123  *
124  * NOTE! MPSAFE, possibly no critical section
125  */
126 static sysclock_t
127 vkernel_timer_get_timecount(void)
128 {
129         sysclock_t counter;
130         size_t len;
131
132         len = sizeof(counter);
133         if (sysctl(cputimer_mib, cputimer_miblen, &counter, &len,
134                    NULL, NULL) < 0) {
135                 panic("vkernel_timer_get_timecount: sysctl failed!");
136         }
137         return(counter);
138 }
139
140 /*
141  * Configure the interrupt for our core systimer.  Use the kqueue timer
142  * support functions.
143  */
144 void
145 cputimer_intr_config(struct cputimer *timer)
146 {
147         kqueue_timer_info = kqueue_add_timer(cputimer_intr, NULL);
148 }
149
150 /*
151  * Reload the interrupt for our core systimer.  Because the caller's
152  * reload calculation can be negatively indexed, we need a minimal
153  * check to ensure that a reasonable reload value is selected. 
154  */
155 void
156 cputimer_intr_reload(sysclock_t reload)
157 {
158         if (kqueue_timer_info) {
159                 if ((int)reload < 1)
160                         reload = 1;
161                 kqueue_reload_timer(kqueue_timer_info, (reload + 999) / 1000);
162         }
163 }
164
165 /*
166  * clock interrupt.
167  *
168  * NOTE: frame is a struct intrframe pointer.
169  */
170 static void
171 cputimer_intr(void *dummy, struct intrframe *frame)
172 {
173         static sysclock_t sysclock_count;
174         struct globaldata *gd = mycpu;
175 #ifdef SMP
176         struct globaldata *gscan;
177         int n;
178 #endif
179         
180         sysclock_count = sys_cputimer->count();
181 #ifdef SMP
182         for (n = 0; n < ncpus; ++n) {
183                 gscan = globaldata_find(n);
184                 if (TAILQ_FIRST(&gscan->gd_systimerq) == NULL)
185                         continue;
186                 if (gscan != gd) {
187                         lwkt_send_ipiq3(gscan, (ipifunc3_t)systimer_intr,
188                                         &sysclock_count, 0);
189                 } else {
190                         systimer_intr(&sysclock_count, 0, frame);
191                 }
192         }
193 #else
194         if (TAILQ_FIRST(&gd->gd_systimerq) != NULL)
195                 systimer_intr(&sysclock_count, 0, frame);
196 #endif
197 }
198
199 /*
200  * Initialize the time of day register, based on the time base which is, e.g.
201  * from a filesystem.
202  */
203 void
204 inittodr(time_t base)
205 {
206         struct timespec ts;
207         struct timeval tv;
208
209         gettimeofday(&tv, NULL);
210         ts.tv_sec = tv.tv_sec;
211         ts.tv_nsec = tv.tv_usec * 1000;
212         set_timeofday(&ts);
213 }
214
215 /*
216  * Write system time back to the RTC
217  */
218 void
219 resettodr(void)
220 {
221 }
222
223 void
224 DELAY(int usec)
225 {
226         usleep(usec);
227 }
228
229 void
230 DRIVERSLEEP(int usec)
231 {
232         if (mycpu->gd_intr_nesting_level)
233                 DELAY(usec);
234         else if (1000000 / usec >= hz)
235                 tsleep(DRIVERSLEEP, 0, "DELAY", 1000000 / usec / hz + 1);
236         else
237                 usleep(usec);
238 }
239