HPET.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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. static struct acpi_HPET_description_table_t *hpet_table;
  8. static uint64_t HPET_REG_BASE = 0;
  9. static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒)
  10. static double HPET_freq = 0; // 主计时器频率
  11. static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量
  12. extern struct rtc_time_t rtc_now; // 导出全局墙上时钟
  13. enum
  14. {
  15. GCAP_ID = 0x00,
  16. GEN_CONF = 0x10,
  17. GINTR_STA = 0x20,
  18. MAIN_CNT = 0xf0,
  19. TIM0_CONF = 0x100,
  20. TIM0_COMP = 0x108,
  21. TIM1_CONF = 0x120,
  22. TIM1_COMP = 0x128,
  23. TIM2_CONF = 0x140,
  24. TIM2_COMP = 0x148,
  25. TIM3_CONF = 0x160,
  26. TIM3_COMP = 0x168,
  27. TIM4_CONF = 0x180,
  28. TIM4_COMP = 0x188,
  29. TIM5_CONF = 0x1a0,
  30. TIM5_COMP = 0x1a8,
  31. TIM6_CONF = 0x1c0,
  32. TIM6_COMP = 0x1c8,
  33. TIM7_CONF = 0x1e0,
  34. TIM7_COMP = 0x1e8,
  35. };
  36. hardware_intr_controller HPET_intr_controller =
  37. {
  38. .enable = apic_ioapic_enable,
  39. .disable = apic_ioapic_disable,
  40. .install = apic_ioapic_install,
  41. .uninstall = apic_ioapic_uninstall,
  42. .ack = apic_ioapic_edge_ack,
  43. };
  44. void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
  45. {
  46. switch (param)
  47. {
  48. case 0: // 定时器0中断
  49. ++timer_jiffies;
  50. set_softirq_status(TIMER_SIRQ);
  51. break;
  52. default:
  53. kwarn("Unsupported HPET irq: %d.", number);
  54. break;
  55. }
  56. }
  57. int HPET_init()
  58. {
  59. // 从acpi获取hpet结构体
  60. ul hpet_table_addr = 0;
  61. acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
  62. if (hpet_table_addr == 0)
  63. {
  64. kerror("HPET Not Found On This Computer!");
  65. return E_HPET_INIT_FAILED;
  66. }
  67. hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr;
  68. // 由于这段内存与io/apic的映射在同一物理页内,因此不需要重复映射
  69. HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address;
  70. // 读取计时精度并计算频率
  71. uint64_t tmp;
  72. tmp = *(uint64_t *)(HPET_REG_BASE + GCAP_ID);
  73. HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff;
  74. HPET_freq = 1.0 * 1e15 / HPET_COUNTER_CLK_PERIOD;
  75. HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
  76. kinfo("HPET CLK_PERIOD=%#03lx Frequency=%f", HPET_COUNTER_CLK_PERIOD, HPET_freq);
  77. struct apic_IO_APIC_RTE_entry entry;
  78. // 使用I/O APIC 的IRQ2接收hpet定时器0的中断
  79. apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
  80. // 注册中断
  81. irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
  82. *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
  83. io_mfence();
  84. *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时,边沿触发,投递到IO APIC的2号引脚(这里有点绕,写的是8259的引脚号,但是因为禁用了8259,因此会被路由到IO APIC的2号引脚)
  85. io_mfence();
  86. *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = HPET_freq; // 1s触发一次中断
  87. io_mfence();
  88. rtc_get_cmos_time(&rtc_now);
  89. *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
  90. io_mfence();
  91. }