aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-13 13:57:16 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-13 13:57:16 -0500
commitb8edf848e9119bab9d999b9ca80d8520641810f2 (patch)
tree76517286b247626ed37dda41a4f946f6c34b8bff /drivers/clocksource
parentdb5b0ae00712b5176d7405e7a1dd2bfd6e8f5070 (diff)
parent3f54db784a6af9a6d53396949cbecf62edbad247 (diff)
Merge tag 'multiplatform' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC multiplatform conversion patches from Olof Johansson: "Here are more patches in the progression towards multiplatform, sparse irq conversions in particular. Tegra has a handful of cleanups and general groundwork, but is not quite there yet on full enablement. Platforms that are enabled through this branch are VT8500 and Zynq. Note that i.MX was converted in one of the earlier cleanup branches as well (before we started a separate topic for multiplatform). And both new platforms for this merge window, sunxi and bcm, were merged with multiplatform support enabled." Fix up conflicts mostly as per Olof. * tag 'multiplatform' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (29 commits) ARM: zynq: Remove all unused mach headers ARM: zynq: add support for ARCH_MULTIPLATFORM ARM: zynq: make use of debug_ll_io_init() ARM: zynq: remove TTC early mapping ARM: tegra: move debug-macro.S to include/debug ARM: tegra: don't include iomap.h from debug-macro.S ARM: tegra: decouple uncompress.h and debug-macro.S ARM: tegra: simplify DEBUG_LL UART selection options ARM: tegra: select SPARSE_IRQ ARM: tegra: enhance timer.c to get IO address from device tree ARM: tegra: enhance timer.c to get IRQ info from device tree ARM: timer: fix checkpatch warnings ARM: tegra: add TWD to device tree ARM: tegra: define DT bindings for and instantiate RTC ARM: tegra: define DT bindings for and instantiate timer clocksource/mtu-nomadik: use apb_pclk clk: ux500: Register mtu apb_pclocks ARM: plat-nomadik: convert platforms to SPARSE_IRQ mfd/db8500-prcmu: use the irq_domain_add_simple() mfd/ab8500-core: use irq_domain_add_simple() ...
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig17
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/nomadik-mtu.c230
3 files changed, 247 insertions, 1 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index a0985732f1e..7fdcbd3f4da 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -25,6 +25,21 @@ config ARMADA_370_XP_TIMER
25config SUNXI_TIMER 25config SUNXI_TIMER
26 bool 26 bool
27 27
28config CLKSRC_NOMADIK_MTU
29 bool
30 depends on (ARCH_NOMADIK || ARCH_U8500)
31 select CLKSRC_MMIO
32 help
33 Support for Multi Timer Unit. MTU provides access
34 to multiple interrupt generating programmable
35 32-bit free running decrementing counters.
36
37config CLKSRC_NOMADIK_MTU_SCHED_CLOCK
38 bool
39 depends on CLKSRC_NOMADIK_MTU
40 help
41 Use the Multi Timer Unit as the sched_clock.
42
28config CLKSRC_DBX500_PRCMU 43config CLKSRC_DBX500_PRCMU
29 bool "Clocksource PRCMU Timer" 44 bool "Clocksource PRCMU Timer"
30 depends on UX500_SOC_DB8500 45 depends on UX500_SOC_DB8500
@@ -34,7 +49,7 @@ config CLKSRC_DBX500_PRCMU
34 49
35config CLKSRC_DBX500_PRCMU_SCHED_CLOCK 50config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
36 bool "Clocksource PRCMU Timer sched_clock" 51 bool "Clocksource PRCMU Timer sched_clock"
37 depends on (CLKSRC_DBX500_PRCMU && !NOMADIK_MTU_SCHED_CLOCK) 52 depends on (CLKSRC_DBX500_PRCMU && !CLKSRC_NOMADIK_MTU_SCHED_CLOCK)
38 default y 53 default y
39 help 54 help
40 Use the always on PRCMU Timer as sched_clock 55 Use the always on PRCMU Timer as sched_clock
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 36f06de4c5a..f93453d0167 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_CLKBLD_I8253) += i8253.o
11obj-$(CONFIG_CLKSRC_MMIO) += mmio.o 11obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
12obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o 12obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
13obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o 13obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
14obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o
14obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o 15obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
15obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o 16obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
16obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o 17obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
new file mode 100644
index 00000000000..8914c3c1c88
--- /dev/null
+++ b/drivers/clocksource/nomadik-mtu.c
@@ -0,0 +1,230 @@
1/*
2 * Copyright (C) 2008 STMicroelectronics
3 * Copyright (C) 2010 Alessandro Rubini
4 * Copyright (C) 2010 Linus Walleij for ST-Ericsson
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#include <linux/init.h>
11#include <linux/interrupt.h>
12#include <linux/irq.h>
13#include <linux/io.h>
14#include <linux/clockchips.h>
15#include <linux/clocksource.h>
16#include <linux/clk.h>
17#include <linux/jiffies.h>
18#include <linux/err.h>
19#include <linux/platform_data/clocksource-nomadik-mtu.h>
20#include <asm/mach/time.h>
21#include <asm/sched_clock.h>
22
23/*
24 * The MTU device hosts four different counters, with 4 set of
25 * registers. These are register names.
26 */
27
28#define MTU_IMSC 0x00 /* Interrupt mask set/clear */
29#define MTU_RIS 0x04 /* Raw interrupt status */
30#define MTU_MIS 0x08 /* Masked interrupt status */
31#define MTU_ICR 0x0C /* Interrupt clear register */
32
33/* per-timer registers take 0..3 as argument */
34#define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */
35#define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */
36#define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */
37#define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
38
39/* bits for the control register */
40#define MTU_CRn_ENA 0x80
41#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
42#define MTU_CRn_PRESCALE_MASK 0x0c
43#define MTU_CRn_PRESCALE_1 0x00
44#define MTU_CRn_PRESCALE_16 0x04
45#define MTU_CRn_PRESCALE_256 0x08
46#define MTU_CRn_32BITS 0x02
47#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
48
49/* Other registers are usual amba/primecell registers, currently not used */
50#define MTU_ITCR 0xff0
51#define MTU_ITOP 0xff4
52
53#define MTU_PERIPH_ID0 0xfe0
54#define MTU_PERIPH_ID1 0xfe4
55#define MTU_PERIPH_ID2 0xfe8
56#define MTU_PERIPH_ID3 0xfeC
57
58#define MTU_PCELL0 0xff0
59#define MTU_PCELL1 0xff4
60#define MTU_PCELL2 0xff8
61#define MTU_PCELL3 0xffC
62
63static void __iomem *mtu_base;
64static bool clkevt_periodic;
65static u32 clk_prescale;
66static u32 nmdk_cycle; /* write-once */
67
68#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK
69/*
70 * Override the global weak sched_clock symbol with this
71 * local implementation which uses the clocksource to get some
72 * better resolution when scheduling the kernel.
73 */
74static u32 notrace nomadik_read_sched_clock(void)
75{
76 if (unlikely(!mtu_base))
77 return 0;
78
79 return -readl(mtu_base + MTU_VAL(0));
80}
81#endif
82
83/* Clockevent device: use one-shot mode */
84static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
85{
86 writel(1 << 1, mtu_base + MTU_IMSC);
87 writel(evt, mtu_base + MTU_LR(1));
88 /* Load highest value, enable device, enable interrupts */
89 writel(MTU_CRn_ONESHOT | clk_prescale |
90 MTU_CRn_32BITS | MTU_CRn_ENA,
91 mtu_base + MTU_CR(1));
92
93 return 0;
94}
95
96void nmdk_clkevt_reset(void)
97{
98 if (clkevt_periodic) {
99 /* Timer: configure load and background-load, and fire it up */
100 writel(nmdk_cycle, mtu_base + MTU_LR(1));
101 writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
102
103 writel(MTU_CRn_PERIODIC | clk_prescale |
104 MTU_CRn_32BITS | MTU_CRn_ENA,
105 mtu_base + MTU_CR(1));
106 writel(1 << 1, mtu_base + MTU_IMSC);
107 } else {
108 /* Generate an interrupt to start the clockevent again */
109 (void) nmdk_clkevt_next(nmdk_cycle, NULL);
110 }
111}
112
113static void nmdk_clkevt_mode(enum clock_event_mode mode,
114 struct clock_event_device *dev)
115{
116 switch (mode) {
117 case CLOCK_EVT_MODE_PERIODIC:
118 clkevt_periodic = true;
119 nmdk_clkevt_reset();
120 break;
121 case CLOCK_EVT_MODE_ONESHOT:
122 clkevt_periodic = false;
123 break;
124 case CLOCK_EVT_MODE_SHUTDOWN:
125 case CLOCK_EVT_MODE_UNUSED:
126 writel(0, mtu_base + MTU_IMSC);
127 /* disable timer */
128 writel(0, mtu_base + MTU_CR(1));
129 /* load some high default value */
130 writel(0xffffffff, mtu_base + MTU_LR(1));
131 break;
132 case CLOCK_EVT_MODE_RESUME:
133 break;
134 }
135}
136
137static struct clock_event_device nmdk_clkevt = {
138 .name = "mtu_1",
139 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
140 .rating = 200,
141 .set_mode = nmdk_clkevt_mode,
142 .set_next_event = nmdk_clkevt_next,
143};
144
145/*
146 * IRQ Handler for timer 1 of the MTU block.
147 */
148static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
149{
150 struct clock_event_device *evdev = dev_id;
151
152 writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
153 evdev->event_handler(evdev);
154 return IRQ_HANDLED;
155}
156
157static struct irqaction nmdk_timer_irq = {
158 .name = "Nomadik Timer Tick",
159 .flags = IRQF_DISABLED | IRQF_TIMER,
160 .handler = nmdk_timer_interrupt,
161 .dev_id = &nmdk_clkevt,
162};
163
164void nmdk_clksrc_reset(void)
165{
166 /* Disable */
167 writel(0, mtu_base + MTU_CR(0));
168
169 /* ClockSource: configure load and background-load, and fire it up */
170 writel(nmdk_cycle, mtu_base + MTU_LR(0));
171 writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
172
173 writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
174 mtu_base + MTU_CR(0));
175}
176
177void __init nmdk_timer_init(void __iomem *base, int irq)
178{
179 unsigned long rate;
180 struct clk *clk0, *pclk0;
181
182 mtu_base = base;
183
184 pclk0 = clk_get_sys("mtu0", "apb_pclk");
185 BUG_ON(IS_ERR(pclk0));
186 BUG_ON(clk_prepare(pclk0) < 0);
187 BUG_ON(clk_enable(pclk0) < 0);
188
189 clk0 = clk_get_sys("mtu0", NULL);
190 BUG_ON(IS_ERR(clk0));
191 BUG_ON(clk_prepare(clk0) < 0);
192 BUG_ON(clk_enable(clk0) < 0);
193
194 /*
195 * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
196 * for ux500.
197 * Use a divide-by-16 counter if the tick rate is more than 32MHz.
198 * At 32 MHz, the timer (with 32 bit counter) can be programmed
199 * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
200 * with 16 gives too low timer resolution.
201 */
202 rate = clk_get_rate(clk0);
203 if (rate > 32000000) {
204 rate /= 16;
205 clk_prescale = MTU_CRn_PRESCALE_16;
206 } else {
207 clk_prescale = MTU_CRn_PRESCALE_1;
208 }
209
210 /* Cycles for periodic mode */
211 nmdk_cycle = DIV_ROUND_CLOSEST(rate, HZ);
212
213
214 /* Timer 0 is the free running clocksource */
215 nmdk_clksrc_reset();
216
217 if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
218 rate, 200, 32, clocksource_mmio_readl_down))
219 pr_err("timer: failed to initialize clock source %s\n",
220 "mtu_0");
221
222#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK
223 setup_sched_clock(nomadik_read_sched_clock, 32, rate);
224#endif
225
226 /* Timer 1 is used for events, register irq and clockevents */
227 setup_irq(irq, &nmdk_timer_irq);
228 nmdk_clkevt.cpumask = cpumask_of(0);
229 clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU);
230}