2 * Allwinner A1X SoCs timer handling.
4 * Copyright (C) 2012 Maxime Ripard
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
9 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
10 * Benn Huang <benn@allwinnertech.com>
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.
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>
23 #include <linux/of_address.h>
24 #include <linux/of_irq.h>
25 #include <linux/sunxi_timer.h>
26 #include <linux/clk/sunxi.h>
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
40 static void __iomem
*timer_base
;
42 static void sunxi_clkevt_mode(enum clock_event_mode mode
,
43 struct clock_event_device
*clk
)
45 u32 u
= readl(timer_base
+ TIMER0_CTL_REG
);
48 case CLOCK_EVT_MODE_PERIODIC
:
49 u
&= ~(TIMER0_CTL_ONESHOT
);
50 writel(u
| TIMER0_CTL_ENABLE
, timer_base
+ TIMER0_CTL_REG
);
53 case CLOCK_EVT_MODE_ONESHOT
:
54 writel(u
| TIMER0_CTL_ONESHOT
, timer_base
+ TIMER0_CTL_REG
);
56 case CLOCK_EVT_MODE_UNUSED
:
57 case CLOCK_EVT_MODE_SHUTDOWN
:
59 writel(u
& ~(TIMER0_CTL_ENABLE
), timer_base
+ TIMER0_CTL_REG
);
64 static int sunxi_clkevt_next_event(unsigned long evt
,
65 struct clock_event_device
*unused
)
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
);
75 static struct clock_event_device sunxi_clockevent
= {
79 .features
= CLOCK_EVT_FEAT_PERIODIC
| CLOCK_EVT_FEAT_ONESHOT
,
80 .set_mode
= sunxi_clkevt_mode
,
81 .set_next_event
= sunxi_clkevt_next_event
,
85 static irqreturn_t
sunxi_timer_interrupt(int irq
, void *dev_id
)
87 struct clock_event_device
*evt
= (struct clock_event_device
*)dev_id
;
89 writel(0x1, timer_base
+ TIMER_IRQ_ST_REG
);
90 evt
->event_handler(evt
);
95 static 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
,
102 static struct of_device_id sunxi_timer_dt_ids
[] = {
103 { .compatible
= "allwinner,sunxi-timer" },
107 static void __init
sunxi_timer_init(void)
109 struct device_node
*node
;
110 unsigned long rate
= 0;
115 node
= of_find_matching_node(NULL
, sunxi_timer_dt_ids
);
117 panic("No sunxi timer node");
119 timer_base
= of_iomap(node
, 0);
121 panic("Can't map registers");
123 irq
= irq_of_parse_and_map(node
, 0);
125 panic("Can't parse IRQ");
129 clk
= of_clk_get(node
, 0);
131 panic("Can't get timer clock");
133 rate
= clk_get_rate(clk
);
135 writel(rate
/ (TIMER_SCAL
* HZ
),
136 timer_base
+ TIMER0_INTVAL_REG
);
138 /* set clock source to HOSC, 16 pre-division */
139 val
= readl(timer_base
+ TIMER0_CTL_REG
);
142 val
|= (4 << 4) | (1 << 2);
143 writel(val
, timer_base
+ TIMER0_CTL_REG
);
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
);
149 ret
= setup_irq(irq
, &sunxi_timer_irq
);
151 pr_warn("failed to setup irq %d\n", irq
);
153 /* Enable timer0 interrupt */
154 val
= readl(timer_base
+ TIMER_CTL_REG
);
155 writel(val
| TIMER_CTL_ENABLE
, timer_base
+ TIMER_CTL_REG
);
157 sunxi_clockevent
.mult
= div_sc(rate
/ TIMER_SCAL
,
159 sunxi_clockevent
.shift
);
160 sunxi_clockevent
.max_delta_ns
= clockevent_delta2ns(0xff,
162 sunxi_clockevent
.min_delta_ns
= clockevent_delta2ns(0x1,
164 sunxi_clockevent
.cpumask
= cpumask_of(0);
166 clockevents_register_device(&sunxi_clockevent
);
169 struct sys_timer sunxi_timer
= {
170 .init
= sunxi_timer_init
,