|
@@ -10,8 +10,10 @@
|
|
|
*/
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/clk.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/device.h>
|
|
|
+#include <linux/err.h>
|
|
|
#include <linux/smp.h>
|
|
|
#include <linux/jiffies.h>
|
|
|
#include <linux/clockchips.h>
|
|
@@ -25,6 +27,7 @@
|
|
|
/* set up by the platform code */
|
|
|
void __iomem *twd_base;
|
|
|
|
|
|
+static struct clk *twd_clk;
|
|
|
static unsigned long twd_timer_rate;
|
|
|
|
|
|
static struct clock_event_device __percpu **twd_evt;
|
|
@@ -140,6 +143,35 @@ static irqreturn_t twd_handler(int irq, void *dev_id)
|
|
|
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(clk);
|
|
|
+ if (err) {
|
|
|
+ pr_err("smp_twd: clock failed to prepare: %d\n", err);
|
|
|
+ clk_put(clk);
|
|
|
+ return ERR_PTR(err);
|
|
|
+ }
|
|
|
+
|
|
|
+ err = clk_enable(clk);
|
|
|
+ if (err) {
|
|
|
+ pr_err("smp_twd: clock failed to enable: %d\n", err);
|
|
|
+ clk_unprepare(clk);
|
|
|
+ clk_put(clk);
|
|
|
+ return ERR_PTR(err);
|
|
|
+ }
|
|
|
+
|
|
|
+ return clk;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Setup the local clock events for a CPU.
|
|
|
*/
|
|
@@ -165,7 +197,13 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- twd_calibrate_rate();
|
|
|
+ if (!twd_clk)
|
|
|
+ twd_clk = twd_get_clock();
|
|
|
+
|
|
|
+ if (!IS_ERR_OR_NULL(twd_clk))
|
|
|
+ twd_timer_rate = clk_get_rate(twd_clk);
|
|
|
+ else
|
|
|
+ twd_calibrate_rate();
|
|
|
|
|
|
clk->name = "local_timer";
|
|
|
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
|