/* * Setup the device for a periodic tick */ void tick_setup_periodic(struct clock_event_device *dev, int broadcast) { tick_set_periodic_handler(dev, broadcast); /* Broadcast setup ? */ if (!tick_device_is_functional(dev)) return; if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) && !tick_broadcast_oneshot_active()) { clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC); } else { unsigned long seq; ktime_t next; do { seq = read_seqcount_begin(&xtime_seq); next = tick_next_period; } while (read_seqcount_retry(&xtime_seq, seq)); clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); for (;;) { if (!clockevents_program_event(dev, next, ktime_get())) return; next = ktime_add(next, tick_period); } } }
/* * Powerstate information: The system enters/leaves a state, where * affected devices might stop */ static void tick_do_broadcast_on_off(void *why) { struct clock_event_device *bc, *dev; struct tick_device *td; unsigned long flags, *reason = why; int cpu; spin_lock_irqsave(&tick_broadcast_lock, flags); cpu = smp_processor_id(); td = &per_cpu(tick_cpu_device, cpu); dev = td->evtdev; bc = tick_broadcast_device.evtdev; /* * Is the device in broadcast mode forever or is it not * affected by the powerstate ? */ if (!dev || !tick_device_is_functional(dev) || !(dev->features & CLOCK_EVT_FEAT_C3STOP)) goto out; if (*reason == CLOCK_EVT_NOTIFY_BROADCAST_ON) { if (!cpu_isset(cpu, tick_broadcast_mask)) { cpu_set(cpu, tick_broadcast_mask); if (td->mode == TICKDEV_MODE_PERIODIC) clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN); } } else { if (cpu_isset(cpu, tick_broadcast_mask)) { cpu_clear(cpu, tick_broadcast_mask); if (td->mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(dev, 0); } } if (cpus_empty(tick_broadcast_mask)) clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN); else { if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_broadcast_start_periodic(bc); else tick_broadcast_setup_oneshot(bc); } out: spin_unlock_irqrestore(&tick_broadcast_lock, flags); }
int tick_resume_broadcast(void) { struct clock_event_device *bc; unsigned long flags; int broadcast = 0; spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; if (bc) { clockevents_set_mode(bc, CLOCK_EVT_MODE_RESUME); switch (tick_broadcast_device.mode) { case TICKDEV_MODE_PERIODIC: if (!cpumask_empty(tick_get_broadcast_mask())) tick_broadcast_start_periodic(bc); broadcast = cpumask_test_cpu(smp_processor_id(), tick_get_broadcast_mask()); break; case TICKDEV_MODE_ONESHOT: broadcast = tick_resume_broadcast_oneshot(bc); break; } } spin_unlock_irqrestore(&tick_broadcast_lock, flags); return broadcast; }
/** * tick_switch_to_oneshot - switch to oneshot mode */ int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); struct clock_event_device *dev = td->evtdev; if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || !tick_device_is_functional(dev)) { printk(KERN_INFO "Clockevents: " "could not switch to one-shot mode:"); if (!dev) { printk(" no tick device\n"); } else { if (!tick_device_is_functional(dev)) printk(" %s is not functional.\n", dev->name); else printk(" %s does not support one-shot mode.\n", dev->name); } return -EINVAL; } td->mode = TICKDEV_MODE_ONESHOT; dev->event_handler = handler; clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); tick_broadcast_switch_to_oneshot(); return 0; }
/* * Called from irq_enter() when idle was interrupted to reenable the * per cpu device. */ void tick_check_oneshot_broadcast(int cpu) { if (cpumask_test_cpu(cpu, to_cpumask(tick_broadcast_oneshot_mask))) { struct tick_device *td = &per_cpu(tick_cpu_device, cpu); clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT); } }
/** * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz) */ void tick_setup_oneshot(struct clock_event_device *newdev, void (*handler)(struct clock_event_device *), ktime_t next_event) { newdev->event_handler = handler; clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); clockevents_program_event(newdev, next_event, ktime_get()); }
/** * tick_resume_onshot - resume oneshot mode */ void tick_resume_oneshot(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); struct clock_event_device *dev = td->evtdev; clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); tick_program_event(ktime_get(), 1); }
static int tick_broadcast_set_event(ktime_t expires, int force) { struct clock_event_device *bc = tick_broadcast_device.evtdev; if (bc->mode != CLOCK_EVT_MODE_ONESHOT) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); return clockevents_program_event(bc, expires, force); }
static void tick_suspend(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); unsigned long flags; spin_lock_irqsave(&tick_device_lock, flags); clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_SHUTDOWN); spin_unlock_irqrestore(&tick_device_lock, flags); }
int tick_resume_broadcast_oneshot(struct clock_event_device *bc) { clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); if(!cpus_empty(tick_broadcast_oneshot_mask)) tick_broadcast_set_event(ktime_get(), 1); return cpu_isset(smp_processor_id(), tick_broadcast_oneshot_mask); }
static int tick_broadcast_set_event(struct clock_event_device *bc, int cpu, ktime_t expires, int force) { int ret; if (bc->mode != CLOCK_EVT_MODE_ONESHOT) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); ret = clockevents_program_event(bc, expires, force); if (!ret) tick_broadcast_set_affinity(bc, cpumask_of(cpu)); return ret; }
void tick_suspend_broadcast(void) { struct clock_event_device *bc; unsigned long flags; spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; if (bc && tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN); spin_unlock_irqrestore(&tick_broadcast_lock, flags); }
void tick_resume(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); int broadcast = tick_resume_broadcast(); clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_RESUME); if (!broadcast) { if (td->mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(td->evtdev, 0); else tick_resume_oneshot(); } }
/** * tick_switch_to_oneshot - switch to oneshot mode */ int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); struct clock_event_device *dev = td->evtdev; if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || !tick_device_is_functional(dev)) return -EINVAL; td->mode = TICKDEV_MODE_ONESHOT; dev->event_handler = handler; clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); tick_broadcast_switch_to_oneshot(); return 0; }
/* * Called from irq_enter() when idle was interrupted to reenable the * per cpu device. */ void tick_check_oneshot_broadcast(int cpu) { if (cpumask_test_cpu(cpu, tick_broadcast_oneshot_mask)) { struct tick_device *td = &per_cpu(tick_cpu_device, cpu); /* * We might be in the middle of switching over from * periodic to oneshot. If the CPU has not yet * switched over, leave the device alone. */ if (td->mode == TICKDEV_MODE_ONESHOT) { clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT); } } }
static void tick_resume(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); unsigned long flags; int broadcast = tick_resume_broadcast(); raw_spin_lock_irqsave(&tick_device_lock, flags); clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_RESUME); if (!broadcast) { if (td->mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(td->evtdev, 0); else tick_resume_oneshot(); } raw_spin_unlock_irqrestore(&tick_device_lock, flags); }
/* * Remove a CPU from broadcasting */ void tick_shutdown_broadcast(unsigned int *cpup) { struct clock_event_device *bc; unsigned long flags; unsigned int cpu = *cpup; spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; cpu_clear(cpu, tick_broadcast_mask); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) { if (bc && cpus_empty(tick_broadcast_mask)) clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN); } spin_unlock_irqrestore(&tick_broadcast_lock, flags); }
/* * Check, if the new registered device should be used. */ static int tick_check_new_device(struct clock_event_device *newdev) { struct clock_event_device *curdev; struct tick_device *td; int cpu, ret = NOTIFY_OK; unsigned long flags; cpumask_t cpumask; spin_lock_irqsave(&tick_device_lock, flags); cpu = smp_processor_id(); if (!cpu_isset(cpu, newdev->cpumask)) goto out_bc; td = &per_cpu(tick_cpu_device, cpu); curdev = td->evtdev; cpumask = cpumask_of_cpu(cpu); /* cpu local device ? */ if (!cpus_equal(newdev->cpumask, cpumask)) { /* * If the cpu affinity of the device interrupt can not * be set, ignore it. */ if (!irq_can_set_affinity(newdev->irq)) goto out_bc; /* * If we have a cpu local device already, do not replace it * by a non cpu local device */ if (curdev && cpus_equal(curdev->cpumask, cpumask)) goto out_bc; } /* * If we have an active device, then check the rating and the oneshot * feature. */ if (curdev) { /* * Prefer one shot capable devices ! */ if ((curdev->features & CLOCK_EVT_FEAT_ONESHOT) && !(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) goto out_bc; /* * Check the rating */ if (curdev->rating >= newdev->rating) goto out_bc; } /* * Replace the eventually existing device by the new * device. If the current device is the broadcast device, do * not give it back to the clockevents layer ! */ if (tick_is_broadcast_device(curdev)) { clockevents_set_mode(curdev, CLOCK_EVT_MODE_SHUTDOWN); curdev = NULL; } clockevents_exchange_device(curdev, newdev); tick_setup_device(td, newdev, cpu, cpumask); if (newdev->features & CLOCK_EVT_FEAT_ONESHOT) tick_oneshot_notify(); spin_unlock_irqrestore(&tick_device_lock, flags); return NOTIFY_STOP; out_bc: /* * Can the new device be used as a broadcast device ? */ if (tick_check_broadcast_device(newdev)) ret = NOTIFY_STOP; spin_unlock_irqrestore(&tick_device_lock, flags); return ret; }
/** * clockevents_shutdown - shutdown the device and clear next_event * @dev: device to shutdown */ void clockevents_shutdown(struct clock_event_device *dev) { clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN); dev->next_event.tv64 = KTIME_MAX; }
int tick_resume_broadcast_oneshot(struct clock_event_device *bc) { clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); return 0; }
static void __cpuinit mmp_percpu_timer_stop(struct clock_event_device *clk) { clockevents_set_mode(clk, CLOCK_EVT_MODE_SHUTDOWN); disable_irq(clk->irq); return; }