|
@@ -171,3 +171,168 @@ static int twd_cpufreq_transition(struct notifier_block *nb,
|
|
|
|
|
|
return NOTIFY_OK;
|
|
return NOTIFY_OK;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+static struct notifier_block twd_cpufreq_nb = {
|
|
|
|
+ .notifier_call = twd_cpufreq_transition,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int twd_cpufreq_init(void)
|
|
|
|
+{
|
|
|
|
+ if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
|
|
|
|
+ return cpufreq_register_notifier(&twd_cpufreq_nb,
|
|
|
|
+ CPUFREQ_TRANSITION_NOTIFIER);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+core_initcall(twd_cpufreq_init);
|
|
|
|
+
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+static void __cpuinit twd_calibrate_rate(void)
|
|
|
|
+{
|
|
|
|
+ unsigned long count;
|
|
|
|
+ u64 waitjiffies;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If this is the first time round, we need to work out how fast
|
|
|
|
+ * the timer ticks
|
|
|
|
+ */
|
|
|
|
+ if (twd_timer_rate == 0) {
|
|
|
|
+ printk(KERN_INFO "Calibrating local timer... ");
|
|
|
|
+
|
|
|
|
+ /* Wait for a tick to start */
|
|
|
|
+ waitjiffies = get_jiffies_64() + 1;
|
|
|
|
+
|
|
|
|
+ while (get_jiffies_64() < waitjiffies)
|
|
|
|
+ udelay(10);
|
|
|
|
+
|
|
|
|
+ /* OK, now the tick has started, let's get the timer going */
|
|
|
|
+ waitjiffies += 5;
|
|
|
|
+
|
|
|
|
+ /* enable, no interrupt or reload */
|
|
|
|
+ __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
|
|
|
|
+
|
|
|
|
+ /* maximum value */
|
|
|
|
+ __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
|
|
|
|
+
|
|
|
|
+ while (get_jiffies_64() < waitjiffies)
|
|
|
|
+ udelay(10);
|
|
|
|
+
|
|
|
|
+ count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
|
|
|
|
+
|
|
|
|
+ twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
|
|
|
|
+
|
|
|
|
+ printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
|
|
|
|
+ (twd_timer_rate / 10000) % 100);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static irqreturn_t twd_handler(int irq, void *dev_id)
|
|
|
|
+{
|
|
|
|
+ struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
|
|
|
|
+
|
|
|
|
+ if (twd_timer_ack()) {
|
|
|
|
+ evt->event_handler(evt);
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return IRQ_NONE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct clk *twd_get_clock(void)
|
|
|
|
+{
|
|
|
|
+ struct clk *clk;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ clk = clk_get_sys("smp_twd", NULL);
|
|
|
|
+ if (IS_ERR(clk)) {
|
|
|
|
+ pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
|
|
|
|
+ return clk;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = clk_prepare_enable(clk);
|
|
|
|
+ if (err) {
|
|
|
|
+ pr_err("smp_twd: clock failed to prepare+enable: %d\n", err);
|
|
|
|
+ clk_put(clk);
|
|
|
|
+ return ERR_PTR(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return clk;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Setup the local clock events for a CPU.
|
|
|
|
+ */
|
|
|
|
+static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
|
|
|
|
+{
|
|
|
|
+ struct clock_event_device **this_cpu_clk;
|
|
|
|
+ int cpu = smp_processor_id();
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If the basic setup for this CPU has been done before don't
|
|
|
|
+ * bother with the below.
|
|
|
|
+ */
|
|
|
|
+ if (per_cpu(percpu_setup_called, cpu)) {
|
|
|
|
+ __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
|
|
|
|
+ clockevents_register_device(*__this_cpu_ptr(twd_evt));
|
|
|
|
+ enable_percpu_irq(clk->irq, 0);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ per_cpu(percpu_setup_called, cpu) = true;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This stuff only need to be done once for the entire TWD cluster
|
|
|
|
+ * during the runtime of the system.
|
|
|
|
+ */
|
|
|
|
+ if (!common_setup_called) {
|
|
|
|
+ twd_clk = twd_get_clock();
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We use IS_ERR_OR_NULL() here, because if the clock stubs
|
|
|
|
+ * are active we will get a valid clk reference which is
|
|
|
|
+ * however NULL and will return the rate 0. In that case we
|
|
|
|
+ * need to calibrate the rate instead.
|
|
|
|
+ */
|
|
|
|
+ if (!IS_ERR_OR_NULL(twd_clk))
|
|
|
|
+ twd_timer_rate = clk_get_rate(twd_clk);
|
|
|
|
+ else
|
|
|
|
+ twd_calibrate_rate();
|
|
|
|
+
|
|
|
|
+ common_setup_called = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The following is done once per CPU the first time .setup() is
|
|
|
|
+ * called.
|
|
|
|
+ */
|
|
|
|
+ __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
|
|
|
|
+
|
|
|
|
+ clk->name = "local_timer";
|
|
|
|
+ clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
|
|
|
|
+ CLOCK_EVT_FEAT_C3STOP;
|
|
|
|
+ clk->rating = 350;
|
|
|
|
+ clk->set_mode = twd_set_mode;
|
|
|
|
+ clk->set_next_event = twd_set_next_event;
|
|
|
|
+ clk->irq = twd_ppi;
|
|
|
|
+
|
|
|
|
+ this_cpu_clk = __this_cpu_ptr(twd_evt);
|
|
|
|
+ *this_cpu_clk = clk;
|
|
|
|
+
|
|
|
|
+ clockevents_config_and_register(clk, twd_timer_rate,
|
|
|
|
+ 0xf, 0xffffffff);
|
|
|
|
+ enable_percpu_irq(clk->irq, 0);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct local_timer_ops twd_lt_ops __cpuinitdata = {
|
|
|
|
+ .setup = twd_timer_setup,
|
|
|
|
+ .stop = twd_timer_stop,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int __init twd_local_timer_common_register(void)
|
|
|
|
+{
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ twd_evt = alloc_percpu(struct clock_event_device *);
|
|
|
|
+ if (!twd_evt) {
|