HPET.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #include "HPET.h"
  2. #include <common/kprint.h>
  3. #include <mm/mm.h>
  4. #include <driver/interrupt/apic/apic.h>
  5. #include <exception/softirq.h>
  6. #include <driver/timers/timer.h>
  7. #include <process/process.h>
  8. #include <sched/sched.h>
  9. #include <smp/ipi.h>
  10. #include <driver/video/video.h>
  11. static struct acpi_HPET_description_table_t *hpet_table;
  12. static uint64_t HPET_REG_BASE = 0;
  13. static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒)
  14. static double HPET_freq = 0; // 主计时器频率
  15. static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量
  16. extern struct rtc_time_t rtc_now; // 导出全局墙上时钟
  17. enum
  18. {
  19. GCAP_ID = 0x00,
  20. GEN_CONF = 0x10,
  21. GINTR_STA = 0x20,
  22. MAIN_CNT = 0xf0,
  23. TIM0_CONF = 0x100,
  24. TIM0_COMP = 0x108,
  25. TIM1_CONF = 0x120,
  26. TIM1_COMP = 0x128,
  27. TIM2_CONF = 0x140,
  28. TIM2_COMP = 0x148,
  29. TIM3_CONF = 0x160,
  30. TIM3_COMP = 0x168,
  31. TIM4_CONF = 0x180,
  32. TIM4_COMP = 0x188,
  33. TIM5_CONF = 0x1a0,
  34. TIM5_COMP = 0x1a8,
  35. TIM6_CONF = 0x1c0,
  36. TIM6_COMP = 0x1c8,
  37. TIM7_CONF = 0x1e0,
  38. TIM7_COMP = 0x1e8,
  39. };
  40. hardware_intr_controller HPET_intr_controller =
  41. {
  42. .enable = apic_ioapic_enable,
  43. .disable = apic_ioapic_disable,
  44. .install = apic_ioapic_install,
  45. .uninstall = apic_ioapic_uninstall,
  46. .ack = apic_ioapic_edge_ack,
  47. };
  48. void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
  49. {
  50. // printk("(HPET)");
  51. switch (param)
  52. {
  53. case 0: // 定时器0中断
  54. ++timer_jiffies;
  55. /*
  56. // 将HEPT中断消息转发到ap:1处理器
  57. ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0xc8,
  58. ICR_APIC_FIXED, ICR_ALL_EXCLUDE_Self, true, 0);
  59. */
  60. // 若当前时间比定时任务的时间间隔大,则进入中断下半部
  61. if (container_of(list_next(&timer_func_head.list), struct timer_func_list_t, list)->expire_jiffies <= timer_jiffies)
  62. set_softirq_status((1 << TIMER_SIRQ));
  63. sched_update_jiffies();
  64. break;
  65. default:
  66. kwarn("Unsupported HPET irq: %d.", number);
  67. break;
  68. }
  69. }
  70. int HPET_init()
  71. {
  72. kinfo("Initializing HPET...");
  73. // 从acpi获取hpet结构体
  74. ul hpet_table_addr = 0;
  75. acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
  76. // ACPI表没有HPET,尝试读HPTC
  77. if (hpet_table_addr == 0)
  78. {
  79. kwarn("ACPI: HPET Table Not Found On This Computer!");
  80. if (RCBA_vaddr != 0)
  81. {
  82. kerror("NO HPET found on this computer!");
  83. uint32_t *hptc = (uint32_t *)(RCBA_vaddr + 0x3404UL);
  84. // enable HPET
  85. io_mfence();
  86. // 读取HPET配置寄存器地址
  87. switch ((*hptc) & 0x3)
  88. {
  89. case 0:
  90. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
  91. break;
  92. case 1:
  93. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed01000;
  94. break;
  95. case 2:
  96. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed02000;
  97. break;
  98. case 3:
  99. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed03000;
  100. break;
  101. default:
  102. break;
  103. }
  104. // enable HPET
  105. *hptc = 0x80;
  106. io_mfence();
  107. }
  108. else
  109. {
  110. // 没有RCBA寄存器,采用默认值
  111. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
  112. kwarn("There is no RCBA register on this computer, and HPET regs base use default value.");
  113. }
  114. }
  115. else // ACPI表中有HPET表
  116. {
  117. hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr;
  118. // kdebug("hpet_table_addr=%#018lx", hpet_table_addr);
  119. // 由于这段内存与io/apic的映射在同一物理页内,因此不需要重复映射
  120. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address;
  121. }
  122. // 读取计时精度并计算频率
  123. uint64_t tmp;
  124. tmp = *(uint64_t *)(HPET_REG_BASE + GCAP_ID);
  125. HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff;
  126. HPET_freq = 1.0 * 1e15 / HPET_COUNTER_CLK_PERIOD;
  127. HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
  128. // kinfo("HPET CLK_PERIOD=%#03lx Frequency=%f", HPET_COUNTER_CLK_PERIOD, (double)HPET_freq);
  129. struct apic_IO_APIC_RTE_entry entry;
  130. // 使用I/O APIC 的IRQ2接收hpet定时器0的中断
  131. apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
  132. // 计算HPET0间隔多少个时钟周期触发一次中断
  133. uint64_t clks_to_intr = 0.001 * HPET0_INTERVAL * HPET_freq;
  134. if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
  135. {
  136. kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
  137. while (1)
  138. hlt();
  139. }
  140. *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
  141. io_mfence();
  142. *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时,边沿触发,投递到IO APIC的2号引脚(这里有点绕,写的是8259的引脚号,但是因为禁用了8259,因此会被路由到IO APIC的2号引脚)
  143. io_mfence();
  144. *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr; // 5ms触发一次中断
  145. //*(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = HPET_freq; // 1s触发一次中断
  146. io_mfence();
  147. rtc_get_cmos_time(&rtc_now);
  148. kinfo("HPET Initialized.");
  149. *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
  150. io_mfence();
  151. // 注册中断
  152. irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
  153. }