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