Merge tag 'v4.0-rc2' into timers/core, to refresh the tree before pulling more changes
authorIngo Molnar <mingo@kernel.org>
Wed, 4 Mar 2015 19:00:05 +0000 (20:00 +0100)
committerIngo Molnar <mingo@kernel.org>
Wed, 4 Mar 2015 19:00:05 +0000 (20:00 +0100)
include/linux/clockchips.h
kernel/time/clockevents.c
kernel/time/timer_list.c

index 2e4cb67f6e560094aa719fe75f595dfbb562cf8e..59af26b54d15e8dfb210addbb1a74fa954c7b97d 100644 (file)
@@ -39,6 +39,8 @@ enum clock_event_mode {
        CLOCK_EVT_MODE_PERIODIC,
        CLOCK_EVT_MODE_ONESHOT,
        CLOCK_EVT_MODE_RESUME,
+
+       /* Legacy ->set_mode() callback doesn't support below modes */
 };
 
 /*
@@ -81,7 +83,11 @@ enum clock_event_mode {
  * @mode:              operating mode assigned by the management code
  * @features:          features
  * @retries:           number of forced programming retries
- * @set_mode:          set mode function
+ * @set_mode:          legacy set mode function, only for modes <= CLOCK_EVT_MODE_RESUME.
+ * @set_mode_periodic: switch mode to periodic, if !set_mode
+ * @set_mode_oneshot:  switch mode to oneshot, if !set_mode
+ * @set_mode_shutdown: switch mode to shutdown, if !set_mode
+ * @set_mode_resume:   resume clkevt device, if !set_mode
  * @broadcast:         function to broadcast events
  * @min_delta_ticks:   minimum delta value in ticks stored for reconfiguration
  * @max_delta_ticks:   maximum delta value in ticks stored for reconfiguration
@@ -108,9 +114,20 @@ struct clock_event_device {
        unsigned int            features;
        unsigned long           retries;
 
-       void                    (*broadcast)(const struct cpumask *mask);
+       /*
+        * Mode transition callback(s): Only one of the two groups should be
+        * defined:
+        * - set_mode(), only for modes <= CLOCK_EVT_MODE_RESUME.
+        * - set_mode_{shutdown|periodic|oneshot|resume}().
+        */
        void                    (*set_mode)(enum clock_event_mode mode,
                                            struct clock_event_device *);
+       int                     (*set_mode_periodic)(struct clock_event_device *);
+       int                     (*set_mode_oneshot)(struct clock_event_device *);
+       int                     (*set_mode_shutdown)(struct clock_event_device *);
+       int                     (*set_mode_resume)(struct clock_event_device *);
+
+       void                    (*broadcast)(const struct cpumask *mask);
        void                    (*suspend)(struct clock_event_device *);
        void                    (*resume)(struct clock_event_device *);
        unsigned long           min_delta_ticks;
index 55449909f11475372135ac61b33e65114eb151ba..489642b08d64ca162ba0402fc52213c3cb6acfa7 100644 (file)
@@ -94,6 +94,57 @@ u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
 }
 EXPORT_SYMBOL_GPL(clockevent_delta2ns);
 
