aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt17
-rw-r--r--drivers/clocksource/Kconfig3
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/sunxi_timer.c170
-rw-r--r--include/linux/sunxi_timer.h24
5 files changed, 215 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt b/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt
new file mode 100644
index 000000000000..0c7b64e95a61
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt
@@ -0,0 +1,17 @@
1Allwinner A1X SoCs Timer Controller
2
3Required properties:
4
5- compatible : should be "allwinner,sunxi-timer"
6- reg : Specifies base physical address and size of the registers.
7- interrupts : The interrupt of the first timer
8- clocks: phandle to the source clock (usually a 24 MHz fixed clock)
9
10Example:
11
12timer {
13 compatible = "allwinner,sunxi-timer";
14 reg = <0x01c20c00 0x400>;
15 interrupts = <22>;
16 clocks = <&osc>;
17};
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..3c46434b64cb
--- /dev/null
+++ b/drivers/clocksource/sunxi_timer.c
@@ -0,0 +1,170 @@
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
106static void __init sunxi_timer_init(void)
107{
108 struct device_node *node;
109 unsigned long rate = 0;
110 struct clk *clk;
111 int ret, irq;
112 u32 val;
113
114 node = of_find_matching_node(NULL, sunxi_timer_dt_ids);
115 if (!node)
116 panic("No sunxi timer node");
117
118 timer_base = of_iomap(node, 0);
119 if (!timer_base)
120 panic("Can't map registers");
121
122 irq = irq_of_parse_and_map(node, 0);
123 if (irq <= 0)
124 panic("Can't parse IRQ");
125
126 sunxi_init_clocks();
127
128 clk = of_clk_get(node, 0);
129 if (IS_ERR(clk))
130 panic("Can't get timer clock");
131
132 rate = clk_get_rate(clk);
133
134 writel(rate / (TIMER_SCAL * HZ),
135 timer_base + TIMER0_INTVAL_REG);
136
137 /* set clock source to HOSC, 16 pre-division */
138 val = readl(timer_base + TIMER0_CTL_REG);
139 val &= ~(0x07 << 4);
140 val &= ~(0x03 << 2);
141 val |= (4 << 4) | (1 << 2);
142 writel(val, timer_base + TIMER0_CTL_REG);
143
144 /* set mode to auto reload */
145 val = readl(timer_base + TIMER0_CTL_REG);
146 writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG);
147
148 ret = setup_irq(irq, &sunxi_timer_irq);
149 if (ret)
150 pr_warn("failed to setup irq %d\n", irq);
151
152 /* Enable timer0 interrupt */
153 val = readl(timer_base + TIMER_CTL_REG);
154 writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG);
155
156 sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL,
157 NSEC_PER_SEC,
158 sunxi_clockevent.shift);
159 sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff,
160 &sunxi_clockevent);
161 sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1,
162 &sunxi_clockevent);
163 sunxi_clockevent.cpumask = cpumask_of(0);
164
165 clockevents_register_device(&sunxi_clockevent);
166}
167
168struct sys_timer sunxi_timer = {
169 .init = sunxi_timer_init,
170};
diff --git a/include/linux/sunxi_timer.h b/include/linux/sunxi_timer.h
new file mode 100644
index 000000000000..b9165bba6e61
--- /dev/null
+++ b/include/linux/sunxi_timer.h
@@ -0,0 +1,24 @@
1/*
2 * Copyright 2012 Maxime Ripard
3 *
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
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 as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#ifndef __SUNXI_TIMER_H
18#define __SUNXI_TIMER_H
19
20#include <asm/mach/time.h>
21
22extern struct sys_timer sunxi_timer;
23
24#endif