blob: b8424e3ef1c673678a8b3d13712f488bacda4594 [file] [log] [blame]
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +02001/*
2 * linux/arch/arm/mach-u8500/timer.c
3 *
4 * Copyright (C) 2008 STMicroelectronics
5 * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
Rabin Vincent6af8a132010-03-30 12:46:38 +05306 * Copyright (C) 2009 ST-Ericsson SA
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +02007 * added support to u8500 platform, heavily based on 8815
8 * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2, as
12 * published by the Free Software Foundation.
13 */
14#include <linux/init.h>
15#include <linux/interrupt.h>
16#include <linux/irq.h>
17#include <linux/io.h>
18#include <linux/clockchips.h>
Rabin Vincenta8b58492010-01-20 16:44:02 +053019#include <linux/clk.h>
20#include <linux/err.h>
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020021#include <linux/jiffies.h>
22#include <asm/mach/time.h>
23#include <mach/mtu.h>
Rabin Vincent23619532010-03-05 11:45:43 +053024#include <mach/setup.h>
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020025
26#define TIMER_CTRL 0x80 /* No divisor */
27#define TIMER_PERIODIC 0x40
28#define TIMER_SZ32BIT 0x02
29
30static u32 u8500_count; /* accumulated count */
31static u32 u8500_cycle; /* write-once */
32static __iomem void *mtu0_base;
33static __iomem void *mtu1_base;
34
35/*
36 * clocksource: the MTU device is a decrementing counters, so we negate
37 * the value being read.
38 */
39static cycle_t u8500_read_timer(void)
40{
41 u32 count = readl(mtu1_base + MTU_VAL(0));
42 return ~count;
43}
44
Sundar R Iyerff649452010-03-04 21:19:21 +053045static void u8500_timer_reset(void);
46
47static void u8500_clocksource_resume(void)
48{
49 u8500_timer_reset();
50}
51
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020052static struct clocksource u8500_clksrc = {
53 .name = "mtu_1",
Mian Yousaf Kaukab01bf08b2010-06-16 16:04:57 +020054 .rating = 120,
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020055 .read = u8500_read_timer,
56 .shift = 20,
57 .mask = CLOCKSOURCE_MASK(32),
58 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
Sundar R Iyerff649452010-03-04 21:19:21 +053059 .resume = u8500_clocksource_resume,
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020060};
61
62/*
63 * Clockevent device: currently only periodic mode is supported
64 */
65static void u8500_clkevt_mode(enum clock_event_mode mode,
66 struct clock_event_device *dev)
67{
68 unsigned long flags;
69
70 switch (mode) {
71 case CLOCK_EVT_MODE_PERIODIC:
72 /* enable interrupts -- and count current value? */
73 raw_local_irq_save(flags);
Marcin Mielczarczyk8381b7e2010-06-08 18:31:05 +020074 writel(1, mtu0_base + MTU_IMSC);
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020075 raw_local_irq_restore(flags);
76 break;
77 case CLOCK_EVT_MODE_ONESHOT:
78 BUG(); /* Not yet supported */
79 /* FALLTHROUGH */
80 case CLOCK_EVT_MODE_SHUTDOWN:
81 case CLOCK_EVT_MODE_UNUSED:
82 /* disable irq */
83 raw_local_irq_save(flags);
Marcin Mielczarczyk8381b7e2010-06-08 18:31:05 +020084 writel(0, mtu0_base + MTU_IMSC);
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020085 raw_local_irq_restore(flags);
86 break;
87 case CLOCK_EVT_MODE_RESUME:
88 break;
89 }
90}
91
92static struct clock_event_device u8500_clkevt = {
93 .name = "mtu_0",
94 .features = CLOCK_EVT_FEAT_PERIODIC,
95 .shift = 32,
Mian Yousaf Kaukab01bf08b2010-06-16 16:04:57 +020096 .rating = 100,
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020097 .set_mode = u8500_clkevt_mode,
Sundar R Iyerc7f9f2c2010-03-02 20:30:31 +053098 .irq = IRQ_MTU0,
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +020099};
100
101/*
102 * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
103 * as we are the only users of mtu0 by now.
104 */
105static irqreturn_t u8500_timer_interrupt(int irq, void *dev_id)
106{
107 /* ack: "interrupt clear register" */
108 writel(1 << 0, mtu0_base + MTU_ICR);
109
110 u8500_clkevt.event_handler(&u8500_clkevt);
111
112 return IRQ_HANDLED;
113}
114
115/*
116 * Set up timer interrupt, and return the current time in seconds.
117 */
118static struct irqaction u8500_timer_irq = {
119 .name = "U8500 Timer Tick",
120 .flags = IRQF_DISABLED | IRQF_TIMER,
121 .handler = u8500_timer_interrupt,
122};
123
124static void u8500_timer_reset(void)
125{
126 u32 cr;
127
128 writel(0, mtu0_base + MTU_CR(0)); /* off */
129 writel(0, mtu1_base + MTU_CR(0)); /* off */
130
131 /* Timer: configure load and background-load, and fire it up */
132 writel(u8500_cycle, mtu0_base + MTU_LR(0));
133 writel(u8500_cycle, mtu0_base + MTU_BGLR(0));
134 cr = MTU_CRn_PERIODIC | (MTU_CRn_PRESCALE_1 << 2) | MTU_CRn_32BITS;
135 writel(cr, mtu0_base + MTU_CR(0));
136 writel(cr | MTU_CRn_ENA, mtu0_base + MTU_CR(0));
137
138 /* CS: configure load and background-load, and fire it up */
139 writel(u8500_cycle, mtu1_base + MTU_LR(0));
140 writel(u8500_cycle, mtu1_base + MTU_BGLR(0));
141 cr = (MTU_CRn_PRESCALE_1 << 2) | MTU_CRn_32BITS;
142 writel(cr, mtu1_base + MTU_CR(0));
143 writel(cr | MTU_CRn_ENA, mtu1_base + MTU_CR(0));
144}
145
146static void __init u8500_timer_init(void)
147{
148 unsigned long rate;
Rabin Vincenta8b58492010-01-20 16:44:02 +0530149 struct clk *clk0;
150 struct clk *clk1;
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +0200151 int bits;
152
153#ifdef CONFIG_LOCAL_TIMERS
Rabin Vincentf5cc84a2010-03-09 11:06:14 +0530154 twd_base = (void *)IO_ADDRESS(UX500_TWD_BASE);
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +0200155#endif
Rabin Vincenta8b58492010-01-20 16:44:02 +0530156 clk0 = clk_get_sys("mtu0", NULL);
157 BUG_ON(IS_ERR(clk0));
158
159 clk1 = clk_get_sys("mtu1", NULL);
160 BUG_ON(IS_ERR(clk1));
161
162 clk_enable(clk0);
163 clk_enable(clk1);
164
165 rate = clk_get_rate(clk0);
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +0200166 u8500_cycle = (rate + HZ/2) / HZ;
167
168 /* Save global pointer to mtu, used by functions above */
Rabin Vincentf5cc84a2010-03-09 11:06:14 +0530169 if (cpu_is_u8500ed()) {
Rabin Vincentede0ee72009-11-16 16:52:35 +0530170 mtu0_base = (void *)IO_ADDRESS(U8500_MTU0_BASE_ED);
171 mtu1_base = (void *)IO_ADDRESS(U8500_MTU1_BASE_ED);
172 } else {
Rabin Vincentf5cc84a2010-03-09 11:06:14 +0530173 mtu0_base = (void *)IO_ADDRESS(UX500_MTU0_BASE);
174 mtu1_base = (void *)IO_ADDRESS(UX500_MTU1_BASE);
Rabin Vincentede0ee72009-11-16 16:52:35 +0530175 }
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +0200176
177 /* Init the timer and register clocksource */
178 u8500_timer_reset();
179
Mian Yousaf Kaukabee879242010-07-14 15:03:49 +0200180#ifdef CONFIG_UX500_SOC_DB8500
Sundar R Iyer3956b4a2010-06-24 12:08:37 +0530181 /* register db8500-prcmu timer as always-on clock source */
182 db8500_prcm_timer_init();
Mian Yousaf Kaukabee879242010-07-14 15:03:49 +0200183#endif
Sundar R Iyer3956b4a2010-06-24 12:08:37 +0530184
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +0200185 u8500_clksrc.mult = clocksource_hz2mult(rate, u8500_clksrc.shift);
186 bits = 8*sizeof(u8500_count);
187
188 clocksource_register(&u8500_clksrc);
189
190 /* Register irq and clockevents */
191 setup_irq(IRQ_MTU0, &u8500_timer_irq);
192 u8500_clkevt.mult = div_sc(rate, NSEC_PER_SEC, u8500_clkevt.shift);
193 u8500_clkevt.cpumask = cpumask_of(0);
194 clockevents_register_device(&u8500_clkevt);
Sundar R Iyerc7f9f2c2010-03-02 20:30:31 +0530195 {
196 extern void u8500_rtc_init(unsigned int cpu);
197 u8500_rtc_init(0);
198 }
Mian Yousaf Kaukabb1b22612010-05-01 21:46:35 +0200199}
200
201static void u8500_timer_suspend(void)
202{
203 /* not supported yet */
204}
205
206struct sys_timer u8500_timer = {
207 .init = u8500_timer_init,
208 .suspend = u8500_timer_suspend,
209 .resume = u8500_timer_reset,
210};