diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 80 |
1 files changed, 64 insertions, 16 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index a00545fe5cdd..648b3a2a3a44 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -80,6 +80,7 @@ static inline void hpet_clear_mapping(void) | |||
80 | */ | 80 | */ |
81 | static int boot_hpet_disable; | 81 | static int boot_hpet_disable; |
82 | int hpet_force_user; | 82 | int hpet_force_user; |
83 | static int hpet_verbose; | ||
83 | 84 | ||
84 | static int __init hpet_setup(char *str) | 85 | static int __init hpet_setup(char *str) |
85 | { | 86 | { |
@@ -88,6 +89,8 @@ static int __init hpet_setup(char *str) | |||
88 | boot_hpet_disable = 1; | 89 | boot_hpet_disable = 1; |
89 | if (!strncmp("force", str, 5)) | 90 | if (!strncmp("force", str, 5)) |
90 | hpet_force_user = 1; | 91 | hpet_force_user = 1; |
92 | if (!strncmp("verbose", str, 7)) | ||
93 | hpet_verbose = 1; | ||
91 | } | 94 | } |
92 | return 1; | 95 | return 1; |
93 | } | 96 | } |
@@ -119,6 +122,43 @@ int is_hpet_enabled(void) | |||
119 | } | 122 | } |
120 | EXPORT_SYMBOL_GPL(is_hpet_enabled); | 123 | EXPORT_SYMBOL_GPL(is_hpet_enabled); |
121 | 124 | ||
125 | static void _hpet_print_config(const char *function, int line) | ||
126 | { | ||
127 | u32 i, timers, l, h; | ||
128 | printk(KERN_INFO "hpet: %s(%d):\n", function, line); | ||
129 | l = hpet_readl(HPET_ID); | ||
130 | h = hpet_readl(HPET_PERIOD); | ||
131 | timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; | ||
132 | printk(KERN_INFO "hpet: ID: 0x%x, PERIOD: 0x%x\n", l, h); | ||
133 | l = hpet_readl(HPET_CFG); | ||
134 | h = hpet_readl(HPET_STATUS); | ||
135 | printk(KERN_INFO "hpet: CFG: 0x%x, STATUS: 0x%x\n", l, h); | ||
136 | l = hpet_readl(HPET_COUNTER); | ||
137 | h = hpet_readl(HPET_COUNTER+4); | ||
138 | printk(KERN_INFO "hpet: COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); | ||
139 | |||
140 | for (i = 0; i < timers; i++) { | ||
141 | l = hpet_readl(HPET_Tn_CFG(i)); | ||
142 | h = hpet_readl(HPET_Tn_CFG(i)+4); | ||
143 | printk(KERN_INFO "hpet: T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", | ||
144 | i, l, h); | ||
145 | l = hpet_readl(HPET_Tn_CMP(i)); | ||
146 | h = hpet_readl(HPET_Tn_CMP(i)+4); | ||
147 | printk(KERN_INFO "hpet: T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", | ||
148 | i, l, h); | ||
149 | l = hpet_readl(HPET_Tn_ROUTE(i)); | ||
150 | h = hpet_readl(HPET_Tn_ROUTE(i)+4); | ||
151 | printk(KERN_INFO "hpet: T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", | ||
152 | i, l, h); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | #define hpet_print_config() \ | ||
157 | do { \ | ||
158 | if (hpet_verbose) \ | ||
159 | _hpet_print_config(__FUNCTION__, __LINE__); \ | ||
160 | } while (0) | ||
161 | |||
122 | /* | 162 | /* |
123 | * When the hpet driver (/dev/hpet) is enabled, we need to reserve | 163 | * When the hpet driver (/dev/hpet) is enabled, we need to reserve |
124 | * timer 0 and timer 1 in case of RTC emulation. | 164 | * timer 0 and timer 1 in case of RTC emulation. |
@@ -191,27 +231,37 @@ static struct clock_event_device hpet_clockevent = { | |||
191 | .rating = 50, | 231 | .rating = 50, |
192 | }; | 232 | }; |
193 | 233 | ||
194 | static void hpet_start_counter(void) | 234 | static void hpet_stop_counter(void) |
195 | { | 235 | { |
196 | unsigned long cfg = hpet_readl(HPET_CFG); | 236 | unsigned long cfg = hpet_readl(HPET_CFG); |
197 | |||
198 | cfg &= ~HPET_CFG_ENABLE; | 237 | cfg &= ~HPET_CFG_ENABLE; |
199 | hpet_writel(cfg, HPET_CFG); | 238 | hpet_writel(cfg, HPET_CFG); |
200 | hpet_writel(0, HPET_COUNTER); | 239 | hpet_writel(0, HPET_COUNTER); |
201 | hpet_writel(0, HPET_COUNTER + 4); | 240 | hpet_writel(0, HPET_COUNTER + 4); |
241 | } | ||
242 | |||
243 | static void hpet_start_counter(void) | ||
244 | { | ||
245 | unsigned long cfg = hpet_readl(HPET_CFG); | ||
202 | cfg |= HPET_CFG_ENABLE; | 246 | cfg |= HPET_CFG_ENABLE; |
203 | hpet_writel(cfg, HPET_CFG); | 247 | hpet_writel(cfg, HPET_CFG); |
204 | } | 248 | } |
205 | 249 | ||
250 | static void hpet_restart_counter(void) | ||
251 | { | ||
252 | hpet_stop_counter(); | ||
253 | hpet_start_counter(); | ||
254 | } | ||
255 | |||
206 | static void hpet_resume_device(void) | 256 | static void hpet_resume_device(void) |
207 | { | 257 | { |
208 | force_hpet_resume(); | 258 | force_hpet_resume(); |
209 | } | 259 | } |
210 | 260 | ||
211 | static void hpet_restart_counter(void) | 261 | static void hpet_resume_counter(void) |
212 | { | 262 | { |
213 | hpet_resume_device(); | 263 | hpet_resume_device(); |
214 | hpet_start_counter(); | 264 | hpet_restart_counter(); |
215 | } | 265 | } |
216 | 266 | ||
217 | static void hpet_enable_legacy_int(void) | 267 | static void hpet_enable_legacy_int(void) |
@@ -259,29 +309,23 @@ static int hpet_setup_msi_irq(unsigned int irq); | |||
259 | static void hpet_set_mode(enum clock_event_mode mode, | 309 | static void hpet_set_mode(enum clock_event_mode mode, |
260 | struct clock_event_device *evt, int timer) | 310 | struct clock_event_device *evt, int timer) |
261 | { | 311 | { |
262 | unsigned long cfg, cmp, now; | 312 | unsigned long cfg; |
263 | uint64_t delta; | 313 | uint64_t delta; |
264 | 314 | ||
265 | switch (mode) { | 315 | switch (mode) { |
266 | case CLOCK_EVT_MODE_PERIODIC: | 316 | case CLOCK_EVT_MODE_PERIODIC: |
317 | hpet_stop_counter(); | ||
267 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; | 318 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; |
268 | delta >>= evt->shift; | 319 | delta >>= evt->shift; |
269 | now = hpet_readl(HPET_COUNTER); | ||
270 | cmp = now + (unsigned long) delta; | ||
271 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 320 | cfg = hpet_readl(HPET_Tn_CFG(timer)); |
272 | /* Make sure we use edge triggered interrupts */ | 321 | /* Make sure we use edge triggered interrupts */ |
273 | cfg &= ~HPET_TN_LEVEL; | 322 | cfg &= ~HPET_TN_LEVEL; |
274 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | | 323 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | |
275 | HPET_TN_SETVAL | HPET_TN_32BIT; | 324 | HPET_TN_SETVAL | HPET_TN_32BIT; |
276 | hpet_writel(cfg, HPET_Tn_CFG(timer)); | 325 | hpet_writel(cfg, HPET_Tn_CFG(timer)); |
277 | /* | ||
278 | * The first write after writing TN_SETVAL to the | ||
279 | * config register sets the counter value, the second | ||
280 | * write sets the period. | ||
281 | */ | ||
282 | hpet_writel(cmp, HPET_Tn_CMP(timer)); | ||
283 | udelay(1); | ||
284 | hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); | 326 | hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); |
327 | hpet_start_counter(); | ||
328 | hpet_print_config(); | ||
285 | break; | 329 | break; |
286 | 330 | ||
287 | case CLOCK_EVT_MODE_ONESHOT: | 331 | case CLOCK_EVT_MODE_ONESHOT: |
@@ -308,6 +352,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
308 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); | 352 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); |
309 | enable_irq(hdev->irq); | 353 | enable_irq(hdev->irq); |
310 | } | 354 | } |
355 | hpet_print_config(); | ||
311 | break; | 356 | break; |
312 | } | 357 | } |
313 | } | 358 | } |
@@ -526,6 +571,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
526 | 571 | ||
527 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); | 572 | num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); |
528 | num_timers++; /* Value read out starts from 0 */ | 573 | num_timers++; /* Value read out starts from 0 */ |
574 | hpet_print_config(); | ||
529 | 575 | ||
530 | hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); | 576 | hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); |
531 | if (!hpet_devs) | 577 | if (!hpet_devs) |
@@ -695,7 +741,7 @@ static struct clocksource clocksource_hpet = { | |||
695 | .mask = HPET_MASK, | 741 | .mask = HPET_MASK, |
696 | .shift = HPET_SHIFT, | 742 | .shift = HPET_SHIFT, |
697 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 743 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
698 | .resume = hpet_restart_counter, | 744 | .resume = hpet_resume_counter, |
699 | #ifdef CONFIG_X86_64 | 745 | #ifdef CONFIG_X86_64 |
700 | .vread = vread_hpet, | 746 | .vread = vread_hpet, |
701 | #endif | 747 | #endif |
@@ -707,7 +753,7 @@ static int hpet_clocksource_register(void) | |||
707 | cycle_t t1; | 753 | cycle_t t1; |
708 | 754 | ||
709 | /* Start the counter */ | 755 | /* Start the counter */ |
710 | hpet_start_counter(); | 756 | hpet_restart_counter(); |
711 | 757 | ||
712 | /* Verify whether hpet counter works */ | 758 | /* Verify whether hpet counter works */ |
713 | t1 = read_hpet(); | 759 | t1 = read_hpet(); |
@@ -793,6 +839,7 @@ int __init hpet_enable(void) | |||
793 | * information and the number of channels | 839 | * information and the number of channels |
794 | */ | 840 | */ |
795 | id = hpet_readl(HPET_ID); | 841 | id = hpet_readl(HPET_ID); |
842 | hpet_print_config(); | ||
796 | 843 | ||
797 | #ifdef CONFIG_HPET_EMULATE_RTC | 844 | #ifdef CONFIG_HPET_EMULATE_RTC |
798 | /* | 845 | /* |
@@ -845,6 +892,7 @@ static __init int hpet_late_init(void) | |||
845 | return -ENODEV; | 892 | return -ENODEV; |
846 | 893 | ||
847 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); | 894 | hpet_reserve_platform_timers(hpet_readl(HPET_ID)); |
895 | hpet_print_config(); | ||
848 | 896 | ||
849 | for_each_online_cpu(cpu) { | 897 | for_each_online_cpu(cpu) { |
850 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); | 898 | hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); |