aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-12 15:05:15 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-12 15:05:15 -0500
commitd027db132b395dabfac208e52a7e510e441bb9d2 (patch)
tree24b055b2385f9848e77e646ce475991d8675c3c4 /drivers/clocksource
parentd01e4afdbb65e030fd6f1f96c30a558e2eb0f279 (diff)
parent5faf7cbb848da827f6ea1458b5a1c26a44e7510a (diff)
Merge tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC updates from Olof Johansson: "This contains the bulk of new SoC development for this merge window. Two new platforms have been added, the sunxi platforms (Allwinner A1x SoCs) by Maxime Ripard, and a generic Broadcom platform for a new series of ARMv7 platforms from them, where the hope is that we can keep the platform code generic enough to have them all share one mach directory. The new Broadcom platform is contributed by Christian Daudt. Highbank has grown support for Calxeda's next generation of hardware, ECX-2000. clps711x has seen a lot of cleanup from Alexander Shiyan, and he's also taken on maintainership of the platform. Beyond this there has been a bunch of work from a number of people on converting more platforms to IRQ domains, pinctrl conversion, cleanup and general feature enablement across most of the active platforms." Fix up trivial conflicts as per Olof. * tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (174 commits) mfd: vexpress-sysreg: Remove LEDs code irqchip: irq-sunxi: Add terminating entry for sunxi_irq_dt_ids clocksource: sunxi_timer: Add terminating entry for sunxi_timer_dt_ids irq: versatile: delete dangling variable ARM: sunxi: add missing include for mdelay() ARM: EXYNOS: Avoid early use of of_machine_is_compatible() ARM: dts: add node for PL330 MDMA1 controller for exynos4 ARM: EXYNOS: Add support for secondary CPU bring-up on Exynos4412 ARM: EXYNOS: add UART3 to DEBUG_LL ports ARM: S3C24XX: Add clkdev entry for camif-upll clock ARM: SAMSUNG: Add s3c24xx/s3c64xx CAMIF GPIO setup helpers ARM: sunxi: Add missing sun4i.dtsi file pinctrl: samsung: Do not initialise statics to 0 ARM i.MX6: remove gate_mask from pllv3 ARM i.MX6: Fix ethernet PLL clocks ARM i.MX6: rename PLLs according to datasheet ARM i.MX6: Add pwm support ARM i.MX51: Add pwm support ARM i.MX53: Add pwm support ARM: mx5: Replace clk_register_clkdev with clock DT lookup ...
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig3
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/sunxi_timer.c171
3 files changed, 175 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 6a78073c3808..a0985732f1e2 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -22,6 +22,9 @@ config DW_APB_TIMER_OF
22config ARMADA_370_XP_TIMER 22config ARMADA_370_XP_TIMER
23 bool 23 bool
24 24
25config SUNXI_TIMER
26 bool
27
25config CLKSRC_DBX500_PRCMU 28config CLKSRC_DBX500_PRCMU
26 bool "Clocksource PRCMU Timer" 29 bool "Clocksource PRCMU Timer"
27 depends on UX500_SOC_DB8500 30 depends on UX500_SOC_DB8500
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 603be366f762..36f06de4c5ab 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
14obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o 14obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
15obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o 15obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
16obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o 16obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
17obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o
17 18
18obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o 19obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o
diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c
new file mode 100644
index 000000000000..3cd1bd3d7aee
--- /dev/null
+++ b/drivers/clocksource/sunxi_timer.c
@@ -0,0 +1,171 @@
1/*
2 * Allwinner A1X SoCs timer handling.
3 *
4 * Copyright (C) 2012 Maxime Ripard
5 *
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * Based on code from
9 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
10 * Benn Huang <benn@allwinnertech.com>
11 *
12 * This file is licensed under the terms of the GNU General Public
13 * License version 2. This program is licensed "as is" without any
14 * warranty of any kind, whether express or implied.
15 */
16
17#include <linux/clk.h>
18#include <linux/clockchips.h>
19#include <linux/interrupt.h>
20#include <linux/irq.h>
21#include <linux/irqreturn.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/of_irq.h>
25#include <linux/sunxi_timer.h>
26#include <linux/clk/sunxi.h>
27
28#define TIMER_CTL_REG 0x00
29#define TIMER_CTL_ENABLE (1 << 0)
30#define TIMER_IRQ_ST_REG 0x04
31#define TIMER0_CTL_REG 0x10
32#define TIMER0_CTL_ENABLE (1 << 0)
33#define TIMER0_CTL_AUTORELOAD (1 << 1)
34#define TIMER0_CTL_ONESHOT (1 << 7)
35#define TIMER0_INTVAL_REG 0x14
36#define TIMER0_CNTVAL_REG 0x18
37
38#define TIMER_SCAL 16
39
40static void __iomem *timer_base;
41
42static void sunxi_clkevt_mode(enum clock_event_mode mode,
43 struct clock_event_device *clk)
44{
45 u32 u = readl(timer_base + TIMER0_CTL_REG);
46
47 switch (mode) {
48 case CLOCK_EVT_MODE_PERIODIC:
49 u &= ~(TIMER0_CTL_ONESHOT);
50 writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG);
51 break;
52
53 case CLOCK_EVT_MODE_ONESHOT:
54 writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG);
55 break;
56 case CLOCK_EVT_MODE_UNUSED:
57 case CLOCK_EVT_MODE_SHUTDOWN:
58 default:
59 writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG);
60 break;
61 }
62}
63
64static int sunxi_clkevt_next_event(unsigned long evt,
65 struct clock_event_device *unused)
66{
67 u32 u = readl(timer_base + TIMER0_CTL_REG);
68 writel(evt, timer_base + TIMER0_CNTVAL_REG);
69 writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD,
70 timer_base + TIMER0_CTL_REG);
71
72 return 0;
73}
74
75static struct clock_event_device sunxi_clockevent = {
76 .name = "sunxi_tick",
77 .shift = 32,
78 .rating = 300,
79 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
80 .set_mode = sunxi_clkevt_mode,
81 .set_next_event = sunxi_clkevt_next_event,
82};
83
84
85static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id)
86{
87 struct clock_event_device *evt = (struct clock_event_device *)dev_id;
88
89 writel(0x1, timer_base + TIMER_IRQ_ST_REG);
90 evt->event_handler(evt);
91
92 return IRQ_HANDLED;
93}
94
95static struct irqaction sunxi_timer_irq = {
96 .name = "sunxi_timer0",
97 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
98 .handler = sunxi_timer_interrupt,
99 .dev_id = &sunxi_clockevent,
100};
101
102static struct of_device_id sunxi_timer_dt_ids[] = {
103 { .compatible = "allwinner,sunxi-timer" },
104 { }
105};
106
107static void __init sunxi_timer_init(void)
108{
109 struct device_node *node;
110 unsigned long rate = 0;
111 struct clk *clk;
112 int ret, irq;
113 u32 val;
114
115 node = of_find_matching_node(NULL, sunxi_timer_dt_ids);
116 if (!node)
117 panic("No sunxi timer node");
118
119 timer_base = of_iomap(node, 0);
120 if (!timer_base)
121 panic("Can't map registers");
122
123 irq = irq_of_parse_and_map(node, 0);
124 if (irq <= 0)
125 panic("Can't parse IRQ");
126
127 sunxi_init_clocks();
128
129 clk = of_clk_get(node, 0);
130 if (IS_ERR(clk))
131 panic("Can't get timer clock");
132
133 rate = clk_get_rate(clk);
134
135 writel(rate / (TIMER_SCAL * HZ),
136 timer_base + TIMER0_INTVAL_REG);
137
138 /* set clock source to HOSC, 16 pre-division */
139 val = readl(timer_base + TIMER0_CTL_REG);
140 val &= ~(0x07 << 4);
141 val &= ~(0x03 << 2);
142 val |= (4 << 4) | (1 << 2);
143 writel(val, timer_base + TIMER0_CTL_REG);
144
145 /* set mode to auto reload */
146 val = readl(timer_base + TIMER0_CTL_REG);
147 writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG);
148
149 ret = setup_irq(irq, &sunxi_timer_irq);
150 if (ret)
151 pr_warn("failed to setup irq %d\n", irq);
152
153 /* Enable timer0 interrupt */
154 val = readl(timer_base + TIMER_CTL_REG);
155 writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG);
156
157 sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL,
158 NSEC_PER_SEC,
159 sunxi_clockevent.shift);
160 sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff,
161 &sunxi_clockevent);
162 sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1,
163 &sunxi_clockevent);
164 sunxi_clockevent.cpumask = cpumask_of(0);
165
166 clockevents_register_device(&sunxi_clockevent);
167}
168
169struct sys_timer sunxi_timer = {
170 .init = sunxi_timer_init,
171};