diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/time/clockevents.c | 88 | ||||
-rw-r--r-- | kernel/time/timer_list.c | 32 |
2 files changed, 115 insertions, 5 deletions
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 55449909f114..489642b08d64 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c | |||
@@ -94,6 +94,57 @@ u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt) | |||
94 | } | 94 | } |
95 | EXPORT_SYMBOL_GPL(clockevent_delta2ns); | 95 | EXPORT_SYMBOL_GPL(clockevent_delta2ns); |
96 | 96 | ||
97 | static int __clockevents_set_mode(struct clock_event_device *dev, | ||
98 | enum clock_event_mode mode) | ||
99 | { | ||
100 | /* Transition with legacy set_mode() callback */ | ||
101 | if (dev->set_mode) { | ||
102 | /* Legacy callback doesn't support new modes */ | ||
103 | if (mode > CLOCK_EVT_MODE_RESUME) | ||
104 | return -ENOSYS; | ||
105 | dev->set_mode(mode, dev); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | if (dev->features & CLOCK_EVT_FEAT_DUMMY) | ||
110 | return 0; | ||
111 | |||
112 | /* Transition with new mode-specific callbacks */ | ||
113 | switch (mode) { | ||
114 | case CLOCK_EVT_MODE_UNUSED: | ||
115 | /* | ||
116 | * This is an internal state, which is guaranteed to go from | ||
117 | * SHUTDOWN to UNUSED. No driver interaction required. | ||
118 | */ | ||
119 | return 0; | ||
120 | |||
121 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
122 | return dev->set_mode_shutdown(dev); | ||
123 | |||
124 | case CLOCK_EVT_MODE_PERIODIC: | ||
125 | /* Core internal bug */ | ||
126 | if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC)) | ||
127 | return -ENOSYS; | ||
128 | return dev->set_mode_periodic(dev); | ||
129 | |||
130 | case CLOCK_EVT_MODE_ONESHOT: | ||
131 | /* Core internal bug */ | ||
132 | if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT)) | ||
133 | return -ENOSYS; | ||
134 | return dev->set_mode_oneshot(dev); | ||
135 | |||
136 | case CLOCK_EVT_MODE_RESUME: | ||
137 | /* Optional callback */ | ||
138 | if (dev->set_mode_resume) | ||
139 | return dev->set_mode_resume(dev); | ||
140 | else | ||
141 | return 0; | ||
142 | |||
143 | default: | ||
144 | return -ENOSYS; | ||
145 | } | ||
146 | } | ||
147 | |||
97 | /** | 148 | /** |
98 | * clockevents_set_mode - set the operating mode of a clock event device | 149 | * clockevents_set_mode - set the operating mode of a clock event device |
99 | * @dev: device to modify | 150 | * @dev: device to modify |
@@ -105,7 +156,9 @@ void clockevents_set_mode(struct clock_event_device *dev, | |||
105 | enum clock_event_mode mode) | 156 | enum clock_event_mode mode) |
106 | { | 157 | { |
107 | if (dev->mode != mode) { | 158 | if (dev->mode != mode) { |
108 | dev->set_mode(mode, dev); | 159 | if (__clockevents_set_mode(dev, mode)) |
160 | return; | ||
161 | |||
109 | dev->mode = mode; | 162 | dev->mode = mode; |
110 | 163 | ||
111 | /* | 164 | /* |
@@ -373,6 +426,35 @@ int clockevents_unbind_device(struct clock_event_device *ced, int cpu) | |||
373 | } | 426 | } |
374 | EXPORT_SYMBOL_GPL(clockevents_unbind); | 427 | EXPORT_SYMBOL_GPL(clockevents_unbind); |
375 | 428 | ||
429 | /* Sanity check of mode transition callbacks */ | ||
430 | static int clockevents_sanity_check(struct clock_event_device *dev) | ||
431 | { | ||
432 | /* Legacy set_mode() callback */ | ||
433 | if (dev->set_mode) { | ||
434 | /* We shouldn't be supporting new modes now */ | ||
435 | WARN_ON(dev->set_mode_periodic || dev->set_mode_oneshot || | ||
436 | dev->set_mode_shutdown || dev->set_mode_resume); | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | if (dev->features & CLOCK_EVT_FEAT_DUMMY) | ||
441 | return 0; | ||
442 | |||
443 | /* New mode-specific callbacks */ | ||
444 | if (!dev->set_mode_shutdown) | ||
445 | return -EINVAL; | ||
446 | |||
447 | if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) && | ||
448 | !dev->set_mode_periodic) | ||
449 | return -EINVAL; | ||
450 | |||
451 | if ((dev->features & CLOCK_EVT_FEAT_ONESHOT) && | ||
452 | !dev->set_mode_oneshot) | ||
453 | return -EINVAL; | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
376 | /** | 458 | /** |
377 | * clockevents_register_device - register a clock event device | 459 | * clockevents_register_device - register a clock event device |
378 | * @dev: device to register | 460 | * @dev: device to register |
@@ -382,6 +464,8 @@ void clockevents_register_device(struct clock_event_device *dev) | |||
382 | unsigned long flags; | 464 | unsigned long flags; |
383 | 465 | ||
384 | BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED); | 466 | BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED); |
467 | BUG_ON(clockevents_sanity_check(dev)); | ||
468 | |||
385 | if (!dev->cpumask) { | 469 | if (!dev->cpumask) { |
386 | WARN_ON(num_possible_cpus() > 1); | 470 | WARN_ON(num_possible_cpus() > 1); |
387 | dev->cpumask = cpumask_of(smp_processor_id()); | 471 | dev->cpumask = cpumask_of(smp_processor_id()); |
@@ -449,7 +533,7 @@ int __clockevents_update_freq(struct clock_event_device *dev, u32 freq) | |||
449 | return clockevents_program_event(dev, dev->next_event, false); | 533 | return clockevents_program_event(dev, dev->next_event, false); |
450 | 534 | ||
451 | if (dev->mode == CLOCK_EVT_MODE_PERIODIC) | 535 | if (dev->mode == CLOCK_EVT_MODE_PERIODIC) |
452 | dev->set_mode(CLOCK_EVT_MODE_PERIODIC, dev); | 536 | return __clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC); |
453 | 537 | ||
454 | return 0; | 538 | return 0; |
455 | } | 539 | } |
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index 61ed862cdd37..2cfd19485824 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c | |||
@@ -228,9 +228,35 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu) | |||
228 | print_name_offset(m, dev->set_next_event); | 228 | print_name_offset(m, dev->set_next_event); |
229 | SEQ_printf(m, "\n"); | 229 | SEQ_printf(m, "\n"); |
230 | 230 | ||
231 | SEQ_printf(m, " set_mode: "); | 231 | if (dev->set_mode) { |
232 | print_name_offset(m, dev->set_mode); | 232 | SEQ_printf(m, " set_mode: "); |
233 | SEQ_printf(m, "\n"); | 233 | print_name_offset(m, dev->set_mode); |
234 | SEQ_printf(m, "\n"); | ||
235 | } else { | ||
236 | if (dev->set_mode_shutdown) { | ||
237 | SEQ_printf(m, " shutdown: "); | ||
238 | print_name_offset(m, dev->set_mode_shutdown); | ||
239 | SEQ_printf(m, "\n"); | ||
240 | } | ||
241 | |||
242 | if (dev->set_mode_periodic) { | ||
243 | SEQ_printf(m, " periodic: "); | ||
244 | print_name_offset(m, dev->set_mode_periodic); | ||
245 | SEQ_printf(m, "\n"); | ||
246 | } | ||
247 | |||
248 | if (dev->set_mode_oneshot) { | ||
249 | SEQ_printf(m, " oneshot: "); | ||
250 | print_name_offset(m, dev->set_mode_oneshot); | ||
251 | SEQ_printf(m, "\n"); | ||
252 | } | ||
253 | |||
254 | if (dev->set_mode_resume) { | ||
255 | SEQ_printf(m, " resume: "); | ||
256 | print_name_offset(m, dev->set_mode_resume); | ||
257 | SEQ_printf(m, "\n"); | ||
258 | } | ||
259 | } | ||
234 | 260 | ||
235 | SEQ_printf(m, " event_handler: "); | 261 | SEQ_printf(m, " event_handler: "); |
236 | print_name_offset(m, dev->event_handler); | 262 | print_name_offset(m, dev->event_handler); |