1 /* 2 * Copyright (c) 2005 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/kern/kern_cputimer.c,v 1.5 2008/06/05 18:06:32 swildner Exp $ 35 */ 36 /* 37 * Generic cputimer - access to a reliable, free-running counter. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/kernel.h> 42 #include <sys/systm.h> 43 #include <sys/thread.h> 44 #include <sys/globaldata.h> 45 #include <sys/serialize.h> 46 #include <sys/systimer.h> 47 #include <sys/sysctl.h> 48 #include <sys/thread2.h> 49 50 static sysclock_t dummy_cputimer_count(void); 51 52 static struct cputimer dummy_cputimer = { 53 SLIST_ENTRY_INITIALIZER, 54 "dummy", 55 CPUTIMER_PRI_DUMMY, 56 CPUTIMER_DUMMY, 57 dummy_cputimer_count, 58 cputimer_default_fromhz, 59 cputimer_default_fromus, 60 cputimer_default_construct, 61 cputimer_default_destruct, 62 1000000, 63 (1000000LL << 32) / 1000000, 64 (1000000000LL << 32) / 1000000, 65 0 66 }; 67 68 struct cputimer *sys_cputimer = &dummy_cputimer; 69 SLIST_HEAD(, cputimer) cputimerhead = SLIST_HEAD_INITIALIZER(&cputimerhead); 70 71 static int cputimer_intr_ps_reqs; 72 static struct lwkt_serialize cputimer_intr_ps_slize = 73 LWKT_SERIALIZE_INITIALIZER; 74 75 /* 76 * Generic cputimer API 77 */ 78 void 79 cputimer_select(struct cputimer *timer, int pri) 80 { 81 sysclock_t oldclock; 82 83 /* 84 * Calculate helper fields 85 */ 86 cputimer_set_frequency(timer, timer->freq); 87 88 /* 89 * Install a new cputimer if its priority allows it. If timer is 90 * passed as NULL we deinstall the current timer and revert to our 91 * dummy. 92 */ 93 if (pri == 0) 94 pri = timer->pri; 95 if (timer == NULL || pri >= sys_cputimer->pri) { 96 oldclock = sys_cputimer->count(); 97 sys_cputimer->destruct(sys_cputimer); 98 sys_cputimer = &dummy_cputimer; 99 if (timer) { 100 sys_cputimer = timer; 101 timer->construct(timer, oldclock); 102 cputimer_intr_config(timer); 103 } 104 } 105 } 106 107 /* 108 * Register a timer. If the timer has already been registered, do nothing. 109 */ 110 void 111 cputimer_register(struct cputimer *timer) 112 { 113 struct cputimer *scan; 114 115 /* 116 * Initialize dummy_cputimer if the slist is empty, it does not get 117 * registered the normal way. 118 */ 119 if (SLIST_EMPTY(&cputimerhead)) 120 SLIST_FIRST(&cputimerhead) = &dummy_cputimer; 121 SLIST_FOREACH(scan, &cputimerhead, next) { 122 if (scan == timer) 123 return; 124 } 125 SLIST_INSERT_HEAD(&cputimerhead, timer, next); 126 } 127 128 /* 129 * Deregister a timer. If the timer has already been deregistered, do nothing. 130 */ 131 void 132 cputimer_deregister(struct cputimer *timer) 133 { 134 struct cputimer *scan; 135 struct cputimer *best; 136 137 /* 138 * Locate and remove the timer. If the timer is our currently active 139 * timer, revert to the dummy timer. 140 */ 141 SLIST_FOREACH(scan, &cputimerhead, next) { 142 if (timer == scan) { 143 if (timer == sys_cputimer) 144 cputimer_select(&dummy_cputimer, 0x7FFFFFFF); 145 SLIST_REMOVE(&cputimerhead, timer, cputimer, next); 146 break; 147 } 148 } 149 150 /* 151 * If sys_cputimer reverted to the dummy, select the best one 152 */ 153 if (sys_cputimer == &dummy_cputimer) { 154 best = NULL; 155 SLIST_FOREACH(scan, &cputimerhead, next) { 156 if (best == NULL || scan->pri > best->pri) 157 best = scan; 158 } 159 if (best) 160 cputimer_select(best, 0x7FFFFFFF); 161 } 162 } 163 164 /* 165 * Calculate usec / tick and nsec / tick, scaled by (1 << 32). 166 * 167 * so e.g. a 3 mhz timer would be 3 usec / tick x (1 << 32), 168 * or 3000 nsec / tick x (1 << 32) 169 */ 170 void 171 cputimer_set_frequency(struct cputimer *timer, sysclock_t freq) 172 { 173 timer->freq = freq; 174 timer->freq64_usec = (1000000LL << 32) / freq; 175 timer->freq64_nsec = (1000000000LL << 32) / freq; 176 if (timer == sys_cputimer) 177 cputimer_intr_config(timer); 178 } 179 180 sysclock_t 181 cputimer_default_fromhz(int freq) 182 { 183 return(sys_cputimer->freq / freq + 1); 184 } 185 186 sysclock_t 187 cputimer_default_fromus(int us) 188 { 189 return((int64_t)sys_cputimer->freq * us / 1000000); 190 } 191 192 /* 193 * Dummy counter implementation 194 */ 195 static 196 sysclock_t 197 dummy_cputimer_count(void) 198 { 199 return(++dummy_cputimer.base); 200 } 201 202 void 203 cputimer_default_construct(struct cputimer *cputimer, sysclock_t oldclock) 204 { 205 cputimer->base = oldclock; 206 } 207 208 void 209 cputimer_default_destruct(struct cputimer *cputimer) 210 { 211 } 212 213 /************************************************************************ 214 * SYSCTL SUPPORT * 215 ************************************************************************ 216 * 217 * Note: the ability to change the systimer is not currently enabled 218 * because it will mess up systimer calculations. You have to live 219 * with what is configured at boot. 220 */ 221 static int 222 sysctl_cputimer_reglist(SYSCTL_HANDLER_ARGS) 223 { 224 struct cputimer *scan; 225 int error = 0; 226 int loop = 0; 227 228 /* 229 * Build a list of available timers 230 */ 231 SLIST_FOREACH(scan, &cputimerhead, next) { 232 if (error == 0 && loop) 233 error = SYSCTL_OUT(req, " ", 1); 234 if (error == 0) 235 error = SYSCTL_OUT(req, scan->name, strlen(scan->name)); 236 ++loop; 237 } 238 return (error); 239 } 240 241 static int 242 sysctl_cputimer_name(SYSCTL_HANDLER_ARGS) 243 { 244 int error; 245 246 error = SYSCTL_OUT(req, sys_cputimer->name, strlen(sys_cputimer->name)); 247 return (error); 248 } 249 250 static int 251 sysctl_cputimer_clock(SYSCTL_HANDLER_ARGS) 252 { 253 sysclock_t clock; 254 int error; 255 256 clock = sys_cputimer->count(); 257 error = SYSCTL_OUT(req, &clock, sizeof(clock)); 258 return (error); 259 } 260 261 static int 262 sysctl_cputimer_freq(SYSCTL_HANDLER_ARGS) 263 { 264 int error; 265 266 error = SYSCTL_OUT(req, &sys_cputimer->freq, sizeof(sys_cputimer->freq)); 267 return (error); 268 } 269 270 SYSCTL_DECL(_kern_cputimer); 271 SYSCTL_NODE(_kern, OID_AUTO, cputimer, CTLFLAG_RW, NULL, "cputimer"); 272 273 SYSCTL_PROC(_kern_cputimer, OID_AUTO, select, CTLTYPE_STRING|CTLFLAG_RD, 274 NULL, 0, sysctl_cputimer_reglist, "A", ""); 275 SYSCTL_PROC(_kern_cputimer, OID_AUTO, name, CTLTYPE_STRING|CTLFLAG_RD, 276 NULL, 0, sysctl_cputimer_name, "A", ""); 277 SYSCTL_PROC(_kern_cputimer, OID_AUTO, clock, CTLTYPE_UINT|CTLFLAG_RD, 278 NULL, 0, sysctl_cputimer_clock, "IU", ""); 279 SYSCTL_PROC(_kern_cputimer, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD, 280 NULL, 0, sysctl_cputimer_freq, "I", ""); 281 282 static struct cputimer_intr *sys_cputimer_intr; 283 static uint32_t cputimer_intr_caps; 284 SLIST_HEAD(, cputimer_intr) cputimer_intr_head = 285 SLIST_HEAD_INITIALIZER(&cputimer_intr_head); 286 287 void 288 cputimer_intr_register(struct cputimer_intr *cti) 289 { 290 struct cputimer_intr *scan; 291 292 SLIST_FOREACH(scan, &cputimer_intr_head, next) { 293 if (scan == cti) 294 return; 295 } 296 cti->config(cti, sys_cputimer); 297 SLIST_INSERT_HEAD(&cputimer_intr_head, cti, next); 298 } 299 300 void 301 cputimer_intr_deregister(struct cputimer_intr *cti) 302 { 303 KKASSERT(cti != sys_cputimer_intr); 304 SLIST_REMOVE(&cputimer_intr_head, cti, cputimer_intr, next); 305 } 306 307 int 308 cputimer_intr_select(struct cputimer_intr *cti, int prio) 309 { 310 KKASSERT(cti != NULL); 311 312 if (prio == 0) 313 prio = cti->prio; 314 315 if (sys_cputimer_intr == NULL) { 316 KKASSERT(cputimer_intr_caps == 0); 317 sys_cputimer_intr = cti; 318 return 0; 319 } 320 321 if ((cti->caps & cputimer_intr_caps) == cputimer_intr_caps) { 322 if (prio > sys_cputimer_intr->prio) { 323 sys_cputimer_intr = cti; 324 return 0; 325 } else { 326 return EBUSY; 327 } 328 } else { 329 return EOPNOTSUPP; 330 } 331 } 332 333 void 334 cputimer_intr_default_enable(struct cputimer_intr *cti __unused) 335 { 336 } 337 338 void 339 cputimer_intr_default_restart(struct cputimer_intr *cti) 340 { 341 cti->reload(cti, 0); 342 } 343 344 void 345 cputimer_intr_default_config(struct cputimer_intr *cti __unused, 346 const struct cputimer *timer __unused) 347 { 348 } 349 350 void 351 cputimer_intr_default_pmfixup(struct cputimer_intr *cti __unused) 352 { 353 } 354 355 void 356 cputimer_intr_default_initclock(struct cputimer_intr *cti __unused, 357 boolean_t selected __unused) 358 { 359 } 360 361 void 362 cputimer_intr_enable(void) 363 { 364 struct cputimer_intr *cti; 365 366 SLIST_FOREACH(cti, &cputimer_intr_head, next) 367 cti->enable(cti); 368 } 369 370 void 371 cputimer_intr_config(const struct cputimer *timer) 372 { 373 struct cputimer_intr *cti; 374 375 SLIST_FOREACH(cti, &cputimer_intr_head, next) 376 cti->config(cti, timer); 377 } 378 379 void 380 cputimer_intr_pmfixup(void) 381 { 382 struct cputimer_intr *cti; 383 384 SLIST_FOREACH(cti, &cputimer_intr_head, next) 385 cti->pmfixup(cti); 386 } 387 388 void 389 cputimer_intr_reload(sysclock_t reload) 390 { 391 struct cputimer_intr *cti = sys_cputimer_intr; 392 393 cti->reload(cti, reload); 394 } 395 396 void 397 cputimer_intr_restart(void) 398 { 399 struct cputimer_intr *cti = sys_cputimer_intr; 400 401 cti->restart(cti); 402 } 403 404 int 405 cputimer_intr_select_caps(uint32_t caps) 406 { 407 struct cputimer_intr *cti, *maybe; 408 int error; 409 410 maybe = NULL; 411 SLIST_FOREACH(cti, &cputimer_intr_head, next) { 412 if ((cti->caps & caps) == caps) { 413 if (maybe == NULL) 414 maybe = cti; 415 else if (cti->prio > maybe->prio) 416 maybe = cti; 417 } 418 } 419 if (maybe == NULL) 420 return ENOENT; 421 422 if (sys_cputimer_intr == maybe) 423 return 0; 424 425 cputimer_intr_caps = caps; 426 error = cputimer_intr_select(maybe, CPUTIMER_INTR_PRIO_MAX); 427 KKASSERT(!error); 428 429 return ERESTART; 430 } 431 432 static void 433 cputimer_intr_initclocks(void) 434 { 435 struct cputimer_intr *cti, *ncti; 436 437 /* 438 * An interrupt cputimer may deregister itself, 439 * so use SLIST_FOREACH_MUTABLE here. 440 */ 441 SLIST_FOREACH_MUTABLE(cti, &cputimer_intr_head, next, ncti) { 442 boolean_t selected = FALSE; 443 444 if (cti == sys_cputimer_intr) 445 selected = TRUE; 446 cti->initclock(cti, selected); 447 } 448 } 449 /* NOTE: Must be SECOND to allow platform initialization to go first */ 450 SYSINIT(cputimer_intr, SI_BOOT2_CLOCKREG, SI_ORDER_SECOND, 451 cputimer_intr_initclocks, NULL) 452 453 static int 454 sysctl_cputimer_intr_reglist(SYSCTL_HANDLER_ARGS) 455 { 456 struct cputimer_intr *scan; 457 int error = 0; 458 int loop = 0; 459 460 /* 461 * Build a list of available interrupt cputimers 462 */ 463 SLIST_FOREACH(scan, &cputimer_intr_head, next) { 464 if (error == 0 && loop) 465 error = SYSCTL_OUT(req, " ", 1); 466 if (error == 0) 467 error = SYSCTL_OUT(req, scan->name, strlen(scan->name)); 468 ++loop; 469 } 470 return (error); 471 } 472 473 static int 474 sysctl_cputimer_intr_freq(SYSCTL_HANDLER_ARGS) 475 { 476 int error; 477 478 error = SYSCTL_OUT(req, &sys_cputimer_intr->freq, 479 sizeof(sys_cputimer_intr->freq)); 480 return (error); 481 } 482 483 static int 484 sysctl_cputimer_intr_select(SYSCTL_HANDLER_ARGS) 485 { 486 struct cputimer_intr *cti; 487 char name[32]; 488 int error; 489 490 ksnprintf(name, sizeof(name), "%s", sys_cputimer_intr->name); 491 error = sysctl_handle_string(oidp, name, sizeof(name), req); 492 if (error != 0 || req->newptr == NULL) 493 return error; 494 495 SLIST_FOREACH(cti, &cputimer_intr_head, next) { 496 if (strcmp(cti->name, name) == 0) 497 break; 498 } 499 if (cti == NULL) 500 return ENOENT; 501 if (cti == sys_cputimer_intr) 502 return 0; 503 504 error = cputimer_intr_select(cti, CPUTIMER_INTR_PRIO_MAX); 505 if (!error) 506 cputimer_intr_restart(); 507 return error; 508 } 509 510 SYSCTL_NODE(_kern_cputimer, OID_AUTO, intr, CTLFLAG_RW, NULL, 511 "interrupt cputimer"); 512 513 SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, reglist, CTLTYPE_STRING|CTLFLAG_RD, 514 NULL, 0, sysctl_cputimer_intr_reglist, "A", ""); 515 SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD, 516 NULL, 0, sysctl_cputimer_intr_freq, "I", ""); 517 SYSCTL_PROC(_kern_cputimer_intr, OID_AUTO, select, CTLTYPE_STRING|CTLFLAG_RW, 518 NULL, 0, sysctl_cputimer_intr_select, "A", ""); 519 520 int 521 cputimer_intr_powersave_addreq(void) 522 { 523 int error = 0; 524 525 lwkt_serialize_enter(&cputimer_intr_ps_slize); 526 527 ++cputimer_intr_ps_reqs; 528 if (cputimer_intr_ps_reqs == 1) { 529 /* 530 * Upon the first power saving request, switch to an one shot 531 * timer, which would not stop in the any power saving state. 532 */ 533 error = cputimer_intr_select_caps(CPUTIMER_INTR_CAP_PS); 534 if (error == ERESTART) { 535 error = 0; 536 if (bootverbose) 537 kprintf("cputimer: first power save request, restart\n"); 538 cputimer_intr_restart(); 539 } else if (error) { 540 kprintf("no suitable intr cputimer found\n"); 541 --cputimer_intr_ps_reqs; 542 } else if (bootverbose) { 543 kprintf("cputimer: first power save request\n"); 544 } 545 } 546 547 lwkt_serialize_exit(&cputimer_intr_ps_slize); 548 549 return error; 550 } 551 552 void 553 cputimer_intr_powersave_remreq(void) 554 { 555 lwkt_serialize_enter(&cputimer_intr_ps_slize); 556 557 KASSERT(cputimer_intr_ps_reqs > 0, 558 ("invalid # of powersave reqs %d", cputimer_intr_ps_reqs)); 559 --cputimer_intr_ps_reqs; 560 if (cputimer_intr_ps_reqs == 0) { 561 int error; 562 563 /* No one needs power saving, use a better one shot timer. */ 564 error = cputimer_intr_select_caps(CPUTIMER_INTR_CAP_NONE); 565 KKASSERT(!error || error == ERESTART); 566 if (error == ERESTART) { 567 if (bootverbose) 568 kprintf("cputimer: no powser save request, restart\n"); 569 cputimer_intr_restart(); 570 } else if (bootverbose) { 571 kprintf("cputimer: no power save request\n"); 572 } 573 } 574 575 lwkt_serialize_exit(&cputimer_intr_ps_slize); 576 } 577