+static int __clockevents_set_mode(struct clock_event_device *dev,
+                                 enum clock_event_mode mode)
+{
+       /* Transition with legacy set_mode() callback */
+       if (dev->set_mode) {
+               /* Legacy callback doesn't support new modes */
+               if (mode > CLOCK_EVT_MODE_RESUME)
+                       return -ENOSYS;
+               dev->set_mode(mode, dev);
+               return 0;
+       }
+
+       if (dev->features & CLOCK_EVT_FEAT_DUMMY)
+               return 0;
+
+       /* Transition with new mode-specific callbacks */
+       switch (mode) {
+       case CLOCK_EVT_MODE_UNUSED:
+               /*
+                * This is an internal state, which is guaranteed to go from
+                * SHUTDOWN to UNUSED. No driver interaction required.
+                */
+               return 0;
+
+       case CLOCK_EVT_MODE_SHUTDOWN:
+               return dev->set_mode_shutdown(dev);
+
+       case CLOCK_EVT_MODE_PERIODIC:
+               /* Core internal bug */
+               if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))
+                       return -ENOSYS;
+               return dev->set_mode_periodic(dev);
+
+       case CLOCK_EVT_MODE_ONESHOT:
+               /* Core internal bug */
+               if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
+                       return -ENOSYS;
+               return dev->set_mode_oneshot(dev);
+
+       case CLOCK_EVT_MODE_RESUME:
+               /* Optional callback */
+               if (dev->set_mode_resume)
+                       return dev->set_mode_resume(dev);
+               else
+                       return 0;
+
+       default:
+               return -ENOSYS;
+       }
+}
+
 /**
  * clockevents_set_mode - set the operating mode of a clock event device
  * @dev:       device to modify
@@ -105,7 +156,9 @@ void clockevents_set_mode(struct clock_event_device *dev,
                                 enum clock_event_mode mode)
 {
        if (dev->mode != mode) {
-               dev->set_mode(mode, dev);
+               if (__clockevents_set_mode(dev, mode))
+                       return;
+
                dev->mode = mode;
 
                /*
@@ -373,6 +426,35 @@ int clockevents_unbind_device(struct clock_event_device *ced, int cpu)
 }
 EXPORT_SYMBOL_GPL(clockevents_unbind);
 
+/* Sanity check of mode transition callbacks */
+static int clockevents_sanity_check(struct clock_event_device *dev)
+{
+       /* Legacy set_mode() callback */
+       if (dev->set_mode) {
+               /* We shouldn't be supporting new modes now */
+               WARN_ON(dev->set_mode_periodic || dev->set_mode_oneshot ||
+                       dev->set_mode_shutdown || dev->set_mode_resume);
+               return 0;
+       }
+
+       if (dev->features & CLOCK_EVT_FEAT_DUMMY)
+               return 0;
+
+       /* New mode-specific callbacks */
+       if (!dev->set_mode_shutdown)
+               return -EINVAL;
+
+       if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
+           !dev->set_mode_periodic)
+               return -EINVAL;
+
+       if ((dev->features & CLOCK_EVT_FEAT_ONESHOT) &&
+           !dev->set_mode_oneshot)
+               return -EINVAL;
+
+       return 0;
+}
+
 /**
  * clockevents_register_device - register a clock event device
  * @dev:       device to register
@@ -382,6 +464,8 @@ void clockevents_register_device(struct clock_event_device *dev)
        unsigned long flags;
 
        BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
+       BUG_ON(clockevents_sanity_check(dev));
+
        if (!dev->cpumask) {
                WARN_ON(num_possible_cpus() > 1);
                dev->cpumask = cpumask_of(smp_processor_id());
@@ -449,7 +533,7 @@ int __clockevents_update_freq(struct clock_event_device *dev, u32 freq)
                return clockevents_program_event(dev, dev->next_event, false);
 
        if (dev->mode == CLOCK_EVT_MODE_PERIODIC)
-               dev->set_mode(CLOCK_EVT_MODE_PERIODIC, dev);
+               return __clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
 
        return 0;
 }
index 61ed862cdd376222dedfa317301f3d6c3dbd3404..2cfd19485824a4d0b7baf9b3b5dd22c81eb60536 100644 (file)
@@ -228,9 +228,35 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
        print_name_offset(m, dev->set_next_event);
        SEQ_printf(m, "\n");
 
-       SEQ_printf(m, " set_mode:       ");
-       print_name_offset(m, dev->set_mode);
-       SEQ_printf(m, "\n");
+       if (dev->set_mode) {
+               SEQ_printf(m, " set_mode:       ");
+               print_name_offset(m, dev->set_mode);
+               SEQ_printf(m, "\n");
+       } else {
+               if (dev->set_mode_shutdown) {
+                       SEQ_printf(m, " shutdown: ");
+                       print_name_offset(m, dev->set_mode_shutdown);
+                       SEQ_printf(m, "\n");
+               }
+
+               if (dev->set_mode_periodic) {
+                       SEQ_printf(m, " periodic: ");
+                       print_name_offset(m, dev->set_mode_periodic);
+                       SEQ_printf(m, "\n");
+               }
+
+               if (dev->set_mode_oneshot) {
+                       SEQ_printf(m, " oneshot:  ");
+                       print_name_offset(m, dev->set_mode_oneshot);
+                       SEQ_printf(m, "\n");
+               }
+
+               if (dev->set_mode_resume) {
+                       SEQ_printf(m, " resume:   ");
+                       print_name_offset(m, dev->set_mode_resume);
+                       SEQ_printf(m, "\n");
+               }
+       }
 
        SEQ_printf(m, " event_handler:  ");
        print_name_offset(m, dev->event_handler);