aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorJingchang Lu <b35083@freescale.com>2013-05-29 04:12:17 -0400
committerDaniel Lezcano <daniel.lezcano@linaro.org>2013-06-06 11:23:13 -0400
commitc19672492d233e0012b60fbfa460ffac1381ee26 (patch)
tree8fac5511bf40ca83b4c9fe3830b6a305a770cc48 /drivers/clocksource
parent1a33bd2be705cbb3f57d7223b60baea441039307 (diff)
clocksource: Add Freescale Vybrid pit timer support
Add Freescale Vybrid Family period interrupt timer support. Signed-off-by: Jingchang Lu <b35083@freescale.com> Reviewed-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig5
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/vf_pit_timer.c194
3 files changed, 200 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index f151c6cf27c3..0a04257edf65 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -85,3 +85,8 @@ config CLKSRC_SAMSUNG_PWM
85 Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver 85 Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
86 for all devicetree enabled platforms. This driver will be 86 for all devicetree enabled platforms. This driver will be
87 needed only on systems that do not have the Exynos MCT available. 87 needed only on systems that do not have the Exynos MCT available.
88
89config VF_PIT_TIMER
90 bool
91 help
92 Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8d979c72aa94..36a9ac105d55 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o
26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o 26obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o 27obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
28obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o 28obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
29obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
29 30
30obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o 31obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
31obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o 32obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c
new file mode 100644
index 000000000000..598399d57fc5
--- /dev/null
+++ b/drivers/clocksource/vf_pit_timer.c
@@ -0,0 +1,194 @@
1/*
2 * Copyright 2012-2013 Freescale Semiconductor, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 */
9
10#include <linux/interrupt.h>
11#include <linux/clockchips.h>
12#include <linux/clk.h>
13#include <linux/of_address.h>
14#include <linux/of_irq.h>
15#include <asm/sched_clock.h>
16
17/*
18 * Each pit takes 0x10 Bytes register space
19 */
20#define PITMCR 0x00
21#define PIT0_OFFSET 0x100
22#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n))
23#define PITLDVAL 0x00
24#define PITCVAL 0x04
25#define PITTCTRL 0x08
26#define PITTFLG 0x0c
27
28#define PITMCR_MDIS (0x1 << 1)
29
30#define PITTCTRL_TEN (0x1 << 0)
31#define PITTCTRL_TIE (0x1 << 1)
32#define PITCTRL_CHN (0x1 << 2)
33
34#define PITTFLG_TIF 0x1
35
36static void __iomem *clksrc_base;
37static void __iomem *clkevt_base;
38static unsigned long cycle_per_jiffy;
39
40static inline void pit_timer_enable(void)
41{
42 __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL);
43}
44
45static inline void pit_timer_disable(void)
46{
47 __raw_writel(0, clkevt_base + PITTCTRL);
48}
49
50static inline void pit_irq_acknowledge(void)
51{
52 __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG);
53}
54
55static unsigned int pit_read_sched_clock(void)
56{
57 return __raw_readl(clksrc_base + PITCVAL);
58}
59
60static int __init pit_clocksource_init(unsigned long rate)
61{
62 /* set the max load value and start the clock source counter */
63 __raw_writel(0, clksrc_base + PITTCTRL);
64 __raw_writel(~0UL, clksrc_base + PITLDVAL);
65 __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL);
66
67 setup_sched_clock(pit_read_sched_clock, 32, rate);
68 return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate,
69 300, 32, clocksource_mmio_readl_down);
70}
71
72static int pit_set_next_event(unsigned long delta,
73 struct clock_event_device *unused)
74{
75 /*
76 * set a new value to PITLDVAL register will not restart the timer,
77 * to abort the current cycle and start a timer period with the new
78 * value, the timer must be disabled and enabled again.
79 * and the PITLAVAL should be set to delta minus one according to pit
80 * hardware requirement.
81 */
82 pit_timer_disable();
83 __raw_writel(delta - 1, clkevt_base + PITLDVAL);
84 pit_timer_enable();
85
86 return 0;
87}
88
89static void pit_set_mode(enum clock_event_mode mode,
90 struct clock_event_device *evt)
91{
92 switch (mode) {
93 case CLOCK_EVT_MODE_PERIODIC:
94 pit_set_next_event(cycle_per_jiffy, evt);
95 break;
96 default:
97 break;
98 }
99}
100
101static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
102{
103 struct clock_event_device *evt = dev_id;
104
105 pit_irq_acknowledge();
106
107 /*
108 * pit hardware doesn't support oneshot, it will generate an interrupt
109 * and reload the counter value from PITLDVAL when PITCVAL reach zero,
110 * and start the counter again. So software need to disable the timer
111 * to stop the counter loop in ONESHOT mode.
112 */
113 if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT))
114 pit_timer_disable();
115
116 evt->event_handler(evt);
117
118 return IRQ_HANDLED;
119}
120
121static struct clock_event_device clockevent_pit = {
122 .name = "VF pit timer",
123 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
124 .set_mode = pit_set_mode,
125 .set_next_event = pit_set_next_event,
126 .rating = 300,
127};
128
129static struct irqaction pit_timer_irq = {
130 .name = "VF pit timer",
131 .flags = IRQF_TIMER | IRQF_IRQPOLL,
132 .handler = pit_timer_interrupt,
133 .dev_id = &clockevent_pit,
134};
135
136static int __init pit_clockevent_init(unsigned long rate, int irq)
137{
138 __raw_writel(0, clkevt_base + PITTCTRL);
139 __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG);
140
141 BUG_ON(setup_irq(irq, &pit_timer_irq));
142
143 clockevent_pit.cpumask = cpumask_of(0);
144 clockevent_pit.irq = irq;
145 /*
146 * The value for the LDVAL register trigger is calculated as:
147 * LDVAL trigger = (period / clock period) - 1
148 * The pit is a 32-bit down count timer, when the conter value
149 * reaches 0, it will generate an interrupt, thus the minimal
150 * LDVAL trigger value is 1. And then the min_delta is
151 * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit.
152 */
153 clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff);
154
155 return 0;
156}
157
158static void __init pit_timer_init(struct device_node *np)
159{
160 struct clk *pit_clk;
161 void __iomem *timer_base;
162 unsigned long clk_rate;
163 int irq;
164
165 timer_base = of_iomap(np, 0);
166 BUG_ON(!timer_base);
167
168 /*
169 * PIT0 and PIT1 can be chained to build a 64-bit timer,
170 * so choose PIT2 as clocksource, PIT3 as clockevent device,
171 * and leave PIT0 and PIT1 unused for anyone else who needs them.
172 */
173 clksrc_base = timer_base + PITn_OFFSET(2);
174 clkevt_base = timer_base + PITn_OFFSET(3);
175
176 irq = irq_of_parse_and_map(np, 0);
177 BUG_ON(irq <= 0);
178
179 pit_clk = of_clk_get(np, 0);
180 BUG_ON(IS_ERR(pit_clk));
181
182 BUG_ON(clk_prepare_enable(pit_clk));
183
184 clk_rate = clk_get_rate(pit_clk);
185 cycle_per_jiffy = clk_rate / (HZ);
186
187 /* enable the pit module */
188 __raw_writel(~PITMCR_MDIS, timer_base + PITMCR);
189
190 BUG_ON(pit_clocksource_init(clk_rate));
191
192 pit_clockevent_init(clk_rate, irq);
193}
194CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);