aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-23 13:34:47 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-23 13:34:47 -0400
commit9d0715630ebf7bf70daa5e6d8db0e3061268c61e (patch)
tree54562185002b22169d81e0fb3c21312cb510ac40 /drivers/clocksource
parentc0c463d34adf0c150e5e24fa412fa23f3f7ddc27 (diff)
parent06c3df49521c1b112b777cc4946e5de057c814ba (diff)
Merge branch 'timers-clocksource-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'timers-clocksource-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: clocksource: apb: Share APB timer code with other platforms
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig3
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/dw_apb_timer.c401
3 files changed, 405 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d8d3e02b912c..34e9c4f88926 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -12,3 +12,6 @@ config CLKBLD_I8253
12 12
13config CLKSRC_MMIO 13config CLKSRC_MMIO
14 bool 14 bool
15
16config DW_APB_TIMER
17 bool
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 7922a0cfc99f..85ad1646a7b7 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
8obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o 8obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
9obj-$(CONFIG_CLKBLD_I8253) += i8253.o 9obj-$(CONFIG_CLKBLD_I8253) += i8253.o
10obj-$(CONFIG_CLKSRC_MMIO) += mmio.o 10obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
11obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c
new file mode 100644
index 000000000000..580f870541a3
--- /dev/null
+++ b/drivers/clocksource/dw_apb_timer.c
@@ -0,0 +1,401 @@
1/*
2 * (C) Copyright 2009 Intel Corporation
3 * Author: Jacob Pan (jacob.jun.pan@intel.com)
4 *
5 * Shared with ARM platforms, Jamie Iles, Picochip 2011
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Support for the Synopsys DesignWare APB Timers.
12 */
13#include <linux/dw_apb_timer.h>
14#include <linux/delay.h>
15#include <linux/kernel.h>
16#include <linux/interrupt.h>
17#include <linux/irq.h>
18#include <linux/io.h>
19#include <linux/slab.h>
20
21#define APBT_MIN_PERIOD 4
22#define APBT_MIN_DELTA_USEC 200
23
24#define APBTMR_N_LOAD_COUNT 0x00
25#define APBTMR_N_CURRENT_VALUE 0x04
26#define APBTMR_N_CONTROL 0x08
27#define APBTMR_N_EOI 0x0c
28#define APBTMR_N_INT_STATUS 0x10
29
30#define APBTMRS_INT_STATUS 0xa0
31#define APBTMRS_EOI 0xa4
32#define APBTMRS_RAW_INT_STATUS 0xa8
33#define APBTMRS_COMP_VERSION 0xac
34
35#define APBTMR_CONTROL_ENABLE (1 << 0)
36/* 1: periodic, 0:free running. */
37#define APBTMR_CONTROL_MODE_PERIODIC (1 << 1)
38#define APBTMR_CONTROL_INT (1 << 2)
39
40static inline struct dw_apb_clock_event_device *
41ced_to_dw_apb_ced(struct clock_event_device *evt)
42{
43 return container_of(evt, struct dw_apb_clock_event_device, ced);
44}
45
46static inline struct dw_apb_clocksource *
47clocksource_to_dw_apb_clocksource(struct clocksource *cs)
48{
49 return container_of(cs, struct dw_apb_clocksource, cs);
50}
51
52static unsigned long apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
53{
54 return readl(timer->base + offs);
55}
56
57static void apbt_writel(struct dw_apb_timer *timer, unsigned long val,
58 unsigned long offs)
59{
60 writel(val, timer->base + offs);
61}
62
63static void apbt_disable_int(struct dw_apb_timer *timer)
64{
65 unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
66
67 ctrl |= APBTMR_CONTROL_INT;
68 apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
69}
70
71/**
72 * dw_apb_clockevent_pause() - stop the clock_event_device from running
73 *
74 * @dw_ced: The APB clock to stop generating events.
75 */
76void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced)
77{
78 disable_irq(dw_ced->timer.irq);
79 apbt_disable_int(&dw_ced->timer);
80}
81
82static void apbt_eoi(struct dw_apb_timer *timer)
83{
84 apbt_readl(timer, APBTMR_N_EOI);
85}
86
87static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
88{
89 struct clock_event_device *evt = data;
90 struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
91
92 if (!evt->event_handler) {
93 pr_info("Spurious APBT timer interrupt %d", irq);
94 return IRQ_NONE;
95 }
96
97 if (dw_ced->eoi)
98 dw_ced->eoi(&dw_ced->timer);
99
100 evt->event_handler(evt);
101 return IRQ_HANDLED;
102}
103
104static void apbt_enable_int(struct dw_apb_timer *timer)
105{
106 unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
107 /* clear pending intr */
108 apbt_readl(timer, APBTMR_N_EOI);
109 ctrl &= ~APBTMR_CONTROL_INT;
110 apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
111}
112
113static void apbt_set_mode(enum clock_event_mode mode,
114 struct clock_event_device *evt)
115{
116 unsigned long ctrl;
117 unsigned long period;
118 struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
119
120 pr_debug("%s CPU %d mode=%d\n", __func__, first_cpu(*evt->cpumask),
121 mode);
122
123 switch (mode) {
124 case CLOCK_EVT_MODE_PERIODIC:
125 period = DIV_ROUND_UP(dw_ced->timer.freq, HZ);
126 ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
127 ctrl |= APBTMR_CONTROL_MODE_PERIODIC;
128 apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
129 /*
130 * DW APB p. 46, have to disable timer before load counter,
131 * may cause sync problem.
132 */
133 ctrl &= ~APBTMR_CONTROL_ENABLE;
134 apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
135 udelay(1);
136 pr_debug("Setting clock period %lu for HZ %d\n", period, HZ);
137 apbt_writel(&dw_ced->timer, period, APBTMR_N_LOAD_COUNT);
138 ctrl |= APBTMR_CONTROL_ENABLE;
139 apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
140 break;
141
142 case CLOCK_EVT_MODE_ONESHOT:
143 ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
144 /*
145 * set free running mode, this mode will let timer reload max
146 * timeout which will give time (3min on 25MHz clock) to rearm
147 * the next event, therefore emulate the one-shot mode.
148 */
149 ctrl &= ~APBTMR_CONTROL_ENABLE;
150 ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC;
151
152 apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
153 /* write again to set free running mode */
154 apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
155
156 /*
157 * DW APB p. 46, load counter with all 1s before starting free
158 * running mode.
159 */
160 apbt_writel(&dw_ced->timer, ~0, APBTMR_N_LOAD_COUNT);
161 ctrl &= ~APBTMR_CONTROL_INT;
162 ctrl |= APBTMR_CONTROL_ENABLE;
163 apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
164 break;
165
166 case CLOCK_EVT_MODE_UNUSED:
167 case CLOCK_EVT_MODE_SHUTDOWN: