late_time_init

在timer_init()中,我们将late_time_init初始化为x86_late_time_init():


[cpp]  view plain  copy
  1. static __init void x86_late_time_init(void)  
  2. {  
  3.     x86_init.timers.timer_init(); //最终调用hpet_time_init  
  4.     tsc_init();  
  5. }  

[cpp]  view plain  copy
  1. /* Default timer init function */  
  2. void __init hpet_time_init(void)  
  3. {  
  4.     if (!hpet_enable())         // 尝试设置高精度事件定时器(HPET)  
  5.         setup_pit_timer();  //如果HPET不能使用,则设置可编程间隔定时器  
  6.     setup_default_timer_irq();  
  7. }  

我们来看hpet_enable(),它检测HPET是否可用,如果可用则将时钟源设置为HPET:

[cpp]  view plain  copy
  1. /** 
  2.  * hpet_enable - Try to setup the HPET timer. Returns 1 on success. 
  3.  */  
  4. int __init hpet_enable(void)  
  5. {  
  6.     unsigned long hpet_period;  
  7.     unsigned int id;  
  8.     u64 freq;  
  9.     int i;  
  10.   
  11.     if (!is_hpet_capable()) //HPET是否可用  
  12.         return 0;         
  13.   
  14.     hpet_set_mapping();     //HPET有自己的内存映射空间  
  15.   
  16.     /* 
  17.      * Read the period and check for a sane value: 
  18.      */  
  19.     hpet_period = hpet_readl(HPET_PERIOD); //从内存中读取HPET_PERIOD  
  20.   
  21.     /* 
  22.      * AMD SB700 based systems with spread spectrum enabled use a 
  23.      * SMM based HPET emulation to provide proper frequency 
  24.      * setting. The SMM code is initialized with the first HPET 
  25.      * register access and takes some time to complete. During 
  26.      * this time the config register reads 0xffffffff. We check 
  27.      * for max. 1000 loops whether the config register reads a non 
  28.      * 0xffffffff value to make sure that HPET is up and running 
  29.      * before we go further. A counting loop is safe, as the HPET 
  30.      * access takes thousands of CPU cycles. On non SB700 based 
  31.      * machines this check is only done once and has no side 
  32.      * effects. 
  33.      */  
  34.     for (i = 0; hpet_readl(HPET_CFG) == 0xFFFFFFFF; i++) {  
  35.         if (i == 1000) {  
  36.             printk(KERN_WARNING  
  37.                    "HPET config register value = 0xFFFFFFFF. "  
  38.                    "Disabling HPET\n");  
  39.             goto out_nohpet;  
  40.         }  
  41.     }  
  42.   
  43.     if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD)   
  44.         goto out_nohpet;  
  45.   
  46.     /* 
  47.      * The period is a femto seconds value. Convert it to a 
  48.      * frequency. 
  49.      */  
  50.     freq = FSEC_PER_SEC;  
  51.     do_div(freq, hpet_period);  
  52.     hpet_freq = freq;  
  53.   
  54.     /* 
  55.      * Read the HPET ID register to retrieve the IRQ routing 
  56.      * information and the number of channels 
  57.      */  
  58.     id = hpet_readl(HPET_ID);  
  59.     hpet_print_config();  
  60.   
  61. #ifdef CONFIG_HPET_EMULATE_RTC  
  62.     /* 
  63.      * The legacy routing mode needs at least two channels, tick timer 
  64.      * and the rtc emulation channel. 
  65.      */  
  66.     if (!(id & HPET_ID_NUMBER))  
  67.         goto out_nohpet;  
  68. #endif  
  69.   
  70.     if (hpet_clocksource_register())  //注册HPET时钟源  
  71.         goto out_nohpet;  
  72.   
  73.     if (id & HPET_ID_LEGSUP) {  
  74.         hpet_legacy_clockevent_register(); //注册HPET时钟事件源设备(源)。  
  75.         return 1;  
  76.     }  
  77.     return 0;  
  78.   
  79. out_nohpet:  
  80.     hpet_clear_mapping();  
  81.     hpet_address = 0;  
  82.     return 0;  
  83. }  

整个函数中最重要的两个操作:

(1)hpet_clocksource_register(),注册HPET时钟源

[cpp]  view plain  copy
  1. static int hpet_clocksource_register(void)  
  2. {  
  3.     u64 start, now;  
  4.     cycle_t t1;  
  5.   
  6.     /* Start the counter */  
  7.     hpet_restart_counter();  
  8.   
  9.     /* Verify whether hpet counter works */  
  10.     t1 = hpet_readl(HPET_COUNTER);  
  11.     rdtscll(start);  
  12.   
  13.     /* 
  14.      * We don't know the TSC frequency yet, but waiting for 
  15.      * 200000 TSC cycles is safe: 
  16.      * 4 GHz == 50us 
  17.      * 1 GHz == 200us 
  18.      */  
  19.     do {  
  20.         rep_nop();  
  21.         rdtscll(now);  
  22.     } while ((now - start) < 200000UL);  
  23.   
  24.     if (t1 == hpet_readl(HPET_COUNTER)) {  
  25.         printk(KERN_WARNING  
  26.                "HPET counter not counting. HPET disabled\n");  
  27.         return -ENODEV;  
  28.     }  
  29.   
  30.     clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq);  
  31.     return 0;  
  32. }  


[cpp]  view plain  copy
  1. static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)  
  2. {  
  3.     return __clocksource_register_scale(cs, 1, hz);  
  4. }  


[cpp]  view plain  copy
  1. /** 
  2.  * __clocksource_register_scale - Used to install new clocksources 
  3.  * @t:      clocksource to be registered 
  4.  * @scale:  Scale factor multiplied against freq to get clocksource hz 
  5.  * @freq:   clocksource frequency (cycles per second) divided by scale 
  6.  * 
  7.  * Returns -EBUSY if registration fails, zero otherwise. 
  8.  * 
  9.  * This *SHOULD NOT* be called directly! Please use the 
  10.  * clocksource_register_hz() or clocksource_register_khz helper functions. 
  11.  */  
  12. int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)  
  13. {  
  14.   
  15.     /* Initialize mult/shift and max_idle_ns */  
  16.     __clocksource_updatefreq_scale(cs, scale, freq);  
  17.   
  18.     /* Add clocksource to the clcoksource list */  
  19.     mutex_lock(&clocksource_mutex);  
  20.     clocksource_enqueue(cs);                //将hpet时钟源插入到clocksource_list中  
  21.     clocksource_enqueue_watchdog(cs);       //看门狗???  
  22.     clocksource_select();                   //重新选择系统时钟源  
  23.     mutex_unlock(&clocksource_mutex);  
  24.     return 0;  
  25. }  

在注册了HPET后,系统会选择HPET作为系统的时钟源:

[cpp]  view plain  copy
  1. /** 
  2.  * clocksource_select - Select the best clocksource available 
  3.  * 
  4.  * Private function. Must hold clocksource_mutex when called. 
  5.  * 
  6.  * Select the clocksource with the best rating, or the clocksource, 
  7.  * which is selected by userspace override. 
  8.  */  
  9. static void clocksource_select(void)  
  10. {  
  11.     struct clocksource *best, *cs;  
  12.   
  13.     if (!finished_booting || list_empty(&clocksource_list))  
  14.         return;  
  15.     /* First clocksource on the list has the best rating. */  
  16.     best = list_first_entry(&clocksource_list, struct clocksource, list);  
  17.     /* Check for the override clocksource. */  
  18.     list_for_each_entry(cs, &clocksource_list, list) {  
  19.         if (strcmp(cs->name, override_name) != 0)  
  20.             continue;  
  21.         /* 
  22.          * Check to make sure we don't switch to a non-highres 
  23.          * capable clocksource if the tick code is in oneshot 
  24.          * mode (highres or nohz) 
  25.          */  
  26.         if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&  
  27.             tick_oneshot_mode_active()) {  
  28.             /* Override clocksource cannot be used. */  
  29.             printk(KERN_WARNING "Override clocksource %s is not "  
  30.                    "HRT compatible. Cannot switch while in "  
  31.                    "HRT/NOHZ mode\n", cs->name);  
  32.             override_name[0] = 0;  
  33.         } else  
  34.             /* Override clocksource can be used. */  
  35.             best = cs;  
  36.         break;  
  37.     }  
  38.     if (curr_clocksource != best) {  
  39.         printk(KERN_INFO "Switching to clocksource %s\n", best->name);  
  40.         curr_clocksource = best;  
  41.         timekeeping_notify(curr_clocksource);  
  42.     }  
  43. }  

