aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2015-02-12 19:54:56 -0500
committerIngo Molnar <mingo@kernel.org>2015-02-18 09:16:23 -0500
commitbd624d75db21ea5402f9ecf4450b311794d80352 (patch)
treeff4343a02e3b46d633b923d68e35766c716c4793
parentf40d149b58f5dea148ceaee5f9249da133e5004c (diff)
clockevents: Introduce mode specific callbacks
It is not possible for the clockevents core to know which modes (other than those with a corresponding feature flag) are supported by a particular implementation. And drivers are expected to handle transition to all modes elegantly, as ->set_mode() would be issued for them unconditionally. Now, adding support for a new mode complicates things a bit if we want to use the legacy ->set_mode() callback. We need to closely review all clockevents drivers to see if they would break on addition of a new mode. And after such reviews, it is found that we have to do non-trivial changes to most of the drivers [1]. Introduce mode-specific set_mode_*() callbacks, some of which the drivers may or may not implement. A missing callback would clearly convey the message that the corresponding mode isn't supported. A driver may still choose to keep supporting the legacy ->set_mode() callback, but ->set_mode() wouldn't be supporting any new modes beyond RESUME. If a driver wants to benefit from using a new mode, it would be required to migrate to the mode specific callbacks. The legacy ->set_mode() callback and the newly introduced mode-specific callbacks are mutually exclusive. Only one of them should be supported by the driver. Sanity check is done at the time of registration to distinguish between optional and required callbacks and to make error recovery and handling simpler. If the legacy ->set_mode() callback is provided, all mode specific ones would be ignored by the core but a warning is thrown if they are present. Call sites calling ->set_mode() directly are also updated to use __clockevents_set_mode() instead, as ->set_mode() may not be available anymore for few drivers. [1] https://lkml.org/lkml/2014/12/9/605 [2] https://lkml.org/lkml/2015/1/23/255 Suggested-by: Thomas Gleixner <tglx@linutronix.de> [2] Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Daniel Lezcano <daniel.lezcano@linaro.org> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: John Stultz <john.stultz@linaro.org> Cc: Kevin Hilman <khilman@linaro.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Preeti U Murthy <preeti@linux.vnet.ibm.com> Cc: linaro-kernel@lists.linaro.org Cc: linaro-networking@linaro.org Link: http://lkml.kernel.org/r/792d59a40423f0acffc9bb0bec9de1341a06fa02.1423788565.git.viresh.kumar@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--include/linux/clockchips.h21
-rw-r--r--kernel/time/clockevents.c88
-rw-r--r--kernel/time/timer_list.c32
3 files changed, 134 insertions, 7 deletions
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h
index 2e4cb67f6e56..59af26b54d15 100644
--- a/include/linux/clockchips.h
+++ b/include/linux/clockchips.h
@@ -39,6 +39,8 @@ enum clock_event_mode {
39 CLOCK_EVT_MODE_PERIODIC, 39 CLOCK_EVT_MODE_PERIODIC,
40 CLOCK_EVT_MODE_ONESHOT, 40 CLOCK_EVT_MODE_ONESHOT,
41 CLOCK_EVT_MODE_RESUME, 41 CLOCK_EVT_MODE_RESUME,
42
43 /* Legacy ->set_mode() callback doesn't support below modes */
42}; 44};
43 45
44/* 46/*
@@ -81,7 +83,11 @@ enum clock_event_mode {
81 * @mode: operating mode assigned by the management code 83 * @mode: operating mode assigned by the management code
82 * @features: features 84 * @features: features
83 * @retries: number of forced programming retries 85 * @retries: number of forced programming retries
84 * @set_mode: set mode function 86 * @set_mode: legacy set mode function, only for modes <= CLOCK_EVT_MODE_RESUME.
87 * @set_mode_periodic: switch mode to periodic, if !set_mode
88 * @set_mode_oneshot: switch mode to oneshot, if !set_mode
89 * @set_mode_shutdown: switch mode to shutdown, if !set_mode
90 * @set_mode_resume: resume clkevt device, if !set_mode
85 * @broadcast: function to broadcast events 91 * @broadcast: function to broadcast events
86 * @min_delta_ticks: minimum delta value in ticks stored for reconfiguration 92 * @min_delta_ticks: minimum delta value in ticks stored for reconfiguration
87 * @max_delta_ticks: maximum delta value in ticks stored for reconfiguration 93 * @max_delta_ticks: maximum delta value in ticks stored for reconfiguration
@@ -108,9 +114,20 @@ struct clock_event_device {
108 unsigned int features; 114 unsigned int features;
109 unsigned long retries; 115 unsigned long retries;
110 116
111 void (*broadcast)(const struct cpumask *mask); 117 /*
118 * Mode transition callback(s): Only one of the two groups should be
119 * defined:
120 * - set_mode(), only for modes <= CLOCK_EVT_MODE_RESUME.
121 * - set_mode_{shutdown|periodic|oneshot|resume}().
122 */
112 void (*set_mode)(enum clock_event_mode mode, 123 void (*set_mode)(enum clock_event_mode mode,
113 struct clock_event_device *); 124 struct clock_event_device *);
125 int (*set_mode_periodic)(struct clock_event_device *);
126 int (*set_mode_oneshot)(struct clock_event_device *);
127 int (*set_mode_shutdown)(struct clock_event_device *);
128 int (*set_mode_resume)(struct clock_event_device *);
129
130 void (*broadcast)(const struct cpumask *mask);
114 void (*suspend)(struct clock_event_device *); 131 void (*suspend)(struct clock_event_device *);
115 void (*resume)(struct clock_event_device *); 132 void (*resume)(struct clock_event_device *);
116 unsigned long min_delta_ticks; 133 unsigned long min_delta_ticks;
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}
95EXPORT_SYMBOL_GPL(clockevent_delta2ns); 95EXPORT_SYMBOL_GPL(clockevent_delta2ns);
96 96
97static 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}
374EXPORT_SYMBOL_GPL(clockevents_unbind); 427EXPORT_SYMBOL_GPL(clockevents_unbind);
375 428
429/* Sanity check of mode transition callbacks */
430static 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);