Commit | Line | Data |
---|---|---|
07862c1c JJ |
1 | /* |
2 | * MOXA ART SoCs timer handling. | |
3 | * | |
4 | * Copyright (C) 2013 Jonas Jensen | |
5 | * | |
6 | * Jonas Jensen <jonas.jensen@gmail.com> | |
7 | * | |
8 | * This file is licensed under the terms of the GNU General Public | |
9 | * License version 2. This program is licensed "as is" without any | |
10 | * warranty of any kind, whether express or implied. | |
11 | */ | |
12 | ||
13 | #include <linux/clk.h> | |
14 | #include <linux/clockchips.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/irq.h> | |
17 | #include <linux/irqreturn.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/of_address.h> | |
20 | #include <linux/of_irq.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/clocksource.h> | |
adf157eb | 23 | #include <linux/bitops.h> |
82fdd070 | 24 | #include <linux/slab.h> |
07862c1c JJ |
25 | |
26 | #define TIMER1_BASE 0x00 | |
27 | #define TIMER2_BASE 0x10 | |
28 | #define TIMER3_BASE 0x20 | |
29 | ||
30 | #define REG_COUNT 0x0 /* writable */ | |
31 | #define REG_LOAD 0x4 | |
32 | #define REG_MATCH1 0x8 | |
33 | #define REG_MATCH2 0xC | |
34 | ||
35 | #define TIMER_CR 0x30 | |
36 | #define TIMER_INTR_STATE 0x34 | |
37 | #define TIMER_INTR_MASK 0x38 | |
38 | ||
39 | /* | |
82fdd070 | 40 | * Moxart TIMER_CR flags: |
07862c1c | 41 | * |
82fdd070 JS |
42 | * MOXART_CR_*_CLOCK 0: PCLK, 1: EXT1CLK |
43 | * MOXART_CR_*_INT overflow interrupt enable bit | |
07862c1c | 44 | */ |
82fdd070 JS |
45 | #define MOXART_CR_1_ENABLE BIT(0) |
46 | #define MOXART_CR_1_CLOCK BIT(1) | |
47 | #define MOXART_CR_1_INT BIT(2) | |
48 | #define MOXART_CR_2_ENABLE BIT(3) | |
49 | #define MOXART_CR_2_CLOCK BIT(4) | |
50 | #define MOXART_CR_2_INT BIT(5) | |
51 | #define MOXART_CR_3_ENABLE BIT(6) | |
52 | #define MOXART_CR_3_CLOCK BIT(7) | |
53 | #define MOXART_CR_3_INT BIT(8) | |
54 | #define MOXART_CR_COUNT_UP BIT(9) | |
55 | ||
56 | #define MOXART_TIMER1_ENABLE (MOXART_CR_2_ENABLE | MOXART_CR_1_ENABLE) | |
57 | #define MOXART_TIMER1_DISABLE (MOXART_CR_2_ENABLE) | |
58 | ||
ba36d53d JS |
59 | /* |
60 | * The ASpeed variant of the IP block has a different layout | |
61 | * for the control register | |
62 | */ | |
63 | #define ASPEED_CR_1_ENABLE BIT(0) | |
64 | #define ASPEED_CR_1_CLOCK BIT(1) | |
65 | #define ASPEED_CR_1_INT BIT(2) | |
66 | #define ASPEED_CR_2_ENABLE BIT(4) | |
67 | #define ASPEED_CR_2_CLOCK BIT(5) | |
68 | #define ASPEED_CR_2_INT BIT(6) | |
69 | #define ASPEED_CR_3_ENABLE BIT(8) | |
70 | #define ASPEED_CR_3_CLOCK BIT(9) | |
71 | #define ASPEED_CR_3_INT BIT(10) | |
72 | ||
73 | #define ASPEED_TIMER1_ENABLE (ASPEED_CR_2_ENABLE | ASPEED_CR_1_ENABLE) | |
74 | #define ASPEED_TIMER1_DISABLE (ASPEED_CR_2_ENABLE) | |
75 | ||
82fdd070 JS |
76 | struct moxart_timer { |
77 | void __iomem *base; | |
78 | unsigned int t1_disable_val; | |
79 | unsigned int t1_enable_val; | |
80 | unsigned int count_per_tick; | |
81 | struct clock_event_device clkevt; | |
82fdd070 JS |
82 | }; |
83 | ||
84 | static inline struct moxart_timer *to_moxart(struct clock_event_device *evt) | |
85 | { | |
86 | return container_of(evt, struct moxart_timer, clkevt); | |
87 | } | |
07862c1c | 88 | |
70164742 | 89 | static inline void moxart_disable(struct clock_event_device *evt) |
07862c1c | 90 | { |
82fdd070 JS |
91 | struct moxart_timer *timer = to_moxart(evt); |
92 | ||
93 | writel(timer->t1_disable_val, timer->base + TIMER_CR); | |
70164742 JS |
94 | } |
95 | ||
96 | static inline void moxart_enable(struct clock_event_device *evt) | |
97 | { | |
82fdd070 JS |
98 | struct moxart_timer *timer = to_moxart(evt); |
99 | ||
100 | writel(timer->t1_enable_val, timer->base + TIMER_CR); | |
70164742 JS |
101 | } |
102 | ||
103 | static int moxart_shutdown(struct clock_event_device *evt) | |
104 | { | |
105 | moxart_disable(evt); | |
37ae2471 VK |
106 | return 0; |
107 | } | |
108 | ||
109 | static int moxart_set_oneshot(struct clock_event_device *evt) | |
110 | { | |
70164742 | 111 | moxart_disable(evt); |
82fdd070 | 112 | writel(~0, to_moxart(evt)->base + TIMER1_BASE + REG_LOAD); |
37ae2471 VK |
113 | return 0; |
114 | } | |
115 | ||
116 | static int moxart_set_periodic(struct clock_event_device *evt) | |
117 | { | |
82fdd070 JS |
118 | struct moxart_timer *timer = to_moxart(evt); |
119 | ||
120 | moxart_disable(evt); | |
121 | writel(timer->count_per_tick, timer->base + TIMER1_BASE + REG_LOAD); | |
122 | writel(0, timer->base + TIMER1_BASE + REG_MATCH1); | |
70164742 | 123 | moxart_enable(evt); |
37ae2471 | 124 | return 0; |
07862c1c JJ |
125 | } |
126 | ||
127 | static int moxart_clkevt_next_event(unsigned long cycles, | |
70164742 | 128 | struct clock_event_device *evt) |
07862c1c | 129 | { |
82fdd070 | 130 | struct moxart_timer *timer = to_moxart(evt); |
07862c1c JJ |
131 | u32 u; |
132 | ||
70164742 | 133 | moxart_disable(evt); |
07862c1c | 134 | |
82fdd070 JS |
135 | u = readl(timer->base + TIMER1_BASE + REG_COUNT) - cycles; |
136 | writel(u, timer->base + TIMER1_BASE + REG_MATCH1); | |
07862c1c | 137 | |
70164742 | 138 | moxart_enable(evt); |
07862c1c JJ |
139 | |
140 | return 0; | |
141 | } | |
142 | ||
07862c1c JJ |
143 | static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id) |
144 | { | |
145 | struct clock_event_device *evt = dev_id; | |
146 | evt->event_handler(evt); | |
147 | return IRQ_HANDLED; | |
148 | } | |
149 | ||
b7357e65 | 150 | static int __init moxart_timer_init(struct device_node *node) |
07862c1c JJ |
151 | { |
152 | int ret, irq; | |
153 | unsigned long pclk; | |
154 | struct clk *clk; | |
82fdd070 | 155 | struct moxart_timer *timer; |
07862c1c | 156 | |
82fdd070 JS |
157 | timer = kzalloc(sizeof(*timer), GFP_KERNEL); |
158 | if (!timer) | |
159 | return -ENOMEM; | |
160 | ||
161 | timer->base = of_iomap(node, 0); | |
162 | if (!timer->base) { | |
b7357e65 | 163 | pr_err("%s: of_iomap failed\n", node->full_name); |
c9435f35 SM |
164 | ret = -ENXIO; |
165 | goto out_free; | |
b7357e65 | 166 | } |
07862c1c JJ |
167 | |
168 | irq = irq_of_parse_and_map(node, 0); | |
b7357e65 DL |
169 | if (irq <= 0) { |
170 | pr_err("%s: irq_of_parse_and_map failed\n", node->full_name); | |
c9435f35 SM |
171 | ret = -EINVAL; |
172 | goto out_unmap; | |
b7357e65 | 173 | } |
07862c1c | 174 | |
07862c1c | 175 | clk = of_clk_get(node, 0); |
b7357e65 DL |
176 | if (IS_ERR(clk)) { |
177 | pr_err("%s: of_clk_get failed\n", node->full_name); | |
c9435f35 SM |
178 | ret = PTR_ERR(clk); |
179 | goto out_unmap; | |
b7357e65 | 180 | } |
07862c1c JJ |
181 | |
182 | pclk = clk_get_rate(clk); | |
183 | ||
82fdd070 JS |
184 | if (of_device_is_compatible(node, "moxa,moxart-timer")) { |
185 | timer->t1_enable_val = MOXART_TIMER1_ENABLE; | |
186 | timer->t1_disable_val = MOXART_TIMER1_DISABLE; | |
ba36d53d JS |
187 | } else if (of_device_is_compatible(node, "aspeed,ast2400-timer")) { |
188 | timer->t1_enable_val = ASPEED_TIMER1_ENABLE; | |
189 | timer->t1_disable_val = ASPEED_TIMER1_DISABLE; | |
e2a2d385 DL |
190 | } else { |
191 | pr_err("%s: unknown platform\n", node->full_name); | |
c9435f35 SM |
192 | ret = -EINVAL; |
193 | goto out_unmap; | |
e2a2d385 | 194 | } |
82fdd070 JS |
195 | |
196 | timer->count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ); | |
197 | ||
198 | timer->clkevt.name = node->name; | |
199 | timer->clkevt.rating = 200; | |
200 | timer->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | | |
201 | CLOCK_EVT_FEAT_ONESHOT; | |
202 | timer->clkevt.set_state_shutdown = moxart_shutdown; | |
203 | timer->clkevt.set_state_periodic = moxart_set_periodic; | |
204 | timer->clkevt.set_state_oneshot = moxart_set_oneshot; | |
205 | timer->clkevt.tick_resume = moxart_set_oneshot; | |
206 | timer->clkevt.set_next_event = moxart_clkevt_next_event; | |
207 | timer->clkevt.cpumask = cpumask_of(0); | |
208 | timer->clkevt.irq = irq; | |
82fdd070 JS |
209 | |
210 | ret = clocksource_mmio_init(timer->base + TIMER2_BASE + REG_COUNT, | |
b7357e65 DL |
211 | "moxart_timer", pclk, 200, 32, |
212 | clocksource_mmio_readl_down); | |
213 | if (ret) { | |
214 | pr_err("%s: clocksource_mmio_init failed\n", node->full_name); | |
c9435f35 | 215 | goto out_unmap; |
b7357e65 | 216 | } |
07862c1c | 217 | |
cf1e929c DL |
218 | ret = request_irq(irq, moxart_timer_interrupt, IRQF_TIMER, |
219 | node->name, &timer->clkevt); | |
82fdd070 JS |
220 | if (ret) { |
221 | pr_err("%s: setup_irq failed\n", node->full_name); | |
c9435f35 | 222 | goto out_unmap; |
82fdd070 | 223 | } |
07862c1c | 224 | |
ba36d53d JS |
225 | /* Clear match registers */ |
226 | writel(0, timer->base + TIMER1_BASE + REG_MATCH1); | |
227 | writel(0, timer->base + TIMER1_BASE + REG_MATCH2); | |
228 | writel(0, timer->base + TIMER2_BASE + REG_MATCH1); | |
229 | writel(0, timer->base + TIMER2_BASE + REG_MATCH2); | |
230 | ||
231 | /* | |
232 | * Start timer 2 rolling as our main wall clock source, keep timer 1 | |
233 | * disabled | |
234 | */ | |
235 | writel(0, timer->base + TIMER_CR); | |
82fdd070 JS |
236 | writel(~0, timer->base + TIMER2_BASE + REG_LOAD); |
237 | writel(timer->t1_disable_val, timer->base + TIMER_CR); | |
07862c1c JJ |
238 | |
239 | /* | |
240 | * documentation is not publicly available: | |
241 | * min_delta / max_delta obtained by trial-and-error, | |
242 | * max_delta 0xfffffffe should be ok because count | |
243 | * register size is u32 | |
244 | */ | |
82fdd070 | 245 | clockevents_config_and_register(&timer->clkevt, pclk, 0x4, 0xfffffffe); |
b7357e65 DL |
246 | |
247 | return 0; | |
c9435f35 SM |
248 | |
249 | out_unmap: | |
250 | iounmap(timer->base); | |
251 | out_free: | |
252 | kfree(timer); | |
253 | return ret; | |
07862c1c | 254 | } |
177cf6e5 | 255 | CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init); |
ba36d53d | 256 | CLOCKSOURCE_OF_DECLARE(aspeed, "aspeed,ast2400-timer", moxart_timer_init); |