| Commit | Line | Data |
|---|---|---|
| 6f7b98e0 MD |
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 | * | |
| 201b3f37 | 34 | * $DragonFly: src/sys/platform/vkernel/platform/systimer.c,v 1.17 2008/06/06 13:19:25 swildner Exp $ |
| 6f7b98e0 MD |
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> | |
| 60297eb4 | 42 | #include <sys/signal.h> |
| 1130c186 MD |
43 | #include <sys/interrupt.h> |
| 44 | #include <sys/bus.h> | |
| 60297eb4 | 45 | #include <sys/time.h> |
| 6f7b98e0 | 46 | #include <machine/cpu.h> |
| 870b0161 | 47 | #include <machine/clock.h> |
| 1130c186 | 48 | #include <machine/globaldata.h> |
| 7c1212ec | 49 | #include <machine/md_var.h> |
| 6f7b98e0 | 50 | |
| 1fdc022f MD |
51 | #include <sys/thread2.h> |
| 52 | ||
| 6f7b98e0 | 53 | #include <unistd.h> |
| 60297eb4 | 54 | #include <signal.h> |
| 6f7b98e0 | 55 | |
| ef612539 SZ |
56 | #define VKTIMER_FREQ 1000000 /* 1us granularity */ |
| 57 | ||
| 58 | static void vktimer_intr(void *dummy, struct intrframe *frame); | |
| 9aeb4297 | 59 | |
| 6f7b98e0 MD |
60 | int disable_rtc_set; |
| 61 | SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, | |
| 62 | CTLFLAG_RW, &disable_rtc_set, 0, ""); | |
| 870b0161 MD |
63 | SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD, |
| 64 | &tsc_present, 0, "TSC Available"); | |
| 65 | SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD, | |
| 66 | &tsc_frequency, 0, "TSC Frequency"); | |
| 6f7b98e0 MD |
67 | |
| 68 | int adjkerntz; | |
| 60297eb4 | 69 | int wall_cmos_clock = 0; |
| 9aeb4297 MD |
70 | static struct kqueue_info *kqueue_timer_info; |
| 71 | ||
| 0a76854c MD |
72 | static int cputimer_mib[16]; |
| 73 | static int cputimer_miblen; | |
| 74 | ||
| 60297eb4 MD |
75 | /* |
| 76 | * SYSTIMER IMPLEMENTATION | |
| 77 | */ | |
| 78 | static sysclock_t vkernel_timer_get_timecount(void); | |
| 79 | static void vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock); | |
| 60297eb4 MD |
80 | |
| 81 | static struct cputimer vkernel_cputimer = { | |
| 82 | SLIST_ENTRY_INITIALIZER, | |
| 83 | "VKERNEL", | |
| 84 | CPUTIMER_PRI_VKERNEL, | |
| 85 | CPUTIMER_VKERNEL, | |
| 86 | vkernel_timer_get_timecount, | |
| 87 | cputimer_default_fromhz, | |
| 88 | cputimer_default_fromus, | |
| 89 | vkernel_timer_construct, | |
| 90 | cputimer_default_destruct, | |
| ef612539 | 91 | VKTIMER_FREQ, |
| 60297eb4 MD |
92 | 0, 0, 0 |
| 93 | }; | |
| 94 | ||
| ef612539 SZ |
95 | static void vktimer_intr_reload(struct cputimer_intr *, sysclock_t); |
| 96 | static void vktimer_intr_initclock(struct cputimer_intr *); | |
| 97 | ||
| 98 | static struct cputimer_intr vkernel_cputimer_intr = { | |
| 99 | .freq = VKTIMER_FREQ, | |
| 100 | .reload = vktimer_intr_reload, | |
| 101 | .enable = cputimer_intr_default_enable, | |
| 102 | .config = cputimer_intr_default_config, | |
| 103 | .restart = cputimer_intr_default_restart, | |
| 104 | .pmfixup = cputimer_intr_default_pmfixup, | |
| 105 | .initclock = vktimer_intr_initclock, | |
| 106 | .next = SLIST_ENTRY_INITIALIZER, | |
| 107 | .name = "vkernel", | |
| 108 | .type = CPUTIMER_INTR_VKERNEL, | |
| 109 | .prio = CPUTIMER_INTR_PRIO_VKERNEL, | |
| 110 | .caps = CPUTIMER_INTR_CAP_NONE | |
| 111 | }; | |
| 112 | ||
| 60297eb4 MD |
113 | /* |
| 114 | * Initialize the systimer subsystem, called from MI code in early boot. | |
| 115 | */ | |
| 50b53814 SZ |
116 | static void |
| 117 | cpu_initclocks(void) | |
| 6f7b98e0 | 118 | { |
| 0a76854c | 119 | int len; |
| 50b53814 | 120 | |
| 60297eb4 | 121 | kprintf("initclocks\n"); |
| 0a76854c MD |
122 | len = sizeof(vkernel_cputimer.freq); |
| 123 | if (sysctlbyname("kern.cputimer.freq", &vkernel_cputimer.freq, &len, | |
| 201b3f37 | 124 | NULL, 0) < 0) { |
| 0a76854c MD |
125 | panic("cpu_initclocks: can't get kern.cputimer.freq!"); |
| 126 | } | |
| 127 | len = sizeof(cputimer_mib)/sizeof(cputimer_mib[0]); | |
| 128 | if (sysctlnametomib("kern.cputimer.clock", cputimer_mib, &len) < 0) | |
| 129 | panic("cpu_initclocks: can't get kern.cputimer.clock!"); | |
| 130 | cputimer_miblen = len; | |
| ef612539 SZ |
131 | |
| 132 | cputimer_intr_register(&vkernel_cputimer_intr); | |
| 133 | cputimer_intr_select(&vkernel_cputimer_intr, 0); | |
| 134 | ||
| 60297eb4 MD |
135 | cputimer_register(&vkernel_cputimer); |
| 136 | cputimer_select(&vkernel_cputimer, 0); | |
| 137 | } | |
| ba39e2e0 | 138 | SYSINIT(clocksvk, SI_BOOT2_CLOCKREG, SI_ORDER_FIRST, cpu_initclocks, NULL) |
| 60297eb4 MD |
139 | |
| 140 | /* | |
| 141 | * Constructor to initialize timer->base and get an initial count. | |
| 142 | */ | |
| 143 | static void | |
| 144 | vkernel_timer_construct(struct cputimer *timer, sysclock_t oclock) | |
| 145 | { | |
| 146 | timer->base = 0; | |
| 147 | timer->base = oclock - vkernel_timer_get_timecount(); | |
| 148 | } | |
| 149 | ||
| 150 | /* | |
| 151 | * Get the current counter, with 2's complement rollover. | |
| 0a76854c MD |
152 | * |
| 153 | * NOTE! MPSAFE, possibly no critical section | |
| 60297eb4 MD |
154 | */ |
| 155 | static sysclock_t | |
| 156 | vkernel_timer_get_timecount(void) | |
| 157 | { | |
| 60297eb4 | 158 | sysclock_t counter; |
| 2b41ad07 | 159 | size_t len; |
| 60297eb4 | 160 | |
| 0a76854c MD |
161 | len = sizeof(counter); |
| 162 | if (sysctl(cputimer_mib, cputimer_miblen, &counter, &len, | |
| 201b3f37 | 163 | NULL, 0) < 0) { |
| 0a76854c MD |
164 | panic("vkernel_timer_get_timecount: sysctl failed!"); |
| 165 | } | |
| 60297eb4 | 166 | return(counter); |
| 6f7b98e0 MD |
167 | } |
| 168 | ||
| 60297eb4 | 169 | /* |
| ef612539 | 170 | * Initialize the interrupt for our core systimer. Use the kqueue timer |
| 9aeb4297 | 171 | * support functions. |
| 60297eb4 | 172 | */ |
| ef612539 SZ |
173 | static void |
| 174 | vktimer_intr_initclock(struct cputimer_intr *cti __unused) | |
| 6f7b98e0 | 175 | { |
| ef612539 SZ |
176 | KKASSERT(kqueue_timer_info == NULL); |
| 177 | kqueue_timer_info = kqueue_add_timer(vktimer_intr, NULL); | |
| 6f7b98e0 MD |
178 | } |
| 179 | ||
| 60297eb4 | 180 | /* |
| 307ae84d MD |
181 | * Reload the interrupt for our core systimer. Because the caller's |
| 182 | * reload calculation can be negatively indexed, we need a minimal | |
| 183 | * check to ensure that a reasonable reload value is selected. | |
| 60297eb4 | 184 | */ |
| c5b8324c | 185 | static void |
| ef612539 | 186 | vktimer_intr_reload(struct cputimer_intr *cti __unused, sysclock_t reload) |
| 6f7b98e0 | 187 | { |
| 307ae84d MD |
188 | if (kqueue_timer_info) { |
| 189 | if ((int)reload < 1) | |
| 190 | reload = 1; | |
| 9aeb4297 | 191 | kqueue_reload_timer(kqueue_timer_info, (reload + 999) / 1000); |
| 307ae84d | 192 | } |
| 60297eb4 MD |
193 | } |
| 194 | ||
| 4b486183 MD |
195 | /* |
| 196 | * clock interrupt. | |
| 197 | * | |
| 198 | * NOTE: frame is a struct intrframe pointer. | |
| 199 | */ | |
| 9aeb4297 | 200 | static void |
| ef612539 | 201 | vktimer_intr(void *dummy, struct intrframe *frame) |
| 60297eb4 MD |
202 | { |
| 203 | static sysclock_t sysclock_count; | |
| 204 | struct globaldata *gd = mycpu; | |
| 205 | #ifdef SMP | |
| 206 | struct globaldata *gscan; | |
| 207 | int n; | |
| 208 | #endif | |
| 9aeb4297 | 209 | |
| 60297eb4 MD |
210 | sysclock_count = sys_cputimer->count(); |
| 211 | #ifdef SMP | |
| 212 | for (n = 0; n < ncpus; ++n) { | |
| 213 | gscan = globaldata_find(n); | |
| 214 | if (TAILQ_FIRST(&gscan->gd_systimerq) == NULL) | |
| 215 | continue; | |
| 216 | if (gscan != gd) { | |
| 217 | lwkt_send_ipiq3(gscan, (ipifunc3_t)systimer_intr, | |
| 218 | &sysclock_count, 0); | |
| 219 | } else { | |
| 4b486183 | 220 | systimer_intr(&sysclock_count, 0, frame); |
| 60297eb4 MD |
221 | } |
| 222 | } | |
| 223 | #else | |
| 224 | if (TAILQ_FIRST(&gd->gd_systimerq) != NULL) | |
| 4b486183 | 225 | systimer_intr(&sysclock_count, 0, frame); |
| 60297eb4 | 226 | #endif |
| 6f7b98e0 MD |
227 | } |
| 228 | ||
| 229 | /* | |
| 230 | * Initialize the time of day register, based on the time base which is, e.g. | |
| 231 | * from a filesystem. | |
| 232 | */ | |
| 233 | void | |
| 234 | inittodr(time_t base) | |
| 235 | { | |
| 0171c06b MD |
236 | struct timespec ts; |
| 237 | struct timeval tv; | |
| 238 | ||
| 239 | gettimeofday(&tv, NULL); | |
| 240 | ts.tv_sec = tv.tv_sec; | |
| 241 | ts.tv_nsec = tv.tv_usec * 1000; | |
| 242 | set_timeofday(&ts); | |
| 6f7b98e0 MD |
243 | } |
| 244 | ||
| 245 | /* | |
| 246 | * Write system time back to the RTC | |
| 247 | */ | |
| 248 | void | |
| 249 | resettodr(void) | |
| 250 | { | |
| 6f7b98e0 MD |
251 | } |
| 252 | ||
| 253 | void | |
| 254 | DELAY(int usec) | |
| 255 | { | |
| 256 | usleep(usec); | |
| 257 | } | |
| 258 | ||
| dbcd0c9b MD |
259 | void |
| 260 | DRIVERSLEEP(int usec) | |
| 261 | { | |
| 262 | if (mycpu->gd_intr_nesting_level) | |
| 263 | DELAY(usec); | |
| 264 | else if (1000000 / usec >= hz) | |
| 265 | tsleep(DRIVERSLEEP, 0, "DELAY", 1000000 / usec / hz + 1); | |
| 266 | else | |
| 267 | usleep(usec); | |
| 268 | } | |
| 269 |