(2)在注册并且选定了HPET后,要hpet_legacy_clockevent_register()


[cpp]  view plain  copy
  1. static void hpet_legacy_clockevent_register(void)  
  2. {  
  3.     /* Start HPET legacy interrupts */  
  4.     hpet_enable_legacy_int();  
  5.   
  6.     /* 
  7.      * Start hpet with the boot cpu mask and make it 
  8.      * global after the IO_APIC has been initialized. 
  9.      */  
  10.     hpet_clockevent.cpumask = cpumask_of(smp_processor_id());  
  11.     clockevents_config_and_register(&hpet_clockevent, hpet_freq,  
  12.                     HPET_MIN_PROG_DELTA, 0x7FFFFFFF);  
  13.     global_clock_event = &hpet_clockevent;  
  14.     printk(KERN_DEBUG "hpet clockevent registered\n");  
  15. }  
  16.   
  17.   
  18. static void hpet_enable_legacy_int(void)  
  19. {  
  20.     unsigned int cfg = hpet_readl(HPET_CFG);  //从相关内存区读取HPET的CFG  
  21.   
  22.     cfg |= HPET_CFG_LEGACY;                   //将HPET的CFG设置为系统默认的clockevent_device。  
  23.     hpet_writel(cfg, HPET_CFG);  
  24.     hpet_legacy_int_enabled = 1;  
  25. }  
  26.    

我们来看一下hpet_clockevent:

[cpp]  view plain  copy
  1. /* 
  2.  * The hpet clock event device 
  3.  */  
  4. static struct clock_event_device hpet_clockevent = {  
  5.     .name       = "hpet",  
  6.     .features   = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,  
  7.     .set_mode   = hpet_legacy_set_mode,  
  8.     .set_next_event = hpet_legacy_next_event,  
  9.     .irq        = 0,  
  10.     .rating     = 50,  
  11. };  
