summaryrefslogtreecommitdiff
path: root/arch/arm/plat-aspeed/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-aspeed/timer.c')
-rw-r--r--arch/arm/plat-aspeed/timer.c223
1 files changed, 156 insertions, 67 deletions
diff --git a/arch/arm/plat-aspeed/timer.c b/arch/arm/plat-aspeed/timer.c
index 079d958c6e3f..6805beb24a12 100644
--- a/arch/arm/plat-aspeed/timer.c
+++ b/arch/arm/plat-aspeed/timer.c
@@ -16,19 +16,13 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <asm/io.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include <linux/types.h>
-#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <linux/sched.h>
-
#include <linux/irq.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <mach/hardware.h>
-#include <mach/irqs.h>
#include <mach/time.h>
#include <plat/ast-scu.h>
@@ -37,72 +31,145 @@
#define ASPEED_TIMER2_VA_BASE (IO_ADDRESS(AST_TIMER_BASE)+ASPEED_TIMER2_OFFSET)
#define ASPEED_TIMERC_VA_BASE (IO_ADDRESS(AST_TIMER_BASE)+ASPEED_TIMERRC_OFFSET)
-/*
- * Returns number of ms since last clock interrupt. Note that interrupts
- * will have been disabled by do_gettimeoffset()
- */
-static unsigned long ast_gettimeoffset(void)
+#define ASPEED_TIMER_RELOAD_MAX 0xFFFFFFFF
+#define ASPEED_TIMER_RELOAD_MIN 1
+
+static struct clock_event_device clockevent_ast;
+
+static inline unsigned long ast_timer_read_count(void *base)
{
- volatile TimerStruct_t *timer0 = (TimerStruct_t *) ASPEED_TIMER0_VA_BASE;
- unsigned long ticks1, ticks2;//, status;
+ volatile TimerStruct_t *timer = (volatile TimerStruct_t *)(base);
+ return timer->TimerValue;
+}
- /*
- * Get the current number of ticks. Note that there is a race
- * condition between us reading the timer and checking for
- * an interrupt. We get around this by ensuring that the
- * counter has not reloaded between our two reads.
- */
- ticks2 = timer0->TimerValue;
- do {
- ticks1 = ticks2;
-// status = readl(AST_RAW_STS(0));// __raw_readl(IO_ADDRESS(ASPEED_VIC_BASE) + ASPEED_VIC_RAW_STATUS_OFFSET);
- ticks2 = timer0->TimerValue;
- } while (ticks2 > ticks1);
+/* change the timer count and load value (if requeseted) */
+static inline void ast_timer_set_count(void *base, unsigned long count,
+ unsigned long reload)
+{
+ volatile TimerStruct_t *timer = (volatile TimerStruct_t *)(base);
+ timer->TimerValue = count;
+ timer->TimerLoad = reload;
+}
- /*
- * Number of ticks since last interrupt.
- */
- ticks1 = TIMER_RELOAD - ticks2;
+#define AST_TIMER_DISABLE 0
+#define AST_TIMER_ENABLE 1
- /*
- * Interrupt pending? If so, we've reloaded once already.
- */
-// if (status & (1 << IRQ_TIMER0))
-// ticks1 += TIMER_RELOAD;
+static inline void ast_timer0_ctrl(int enable)
+{
+ volatile __u32 *timerc = (volatile __u32*) ASPEED_TIMERC_VA_BASE;
+ if (enable == AST_TIMER_ENABLE) {
+ *timerc |= TIMER0_ENABLE | TIMER0_RefExt;
+ } else {
+ *timerc &= ~TIMER0_ENABLE;
+ }
+}
- /*
- * Convert the ticks to usecs
- */
- return TICKS2USECS(ticks1);
+static inline void ast_timer1_ctrl(int enable)
+{
+ volatile __u32 *timerc = (volatile __u32*) ASPEED_TIMERC_VA_BASE;
+ if (enable == AST_TIMER_ENABLE) {
+ *timerc |= TIMER1_ENABLE | TIMER1_RefExt;
+ } else {
+ *timerc &= ~TIMER1_ENABLE;
+ }
}
+static inline void ast_timer_disable_all()
+{
+ volatile __u32 *timerc = (volatile __u32*) ASPEED_TIMERC_VA_BASE;
+ *timerc = 0;
+}
/*
- * IRQ handler for the timer
+ * clocksource
*/
-static irqreturn_t
-ast_timer_interrupt(int irq, void *dev_id)
+static irqreturn_t ast_clocksource_interrupt(int irq, void *dev_id)
{
+ return IRQ_HANDLED;
+}
-// write_seqlock(&xtime_lock);
+static struct irqaction ast_clocksource_irq = {
+ .name = "ast-clocksource",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = ast_clocksource_interrupt,
+};
+
+static cycle_t read_cycles(void)
+{
+#if 1
+ return (cycles_t)(ASPEED_TIMER_RELOAD_MAX
+ - ast_timer_read_count(ASPEED_TIMER1_VA_BASE));
+#else
+ return (cycles_t) ast_timer_read_count(ASPEED_TIMER1_VA_BASE);
+#endif
+}
+
+static struct clocksource clocksource_ast = {
+ .name = "ast-clocksource",
+ .rating = 300,
+ .read = read_cycles,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
/*
- * clear the interrupt in Irq.c
+ * clockevent
*/
-// IRQ_EDGE_CLEAR(0,IRQ_TIMER0);
-
- timer_tick();
+/* IRQ handler for the timer */
+static irqreturn_t ast_clockevent_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &clockevent_ast;
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+}
+static struct irqaction ast_clockevent_irq = {
+ .name = "ast-clockevent",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = ast_clockevent_interrupt,
+};
-// write_sequnlock(&xtime_lock);
+static int ast_timer_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ /* In this case, we shall not set the load value. */
+ ast_timer_set_count(ASPEED_TIMER0_VA_BASE, cycles, 0);
+ /* turn on the timer */
+ ast_timer0_ctrl(AST_TIMER_ENABLE);
+ return 0;
+}
- return IRQ_HANDLED;
+static void ast_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ /* stop timer first */
+ ast_timer0_ctrl(AST_TIMER_DISABLE);
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ ast_timer_set_count(ASPEED_TIMER0_VA_BASE,
+ TIMER_RELOAD - 1, TIMER_RELOAD - 1);
+ ast_timer0_ctrl(AST_TIMER_ENABLE);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ /*
+ * Leave the timer disabled, ast_timer_set_next_event() will
+ * enable it later
+ */
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
}
-static struct irqaction ast_timer_irq = {
- .name = "ast timer",
- .flags = IRQF_DISABLED | IRQF_TIMER,
- .handler = ast_timer_interrupt,
+static struct clock_event_device clockevent_ast = {
+ .name = "ast-clockevent",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .set_next_event = ast_timer_set_next_event,
+ .set_mode = ast_timer_set_mode,
};
/*
@@ -110,28 +177,50 @@ static struct irqaction ast_timer_irq = {
*/
static void __init ast_setup_timer(void)
{
- volatile TimerStruct_t *timer0 = (volatile TimerStruct_t *) ASPEED_TIMER0_VA_BASE;
- volatile __u32 *timerc = (volatile __u32*) ASPEED_TIMERC_VA_BASE;
-
/*
* Initialise to a known state (all timers off)
*/
- *timerc = 0;
-
- timer0->TimerLoad = TIMER_RELOAD - 1;
- timer0->TimerValue = TIMER_RELOAD - 1;
- *timerc = TIMER0_ENABLE | TIMER0_RefExt;
+ ast_timer_disable_all();
/*
- * Make irqs happen for the system timer
+ * For clock event, set the value and reload to 0, so that no interrupt even
+ * after enabling timer.
*/
+ ast_timer_set_count(ASPEED_TIMER0_VA_BASE, 0, 0);
+ /*
+ * For clock source, set the value and reload to the max
+ */
+ ast_timer_set_count(ASPEED_TIMER1_VA_BASE,
+ ASPEED_TIMER_RELOAD_MAX, ASPEED_TIMER_RELOAD_MAX);
+
+ /* Enable timer */
+ ast_timer0_ctrl(AST_TIMER_ENABLE);
+ ast_timer1_ctrl(AST_TIMER_ENABLE);
+
ast_scu_show_system_info();
- setup_irq(IRQ_TIMER0, &ast_timer_irq);
-
+ /* irqs happen for the system timer */
+ setup_irq(IRQ_TIMER0, &ast_clockevent_irq);
+ setup_irq(IRQ_TIMER1, &ast_clocksource_irq);
+
+ /* setup clocksource */
+ clocksource_ast.mult = clocksource_hz2mult(ASPEED_TIMER_CLKRATE,
+ clocksource_ast.shift);
+ if (clocksource_register(&clocksource_ast)) {
+ printk(KERN_ERR "Failed to register clock source %s", clocksource_ast.name);
+ }
+
+ /* setup clockevent */
+ clockevent_ast.mult = div_sc(ASPEED_TIMER_CLKRATE, NSEC_PER_SEC,
+ clockevent_ast.shift);
+ clockevent_ast.max_delta_ns = clockevent_delta2ns(ASPEED_TIMER_RELOAD_MAX,
+ &clockevent_ast);
+ clockevent_ast.min_delta_ns = clockevent_delta2ns(ASPEED_TIMER_RELOAD_MIN,
+ &clockevent_ast);
+ clockevent_ast.cpumask = cpumask_of_cpu(0);
+ clockevents_register_device(&clockevent_ast);
}
struct sys_timer ast_timer = {
.init = ast_setup_timer,
-// .offset = ast_gettimeoffset,
};