aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-msm
diff options
context:
space:
mode:
authorArve Hjønnevåg <arve@android.com>2007-11-26 07:11:58 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-01-26 09:39:14 -0500
commit3e4ea3728a38b224d8b35ff8a9281407f603aa20 (patch)
treeb6deb793ca209ab5948400c82bfa9ac9b32a2b78 /arch/arm/mach-msm
parent3042102a28501510a409fe86962f20369e325cf2 (diff)
[ARM] msm: irq and timer support for ARCH_MSM7X00A
- Vectored Interrupt Controller support - Timer support using the GPT and DGT timers Signed-off-by: Brian Swetland <swetland@google.com>
Diffstat (limited to 'arch/arm/mach-msm')
-rw-r--r--arch/arm/mach-msm/Makefile2
-rw-r--r--arch/arm/mach-msm/irq.c154
-rw-r--r--arch/arm/mach-msm/timer.c205
3 files changed, 360 insertions, 1 deletions
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index feb9e469df9a..a42d1a019cd2 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -1,2 +1,2 @@
1obj-y += io.o idle.o 1obj-y += io.o idle.o irq.o timer.o
2 2
diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c
new file mode 100644
index 000000000000..24158040b789
--- /dev/null
+++ b/arch/arm/mach-msm/irq.c
@@ -0,0 +1,154 @@
1/* linux/arch/arm/mach-msm/irq.c
2 *
3 * Copyright (C) 2007 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/sched.h>
19#include <linux/interrupt.h>
20#include <linux/ptrace.h>
21#include <linux/timer.h>
22
23#include <linux/irq.h>
24#include <asm/hardware.h>
25
26#include <asm/io.h>
27
28#include <asm/arch/msm_iomap.h>
29
30#define VIC_REG(off) (MSM_VIC_BASE + (off))
31
32#define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */
33#define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */
34#define VIC_INT_EN0 VIC_REG(0x0010)
35#define VIC_INT_EN1 VIC_REG(0x0014)
36#define VIC_INT_ENCLEAR0 VIC_REG(0x0020)
37#define VIC_INT_ENCLEAR1 VIC_REG(0x0024)
38#define VIC_INT_ENSET0 VIC_REG(0x0030)
39#define VIC_INT_ENSET1 VIC_REG(0x0034)
40#define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */
41#define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */
42#define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */
43#define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */
44#define VIC_NO_PEND_VAL VIC_REG(0x0060)
45#define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */
46#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */
47#define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */
48#define VIC_IRQ_STATUS0 VIC_REG(0x0080)
49#define VIC_IRQ_STATUS1 VIC_REG(0x0084)
50#define VIC_FIQ_STATUS0 VIC_REG(0x0090)
51#define VIC_FIQ_STATUS1 VIC_REG(0x0094)
52#define VIC_RAW_STATUS0 VIC_REG(0x00A0)
53#define VIC_RAW_STATUS1 VIC_REG(0x00A4)
54#define VIC_INT_CLEAR0 VIC_REG(0x00B0)
55#define VIC_INT_CLEAR1 VIC_REG(0x00B4)
56#define VIC_SOFTINT0 VIC_REG(0x00C0)
57#define VIC_SOFTINT1 VIC_REG(0x00C4)
58#define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */
59#define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */
60#define VIC_IRQ_VEC_WR VIC_REG(0x00D8)
61#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0)
62#define VIC_IRQ_IN_STACK VIC_REG(0x00E4)
63#define VIC_TEST_BUS_SEL VIC_REG(0x00E8)
64
65#define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4))
66#define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4))
67
68static void msm_irq_ack(unsigned int irq)
69{
70 unsigned reg = VIC_INT_CLEAR0 + ((irq & 32) ? 4 : 0);
71 irq = 1 << (irq & 31);
72 writel(irq, reg);
73}
74
75static void msm_irq_mask(unsigned int irq)
76{
77 unsigned reg = VIC_INT_ENCLEAR0 + ((irq & 32) ? 4 : 0);
78 writel(1 << (irq & 31), reg);
79}
80
81static void msm_irq_unmask(unsigned int irq)
82{
83 unsigned reg = VIC_INT_ENSET0 + ((irq & 32) ? 4 : 0);
84 writel(1 << (irq & 31), reg);
85}
86
87static int msm_irq_set_wake(unsigned int irq, unsigned int on)
88{
89 return -EINVAL;
90}
91
92static int msm_irq_set_type(unsigned int irq, unsigned int flow_type)
93{
94 unsigned treg = VIC_INT_TYPE0 + ((irq & 32) ? 4 : 0);
95 unsigned preg = VIC_INT_POLARITY0 + ((irq & 32) ? 4 : 0);
96 int b = 1 << (irq & 31);
97
98 if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
99 writel(readl(preg) | b, preg);
100 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
101 writel(readl(preg) & (~b), preg);
102
103 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
104 writel(readl(treg) | b, treg);
105 set_irq_handler(irq, handle_edge_irq);
106 }
107 if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) {
108 writel(readl(treg) & (~b), treg);
109 set_irq_handler(irq, handle_level_irq);
110 }
111 return 0;
112}
113
114static struct irq_chip msm_irq_chip = {
115 .name = "msm",
116 .ack = msm_irq_ack,
117 .mask = msm_irq_mask,
118 .unmask = msm_irq_unmask,
119 .set_wake = msm_irq_set_wake,
120 .set_type = msm_irq_set_type,
121};
122
123void __init msm_init_irq(void)
124{
125 unsigned n;
126
127 /* select level interrupts */
128 writel(0, VIC_INT_TYPE0);
129 writel(0, VIC_INT_TYPE1);
130
131 /* select highlevel interrupts */
132 writel(0, VIC_INT_POLARITY0);
133 writel(0, VIC_INT_POLARITY1);
134
135 /* select IRQ for all INTs */
136 writel(0, VIC_INT_SELECT0);
137 writel(0, VIC_INT_SELECT1);
138
139 /* disable all INTs */
140 writel(0, VIC_INT_EN0);
141 writel(0, VIC_INT_EN1);
142
143 /* don't use 1136 vic */
144 writel(0, VIC_CONFIG);
145
146 /* enable interrupt controller */
147 writel(1, VIC_INT_MASTEREN);
148
149 for (n = 0; n < NR_MSM_IRQS; n++) {
150 set_irq_chip(n, &msm_irq_chip);
151 set_irq_handler(n, handle_level_irq);
152 set_irq_flags(n, IRQF_VALID);
153 }
154}
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
new file mode 100644
index 000000000000..bd4732d1ab3e
--- /dev/null
+++ b/arch/arm/mach-msm/timer.c
@@ -0,0 +1,205 @@
1/* linux/arch/arm/mach-msm/timer.c
2 *
3 * Copyright (C) 2007 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/init.h>
17#include <linux/time.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/clk.h>
21#include <linux/clockchips.h>
22#include <linux/delay.h>
23
24#include <asm/mach/time.h>
25#include <asm/arch/msm_iomap.h>
26
27#include <asm/io.h>
28
29#define MSM_DGT_BASE (MSM_GPT_BASE + 0x10)
30#define MSM_DGT_SHIFT (5)
31
32#define TIMER_MATCH_VAL 0x0000
33#define TIMER_COUNT_VAL 0x0004
34#define TIMER_ENABLE 0x0008
35#define TIMER_ENABLE_CLR_ON_MATCH_EN 2
36#define TIMER_ENABLE_EN 1
37#define TIMER_CLEAR 0x000C
38
39#define CSR_PROTECTION 0x0020
40#define CSR_PROTECTION_EN 1
41
42#define GPT_HZ 32768
43#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */
44
45struct msm_clock {
46 struct clock_event_device clockevent;
47 struct clocksource clocksource;
48 struct irqaction irq;
49 uint32_t regbase;
50 uint32_t freq;
51 uint32_t shift;
52};
53
54static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
55{
56 struct clock_event_device *evt = dev_id;
57 evt->event_handler(evt);
58 return IRQ_HANDLED;
59}
60
61static cycle_t msm_gpt_read(void)
62{
63 return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
64}
65
66static cycle_t msm_dgt_read(void)
67{
68 return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT;
69}
70
71static int msm_timer_set_next_event(unsigned long cycles,
72 struct clock_event_device *evt)
73{
74 struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
75 uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL);
76 uint32_t alarm = now + (cycles << clock->shift);
77 int late;
78
79 writel(alarm, clock->regbase + TIMER_MATCH_VAL);
80 now = readl(clock->regbase + TIMER_COUNT_VAL);
81 late = now - alarm;
82 if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
83 printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
84 "alarm already expired, now %x, alarm %x, late %d\n",
85 cycles, clock->clockevent.name, now, alarm, late);
86 return -ETIME;
87 }
88 return 0;
89}
90
91static void msm_timer_set_mode(enum clock_event_mode mode,
92 struct clock_event_device *evt)
93{
94 struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
95 switch (mode) {
96 case CLOCK_EVT_MODE_RESUME:
97 case CLOCK_EVT_MODE_PERIODIC:
98 break;
99 case CLOCK_EVT_MODE_ONESHOT:
100 writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
101 break;
102 case CLOCK_EVT_MODE_UNUSED:
103 case CLOCK_EVT_MODE_SHUTDOWN:
104 writel(0, clock->regbase + TIMER_ENABLE);
105 break;
106 }
107}
108
109static struct msm_clock msm_clocks[] = {
110 {
111 .clockevent = {
112 .name = "gp_timer",
113 .features = CLOCK_EVT_FEAT_ONESHOT,
114 .shift = 32,
115 .rating = 200,
116 .set_next_event = msm_timer_set_next_event,
117 .set_mode = msm_timer_set_mode,
118 },
119 .clocksource = {
120 .name = "gp_timer",
121 .rating = 200,
122 .read = msm_gpt_read,
123 .mask = CLOCKSOURCE_MASK(32),
124 .shift = 24,
125 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
126 },
127 .irq = {
128 .name = "gp_timer",
129 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
130 .handler = msm_timer_interrupt,
131 .dev_id = &msm_clocks[0].clockevent,
132 .irq = INT_GP_TIMER_EXP
133 },
134 .regbase = MSM_GPT_BASE,
135 .freq = GPT_HZ
136 },
137 {
138 .clockevent = {
139 .name = "dg_timer",
140 .features = CLOCK_EVT_FEAT_ONESHOT,
141 .shift = 32 + MSM_DGT_SHIFT,
142 .rating = 300,
143 .set_next_event = msm_timer_set_next_event,
144 .set_mode = msm_timer_set_mode,
145 },
146 .clocksource = {
147 .name = "dg_timer",
148 .rating = 300,
149 .read = msm_dgt_read,
150 .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
151 .shift = 24 - MSM_DGT_SHIFT,
152 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
153 },
154 .irq = {
155 .name = "dg_timer",
156 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
157 .handler = msm_timer_interrupt,
158 .dev_id = &msm_clocks[1].clockevent,
159 .irq = INT_DEBUG_TIMER_EXP
160 },
161 .regbase = MSM_DGT_BASE,
162 .freq = DGT_HZ >> MSM_DGT_SHIFT,
163 .shift = MSM_DGT_SHIFT
164 }
165};
166
167static void __init msm_timer_init(void)
168{
169 int i;
170 int res;
171
172 for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) {
173 struct msm_clock *clock = &msm_clocks[i];
174 struct clock_event_device *ce = &clock->clockevent;
175 struct clocksource *cs = &clock->clocksource;
176 writel(0, clock->regbase + TIMER_ENABLE);
177 writel(0, clock->regbase + TIMER_CLEAR);
178 writel(~0, clock->regbase + TIMER_MATCH_VAL);
179
180 ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift);
181 /* allow at least 10 seconds to notice that the timer wrapped */
182 ce->max_delta_ns =
183 clockevent_delta2ns(0xf0000000 >> clock->shift, ce);
184 /* 4 gets rounded down to 3 */
185 ce->min_delta_ns = clockevent_delta2ns(4, ce);
186 ce->cpumask = cpumask_of_cpu(0);
187
188 cs->mult = clocksource_hz2mult(clock->freq, cs->shift);
189 res = clocksource_register(cs);
190 if (res)
191 printk(KERN_ERR "msm_timer_init: clocksource_register "
192 "failed for %s\n", cs->name);
193
194 res = setup_irq(clock->irq.irq, &clock->irq);
195 if (res)
196 printk(KERN_ERR "msm_timer_init: setup_irq "
197 "failed for %s\n", cs->name);
198
199 clockevents_register_device(ce);
200 }
201}
202
203struct sys_timer msm_timer = {
204 .init = msm_timer_init
205};