HPET.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. #include "HPET.h"
  2. #include <common/kprint.h>
  3. #include <common/compiler.h>
  4. #include <mm/mm.h>
  5. #include <driver/interrupt/apic/apic.h>
  6. #include <exception/softirq.h>
  7. #include <time/timer.h>
  8. #include <process/process.h>
  9. #include <sched/sched.h>
  10. #include <smp/ipi.h>
  11. #include <driver/video/video.h>
  12. #include <driver/interrupt/apic/apic_timer.h>
  13. #include <common/spinlock.h>
  14. #pragma GCC push_options
  15. #pragma GCC optimize("O0")
  16. static struct acpi_HPET_description_table_t *hpet_table;
  17. static uint64_t HPET_REG_BASE = 0;
  18. static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒)
  19. static double HPET_freq = 0; // 主计时器频率
  20. static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量
  21. static char measure_apic_timer_flag; // 初始化apic时钟时所用到的标志变量
  22. // 测定tsc频率的临时变量
  23. static uint64_t test_tsc_start = 0;
  24. static uint64_t test_tsc_end = 0;
  25. extern uint64_t Cpu_tsc_freq; // 导出自cpu.c
  26. extern struct rtc_time_t rtc_now; // 导出全局墙上时钟
  27. enum
  28. {
  29. GCAP_ID = 0x00,
  30. GEN_CONF = 0x10,
  31. GINTR_STA = 0x20,
  32. MAIN_CNT = 0xf0,
  33. TIM0_CONF = 0x100,
  34. TIM0_COMP = 0x108,
  35. TIM1_CONF = 0x120,
  36. TIM1_COMP = 0x128,
  37. TIM2_CONF = 0x140,
  38. TIM2_COMP = 0x148,
  39. TIM3_CONF = 0x160,
  40. TIM3_COMP = 0x168,
  41. TIM4_CONF = 0x180,
  42. TIM4_COMP = 0x188,
  43. TIM5_CONF = 0x1a0,
  44. TIM5_COMP = 0x1a8,
  45. TIM6_CONF = 0x1c0,
  46. TIM6_COMP = 0x1c8,
  47. TIM7_CONF = 0x1e0,
  48. TIM7_COMP = 0x1e8,
  49. };
  50. hardware_intr_controller HPET_intr_controller =
  51. {
  52. .enable = apic_ioapic_enable,
  53. .disable = apic_ioapic_disable,
  54. .install = apic_ioapic_install,
  55. .uninstall = apic_ioapic_uninstall,
  56. .ack = apic_ioapic_edge_ack,
  57. };
  58. void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
  59. {
  60. // printk("(HPET)");
  61. switch (param)
  62. {
  63. case 0: // 定时器0中断
  64. timer_jiffies += HPET0_INTERVAL;
  65. /*
  66. // 将HEPT中断消息转发到ap:1处理器
  67. ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0xc8,
  68. ICR_APIC_FIXED, ICR_ALL_EXCLUDE_Self, true, 0);
  69. */
  70. // 若当前时间比定时任务的时间间隔大,则进入中断下半部
  71. if (container_of(list_next(&timer_func_head.list), struct timer_func_list_t, list)->expire_jiffies <= timer_jiffies)
  72. raise_softirq(TIMER_SIRQ);
  73. // 当时间到了,或进程发生切换时,刷新帧缓冲区
  74. if (timer_jiffies >= video_refresh_expire_jiffies || (video_last_refresh_pid != current_pcb->pid))
  75. {
  76. raise_softirq(VIDEO_REFRESH_SIRQ);
  77. // 超过130ms仍未刷新完成,则重新发起刷新(防止由于进程异常退出导致的屏幕无法刷新)
  78. if (unlikely(timer_jiffies >= (video_refresh_expire_jiffies + (1 << 17))))
  79. {
  80. video_refresh_expire_jiffies = timer_jiffies + (1 << 20);
  81. clear_softirq_pending(VIDEO_REFRESH_SIRQ);
  82. }
  83. }
  84. break;
  85. default:
  86. kwarn("Unsupported HPET irq: %d.", number);
  87. break;
  88. }
  89. }
  90. /**
  91. * @brief 测定apic定时器以及tsc的频率的中断回调函数
  92. *
  93. */
  94. void HPET_measure_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
  95. {
  96. test_tsc_end = rdtsc();
  97. // 停止apic定时器
  98. // 写入每1ms的ticks
  99. apic_timer_stop();
  100. apic_timer_ticks_result = 0xFFFFFFFF - apic_timer_get_current();
  101. measure_apic_timer_flag = true;
  102. }
  103. /**
  104. * @brief 测定apic定时器以及tsc的频率
  105. *
  106. */
  107. void HPET_measure_freq()
  108. {
  109. kinfo("Measuring local APIC timer's frequency...");
  110. const uint64_t interval = APIC_TIMER_INTERVAL; // 测量给定时间内的计数
  111. struct apic_IO_APIC_RTE_entry entry;
  112. // 使用I/O APIC 的IRQ2接收hpet定时器0的中断
  113. apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
  114. // 计算HPET0间隔多少个时钟周期触发一次中断
  115. uint64_t clks_to_intr = 0.001 * interval * HPET_freq;
  116. // kdebug("clks_to_intr=%#ld", clks_to_intr);
  117. if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
  118. {
  119. kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
  120. while (1)
  121. hlt();
  122. }
  123. *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
  124. io_mfence();
  125. *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x0044; // 设置定时器0为非周期,边沿触发,默认投递到IO APIC的2号引脚
  126. io_mfence();
  127. *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr;
  128. io_mfence();
  129. measure_apic_timer_flag = false;
  130. // 注册中断
  131. irq_register(34, &entry, &HPET_measure_handler, 0, &HPET_intr_controller, "HPET0 measure");
  132. // 设置div16
  133. apic_timer_stop();
  134. apic_timer_set_div(APIC_TIMER_DIVISOR);
  135. // 设置初始计数
  136. apic_timer_set_init_cnt(0xFFFFFFFF);
  137. // 启动apic定时器
  138. apic_timer_set_LVT(151, 0, APIC_LVT_Timer_One_Shot);
  139. *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位,开始计时
  140. // 顺便测定tsc频率
  141. test_tsc_start = rdtsc();
  142. io_mfence();
  143. while (measure_apic_timer_flag == false)
  144. ;
  145. kdebug("wait done");
  146. irq_unregister(34);
  147. *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 0; // 停用HPET定时器
  148. io_mfence();
  149. kinfo("Local APIC timer's freq: %d ticks/ms.", apic_timer_ticks_result);
  150. // 计算tsc频率
  151. Cpu_tsc_freq = (test_tsc_end - test_tsc_start) * (1000UL / interval);
  152. kinfo("TSC frequency: %ldMHz", Cpu_tsc_freq / 1000000);
  153. }
  154. /**
  155. * @brief 启用HPET周期中断(5ms)
  156. *
  157. */
  158. void HPET_enable()
  159. {
  160. struct apic_IO_APIC_RTE_entry entry;
  161. // 使用I/O APIC 的IRQ2接收hpet定时器0的中断
  162. apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
  163. // 计算HPET0间隔多少个时钟周期触发一次中断
  164. uint64_t clks_to_intr = 0.000001 * HPET0_INTERVAL * HPET_freq;
  165. // kdebug("clks_to_intr=%#ld", clks_to_intr);
  166. if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
  167. {
  168. kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
  169. while (1)
  170. hlt();
  171. }
  172. // kdebug("[HPET0] conf register=%#018lx conf register[63:32]=%#06lx", (*(uint64_t *)(HPET_REG_BASE + TIM0_CONF)), ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))>>32)&0xffffffff);
  173. *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
  174. io_mfence();
  175. *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时,边沿触发,默认投递到IO APIC的2号引脚(看conf寄存器的高32bit,哪一位被置1,则可以投递到哪一个I/O apic引脚)
  176. io_mfence();
  177. *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr;
  178. io_mfence();
  179. // kdebug("[HPET0] conf register after modify=%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))));
  180. // kdebug("[HPET1] conf register =%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM1_CONF))));
  181. rtc_get_cmos_time(&rtc_now);
  182. kinfo("HPET0 enabled.");
  183. *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
  184. io_mfence();
  185. // 注册中断
  186. irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
  187. }
  188. int HPET_init()
  189. {
  190. kinfo("Initializing HPET...");
  191. // 从acpi获取hpet结构体
  192. ul hpet_table_addr = 0;
  193. acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
  194. // ACPI表没有HPET,尝试读HPTC
  195. if (hpet_table_addr == 0)
  196. {
  197. kwarn("ACPI: HPET Table Not Found On This Computer!");
  198. if (RCBA_vaddr != 0)
  199. {
  200. kerror("NO HPET found on this computer!");
  201. uint32_t *hptc = (uint32_t *)(RCBA_vaddr + 0x3404UL);
  202. // enable HPET
  203. io_mfence();
  204. // 读取HPET配置寄存器地址
  205. switch ((*hptc) & 0x3)
  206. {
  207. case 0:
  208. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
  209. break;
  210. case 1:
  211. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed01000;
  212. break;
  213. case 2:
  214. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed02000;
  215. break;
  216. case 3:
  217. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed03000;
  218. break;
  219. default:
  220. break;
  221. }
  222. // enable HPET
  223. *hptc = 0x80;
  224. io_mfence();
  225. }
  226. else
  227. {
  228. // 没有RCBA寄存器,采用默认值
  229. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
  230. kwarn("There is no RCBA register on this computer, and HPET regs base use default value.");
  231. }
  232. }
  233. else // ACPI表中有HPET表
  234. {
  235. hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr;
  236. // kdebug("hpet_table_addr=%#018lx", hpet_table_addr);
  237. // 由于这段内存与io/apic的映射在同一物理页内,因此不需要重复映射
  238. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address;
  239. }
  240. // 读取计时精度并计算频率
  241. uint64_t tmp;
  242. tmp = *(uint64_t *)(HPET_REG_BASE + GCAP_ID);
  243. HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff;
  244. HPET_freq = 1.0 * 1e15 / HPET_COUNTER_CLK_PERIOD;
  245. HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
  246. kinfo("Total HPET timers: %d", HPET_NUM_TIM_CAP);
  247. kinfo("HPET driver Initialized.");
  248. // kinfo("HPET CLK_PERIOD=%#03lx Frequency=%f", HPET_COUNTER_CLK_PERIOD, (double)HPET_freq);
  249. // kdebug("HPET_freq=%ld", (long)HPET_freq);
  250. // kdebug("HPET_freq=%lf", HPET_freq);
  251. }
  252. #pragma GCC pop_options