继续hpet_legacy_clockevent_register:
[cpp]  view plain  copy
  1. /** 
  2.  * clockevents_config_and_register - Configure and register a clock event device 
  3.  * @dev:    device to register 
  4.  * @freq:   The clock frequency 
  5.  * @min_delta:  The minimum clock ticks to program in oneshot mode 
  6.  * @max_delta:  The maximum clock ticks to program in oneshot mode 
  7.  * 
  8.  * min/max_delta can be 0 for devices which do not support oneshot mode. 
  9.  */  
  10. void clockevents_config_and_register(struct clock_event_device *dev,  
  11.                      u32 freq, unsigned long min_delta,  
  12.                      unsigned long max_delta)  
  13. {  
  14.     dev->min_delta_ticks = min_delta;  
  15.     dev->max_delta_ticks = max_delta;  
  16.     clockevents_config(dev, freq);  
  17.     clockevents_register_device(dev);  
  18. }  
  19.   
  20.   
  21. static void clockevents_config(struct clock_event_device *dev,  
  22.                    u32 freq)  
  23. {  
  24.     u64 sec;  
  25.   
  26.     if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))  
  27.         return;  
  28.   
  29.     /* 
  30.      * Calculate the maximum number of seconds we can sleep. Limit 
  31.      * to 10 minutes for hardware which can program more than 
  32.      * 32bit ticks so we still get reasonable conversion values. 
  33.      */  
  34.     sec = dev->max_delta_ticks;  
  35.     do_div(sec, freq);  
  36.     if (!sec)  
  37.         sec = 1;  
  38.     else if (sec > 600 && dev->max_delta_ticks > UINT_MAX)  
  39.         sec = 600;  
  40.   
  41.     clockevents_calc_mult_shift(dev, freq, sec);  
  42.     dev->min_delta_ns = clockevent_delta2ns(dev->min_delta_ticks, dev);  
  43.     dev->max_delta_ns = clockevent_delta2ns(dev->max_delta_ticks, dev);  
  44. }  
  45.   
  46.   
  47. /** 
  48.  * clockevents_register_device - register a clock event device 
  49.  * @dev:    device to register 
  50.  */  
  51. void clockevents_register_device(struct clock_event_device *dev)  
  52. {  
  53.     unsigned long flags;  
  54.   
  55.     BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);  
  56.     if (!dev->cpumask) {  
  57.         WARN_ON(num_possible_cpus() > 1);  
  58.         dev->cpumask = cpumask_of(smp_processor_id());  
  59.     }  
  60.   
  61.     raw_spin_lock_irqsave(&clockevents_lock, flags);  
  62.   
  63.     list_add(&dev->list, &clockevent_devices);              //将hpet_clockevent挂到clockevent_devices上。  
  64.     clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);  
  65.     clockevents_notify_released();  
  66.   
  67.     raw_spin_unlock_irqrestore(&clockevents_lock, flags);  
  68. }  
  69.   
  70.   
  71.   
  72. /*We have converted clockevent_devices to store all active devices, and 
  73. *clockevents_released to store all fail-to-add/replace-out devices.  
  74. */  
  75.   
  76.   
  77. /* 
  78.  * Called after a notify add to make devices available which were 
  79.  * released from the notifier call. 
  80.  */  
  81. static void clockevents_notify_released(void)  
  82. {  
  83.     struct clock_event_device *dev;  
  84.   
  85.     while (!list_empty(&clockevents_released)) {  
  86.         dev = list_entry(clockevents_released.next,  
  87.                  struct clock_event_device, list);  
  88.         list_del(&dev->list);  
  89.         list_add(&dev->list, &clockevent_devices);  
  90.         clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);  
  91.     }  
  92. }  
  93.   
  94.   
  95.    
这里,我们需要hpet_clockevent挂到clockevent_devices上的方式。我们看到,clockevent_devices只不过是普通的list_head结构,而非想象中的由clockevent_device组成的链表,在想clockevent_devices上添加元素时,只是将该元素的list字段链入到clockevent_devices上即可,这是内核链表结构的精妙之处!


到现在,我们已经将hpet时钟源和时钟事件源的注册工作完成了!


如果HPET是不可用的,那么

[cpp]  view plain  copy
  1. /* 
  2.  * Initialize the conversion factor and the min/max deltas of the clock event 
  3.  * structure and register the clock event source with the framework. 
  4.  */  
  5. void __init setup_pit_timer(void)  
  6. {  
  7.     /* 
  8.      * Start pit with the boot cpu mask and make it global after the 
  9.      * IO_APIC has been initialized. 
  10.      */  
  11.     pit_ce.cpumask = cpumask_of(smp_processor_id());  
  12.   
  13.     clockevents_config_and_register(&pit_ce, CLOCK_TICK_RATE, 0xF, 0x7FFF);  
  14.     global_clock_event = &pit_ce;  
  15. }  
首先来看一下pit_ce:
[cpp]  view plain  copy
  1. static struct clock_event_device pit_ce = {  
  2.     .name       = "pit",  
  3.     .features   = CLOCK_EVT_FEAT_PERIODIC,  
  4.     .set_mode   = pit_set_mode,  
  5.     .set_next_event = pit_set_next_event,  
  6.     .shift      = 32,  
  7. };  

由于PIT是做为默认时钟源的,因此在setup_pit_timer中我们只需要注册将其注册为时钟事件源即可。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值