timer.c 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2011-2018 K. Lange
  5. *
  6. * Programmable Interrupt Timer
  7. */
  8. #include <kernel/system.h>
  9. #include <kernel/logging.h>
  10. #include <kernel/process.h>
  11. #define PIT_A 0x40
  12. #define PIT_B 0x41
  13. #define PIT_C 0x42
  14. #define PIT_CONTROL 0x43
  15. #define PIT_MASK 0xFF
  16. #define PIT_SCALE 1193180
  17. #define PIT_SET 0x34
  18. #define TIMER_IRQ 0
  19. #define SUBTICKS_PER_TICK 1000
  20. #define RESYNC_TIME 1
  21. /*
  22. * Set the phase (in hertz) for the Programmable
  23. * Interrupt Timer (PIT).
  24. */
  25. void
  26. timer_phase(
  27. int hz
  28. ) {
  29. int divisor = PIT_SCALE / hz;
  30. outportb(PIT_CONTROL, PIT_SET);
  31. outportb(PIT_A, divisor & PIT_MASK);
  32. outportb(PIT_A, (divisor >> 8) & PIT_MASK);
  33. }
  34. /*
  35. * Internal timer counters
  36. */
  37. unsigned long timer_ticks = 0;
  38. unsigned long timer_subticks = 0;
  39. signed long timer_drift = 0;
  40. signed long _timer_drift = 0;
  41. static int behind = 0;
  42. /*
  43. * IRQ handler for when the timer fires
  44. */
  45. int timer_handler(struct regs *r) {
  46. if (++timer_subticks == SUBTICKS_PER_TICK || (behind && ++timer_subticks == SUBTICKS_PER_TICK)) {
  47. timer_ticks++;
  48. timer_subticks = 0;
  49. if (timer_ticks % RESYNC_TIME == 0) {
  50. uint32_t new_time = read_cmos();
  51. _timer_drift = new_time - boot_time - timer_ticks;
  52. if (_timer_drift > 0) behind = 1;
  53. else behind = 0;
  54. }
  55. }
  56. irq_ack(TIMER_IRQ);
  57. wakeup_sleepers(timer_ticks, timer_subticks);
  58. switch_task(1);
  59. return 1;
  60. }
  61. void relative_time(unsigned long seconds, unsigned long subseconds, unsigned long * out_seconds, unsigned long * out_subseconds) {
  62. if (subseconds + timer_subticks > SUBTICKS_PER_TICK) {
  63. *out_seconds = timer_ticks + seconds + 1;
  64. *out_subseconds = (subseconds + timer_subticks) - SUBTICKS_PER_TICK;
  65. } else {
  66. *out_seconds = timer_ticks + seconds;
  67. *out_subseconds = timer_subticks + subseconds;
  68. }
  69. }
  70. /*
  71. * Device installer for the PIT
  72. */
  73. void timer_install(void) {
  74. debug_print(NOTICE,"Initializing interval timer");
  75. boot_time = read_cmos();
  76. irq_install_handler(TIMER_IRQ, timer_handler, "pit timer");
  77. timer_phase(SUBTICKS_PER_TICK); /* 100Hz */
  78. }