aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorDaniel Tang <dt.tangr@gmail.com>2013-06-01 02:02:37 -0400
committerDaniel Lezcano <daniel.lezcano@linaro.org>2013-06-06 11:23:13 -0400
commit77ba83bb1bb1cdabd522d32536f8eee65a870145 (patch)
treeabb558298ffeccafa780996f2bcd90f17a893d43 /drivers/clocksource
parentc19672492d233e0012b60fbfa460ffac1381ee26 (diff)
clocksource: Add TI-Nspire timer support
This patch adds a clocksource/clockevent driver for the timer found on some models in the TI-Nspire calculator series. The timer has two 16bit subtimers within its memory mapped I/O interface but only the first can generate interrupts. The first subtimer is used to generate clockevents but only if an interrupt number and register is given. The interrupt acknowledgement mechanism is a little strange because the interrupt mask and acknowledge registers are located in another memory mapped I/O peripheral. The address of this register is passed to the driver through device tree bindings. The second subtimer is used as a clocksource because it isn't capable of generating an interrupt. This subtimer is always added. Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Daniel Tang <dt.tangr@gmail.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/zevio-timer.c215
2 files changed, 216 insertions, 0 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 36a9ac105d55..4853ea0f8fd5 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
22obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o 22obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
23obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o 23obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
24obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o 24obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
25obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o
25obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o 26obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o 27obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o 28obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
new file mode 100644
index 000000000000..ca81809d159d
--- /dev/null
+++ b/drivers/clocksource/zevio-timer.c
@@ -0,0 +1,215 @@
1/*
2 * linux/drivers/clocksource/zevio-timer.c
3 *
4 * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 */
11
12#include <linux/io.h>
13#include <linux/irq.h>
14#include <linux/of.h>
15#include <linux/of_address.h>
16#include <linux/of_irq.h>
17#include <linux/clk.h>
18#include <linux/clockchips.h>
19#include <linux/cpumask.h>
20#include <linux/interrupt.h>
21#include <linux/slab.h>
22
23#define IO_CURRENT_VAL 0x00
24#define IO_DIVIDER 0x04
25#define IO_CONTROL 0x08
26
27#define IO_TIMER1 0x00
28#define IO_TIMER2 0x0C
29
30#define IO_MATCH_BEGIN 0x18
31#define IO_MATCH(x) (IO_MATCH_BEGIN + ((x) << 2))
32
33#define IO_INTR_STS 0x00
34#define IO_INTR_ACK 0x00
35#define IO_INTR_MSK 0x04
36
37#define CNTL_STOP_TIMER (1 << 4)
38#define CNTL_RUN_TIMER (0 << 4)
39
40#define CNTL_INC (1 << 3)
41#define CNTL_DEC (0 << 3)
42
43#define CNTL_TOZERO 0
44#define CNTL_MATCH(x) ((x) + 1)
45#define CNTL_FOREVER 7
46
47/* There are 6 match registers but we only use one. */
48#define TIMER_MATCH 0
49
50#define TIMER_INTR_MSK (1 << (TIMER_MATCH))
51#define TIMER_INTR_ALL 0x3F
52
53struct zevio_timer {
54 void __iomem *base;
55 void __iomem *timer1, *timer2;
56 void __iomem *interrupt_regs;
57
58 struct clk *clk;
59 struct clock_event_device clkevt;
60 struct irqaction clkevt_irq;
61
62 char clocksource_name[64];
63 char clockevent_name[64];
64};
65
66static int zevio_timer_set_event(unsigned long delta,
67 struct clock_event_device *dev)
68{
69 struct zevio_timer *timer = container_of(dev, struct zevio_timer,
70 clkevt);
71
72 writel(delta, timer->timer1 + IO_CURRENT_VAL);
73 writel(CNTL_RUN_TIMER | CNTL_DEC | CNTL_MATCH(TIMER_MATCH),
74 timer->timer1 + IO_CONTROL);
75
76 return 0;
77}
78
79static void zevio_timer_set_mode(enum clock_event_mode mode,
80 struct clock_event_device *dev)
81{
82 struct zevio_timer *timer = container_of(dev, struct zevio_timer,
83 clkevt);
84
85 switch (mode) {
86 case CLOCK_EVT_MODE_RESUME:
87 case CLOCK_EVT_MODE_ONESHOT:
88 /* Enable timer interrupts */
89 writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_MSK);
90 writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
91 break;
92 case CLOCK_EVT_MODE_SHUTDOWN:
93 case CLOCK_EVT_MODE_UNUSED:
94 /* Disable timer interrupts */
95 writel(0, timer->interrupt_regs + IO_INTR_MSK);
96 writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
97 /* Stop timer */
98 writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
99 break;
100 case CLOCK_EVT_MODE_PERIODIC:
101 default:
102 /* Unsupported */
103 break;
104 }
105}
106
107static irqreturn_t zevio_timer_interrupt(int irq, void *dev_id)
108{
109 struct zevio_timer *timer = dev_id;
110 u32 intr;
111
112 intr = readl(timer->interrupt_regs + IO_INTR_ACK);
113 if (!(intr & TIMER_INTR_MSK))
114 return IRQ_NONE;
115
116 writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_ACK);
117 writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
118
119 if (timer->clkevt.event_handler)
120 timer->clkevt.event_handler(&timer->clkevt);
121
122 return IRQ_HANDLED;
123}
124
125static int __init zevio_timer_add(struct device_node *node)
126{
127 struct zevio_timer *timer;
128 struct resource res;
129 int irqnr, ret;
130
131 timer = kzalloc(sizeof(*timer), GFP_KERNEL);
132 if (!timer)
133 return -ENOMEM;
134
135 timer->base = of_iomap(node, 0);
136 if (!timer->base) {
137 ret = -EINVAL;
138 goto error_free;
139 }
140 timer->timer1 = timer->base + IO_TIMER1;
141 timer->timer2 = timer->base + IO_TIMER2;
142
143 timer->clk = of_clk_get(node, 0);
144 if (IS_ERR(timer->clk)) {
145 ret = PTR_ERR(timer->clk);
146 pr_err("Timer clock not found! (error %d)\n", ret);
147 goto error_unmap;
148 }
149
150 timer->interrupt_regs = of_iomap(node, 1);
151 irqnr = irq_of_parse_and_map(node, 0);
152
153 of_address_to_resource(node, 0, &res);
154 scnprintf(timer->clocksource_name, sizeof(timer->clocksource_name),
155 "%llx.%s_clocksource",
156 (unsigned long long)res.start, node->name);
157
158 scnprintf(timer->clockevent_name, sizeof(timer->clockevent_name),
159 "%llx.%s_clockevent",
160 (unsigned long long)res.start, node->name);
161
162 if (timer->interrupt_regs && irqnr) {
163 timer->clkevt.name = timer->clockevent_name;
164 timer->clkevt.set_next_event = zevio_timer_set_event;
165 timer->clkevt.set_mode = zevio_timer_set_mode;
166 timer->clkevt.rating = 200;
167 timer->clkevt.cpumask = cpu_all_mask;
168 timer->clkevt.features = CLOCK_EVT_FEAT_ONESHOT;
169 timer->clkevt.irq = irqnr;
170
171 writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL);
172 writel(0, timer->timer1 + IO_DIVIDER);
173
174 /* Start with timer interrupts disabled */
175 writel(0, timer->interrupt_regs + IO_INTR_MSK);
176 writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK);
177
178 /* Interrupt to occur when timer value matches 0 */
179 writel(0, timer->base + IO_MATCH(TIMER_MATCH));
180
181 timer->clkevt_irq.name = timer->clockevent_name;
182 timer->clkevt_irq.handler = zevio_timer_interrupt;
183 timer->clkevt_irq.dev_id = timer;
184 timer->clkevt_irq.flags = IRQF_TIMER | IRQF_IRQPOLL;
185
186 setup_irq(irqnr, &timer->clkevt_irq);
187
188 clockevents_config_and_register(&timer->clkevt,
189 clk_get_rate(timer->clk), 0x0001, 0xffff);
190 pr_info("Added %s as clockevent\n", timer->clockevent_name);
191 }
192
193 writel(CNTL_STOP_TIMER, timer->timer2 + IO_CONTROL);
194 writel(0, timer->timer2 + IO_CURRENT_VAL);
195 writel(0, timer->timer2 + IO_DIVIDER);
196 writel(CNTL_RUN_TIMER | CNTL_FOREVER | CNTL_INC,
197 timer->timer2 + IO_CONTROL);
198
199 clocksource_mmio_init(timer->timer2 + IO_CURRENT_VAL,
200 timer->clocksource_name,
201 clk_get_rate(timer->clk),
202 200, 16,
203 clocksource_mmio_readw_up);
204
205 pr_info("Added %s as clocksource\n", timer->clocksource_name);
206
207 return 0;
208error_unmap:
209 iounmap(timer->base);
210error_free:
211 kfree(timer);
212 return ret;
213}
214
215CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_add);