diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2017-05-18 16:17:01 -0400 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2017-06-12 04:14:00 -0400 |
commit | e7bad212ca0e6b1dcca1e0316ca8658c738c1206 (patch) | |
tree | 3d7b90061ad072196bbcc6c73439d18e9eccc40c /drivers/clocksource | |
parent | dd98442e17a66318b021d4342abf53069cc8bed1 (diff) |
clocksource/drivers/fttmr010: Use state container
This converts the Faraday FTTMR010 to use the state container
design pattern. Take some care to handle the state container
and free:ing of resources as has been done in the Moxa driver.
Cc: Joel Stanley <joel@jms.id.au>
Tested-by: Jonas Jensen <jonas.jensen@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/timer-fttmr010.c | 190 |
1 files changed, 116 insertions, 74 deletions
diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c index db097db346e3..9ad31489bbef 100644 --- a/drivers/clocksource/timer-fttmr010.c +++ b/drivers/clocksource/timer-fttmr010.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/clocksource.h> | 15 | #include <linux/clocksource.h> |
16 | #include <linux/sched_clock.h> | 16 | #include <linux/sched_clock.h> |
17 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
18 | #include <linux/slab.h> | ||
18 | 19 | ||
19 | /* | 20 | /* |
20 | * Register definitions for the timers | 21 | * Register definitions for the timers |
@@ -62,23 +63,35 @@ | |||
62 | #define TIMER_3_INT_OVERFLOW (1 << 8) | 63 | #define TIMER_3_INT_OVERFLOW (1 << 8) |
63 | #define TIMER_INT_ALL_MASK 0x1ff | 64 | #define TIMER_INT_ALL_MASK 0x1ff |
64 | 65 | ||
65 | static unsigned int tick_rate; | 66 | struct fttmr010 { |
66 | static void __iomem *base; | 67 | void __iomem *base; |
68 | unsigned int tick_rate; | ||
69 | struct clock_event_device clkevt; | ||
70 | }; | ||
71 | |||
72 | /* A local singleton used by sched_clock, which is stateless */ | ||
73 | static struct fttmr010 *local_fttmr; | ||
74 | |||
75 | static inline struct fttmr010 *to_fttmr010(struct clock_event_device *evt) | ||
76 | { | ||
77 | return container_of(evt, struct fttmr010, clkevt); | ||
78 | } | ||
67 | 79 | ||
68 | static u64 notrace fttmr010_read_sched_clock(void) | 80 | static u64 notrace fttmr010_read_sched_clock(void) |
69 | { | 81 | { |
70 | return readl(base + TIMER3_COUNT); | 82 | return readl(local_fttmr->base + TIMER3_COUNT); |
71 | } | 83 | } |
72 | 84 | ||
73 | static int fttmr010_timer_set_next_event(unsigned long cycles, | 85 | static int fttmr010_timer_set_next_event(unsigned long cycles, |
74 | struct clock_event_device *evt) | 86 | struct clock_event_device *evt) |
75 | { | 87 | { |
88 | struct fttmr010 *fttmr010 = to_fttmr010(evt); | ||
76 | u32 cr; | 89 | u32 cr; |
77 | 90 | ||
78 | /* Setup the match register */ | 91 | /* Setup the match register */ |
79 | cr = readl(base + TIMER1_COUNT); | 92 | cr = readl(fttmr010->base + TIMER1_COUNT); |
80 | writel(cr + cycles, base + TIMER1_MATCH1); | 93 | writel(cr + cycles, fttmr010->base + TIMER1_MATCH1); |
81 | if (readl(base + TIMER1_COUNT) - cr > cycles) | 94 | if (readl(fttmr010->base + TIMER1_COUNT) - cr > cycles) |
82 | return -ETIME; | 95 | return -ETIME; |
83 | 96 | ||
84 | return 0; | 97 | return 0; |
@@ -86,99 +99,90 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, | |||
86 | 99 | ||
87 | static int fttmr010_timer_shutdown(struct clock_event_device *evt) | 100 | static int fttmr010_timer_shutdown(struct clock_event_device *evt) |
88 | { | 101 | { |
102 | struct fttmr010 *fttmr010 = to_fttmr010(evt); | ||
103 | u32 cr; | ||
104 | |||
105 | /* Stop timer and interrupt. */ | ||
106 | cr = readl(fttmr010->base + TIMER_CR); | ||
107 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); | ||
108 | writel(cr, fttmr010->base + TIMER_CR); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) | ||
114 | { | ||
115 | struct fttmr010 *fttmr010 = to_fttmr010(evt); | ||
89 | u32 cr; | 116 | u32 cr; |
90 | 117 | ||
91 | /* | ||
92 | * Disable also for oneshot: the set_next() call will arm the timer | ||
93 | * instead. | ||
94 | */ | ||
95 | /* Stop timer and interrupt. */ | 118 | /* Stop timer and interrupt. */ |
96 | cr = readl(base + TIMER_CR); | 119 | cr = readl(fttmr010->base + TIMER_CR); |
97 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); | 120 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); |
98 | writel(cr, base + TIMER_CR); | 121 | writel(cr, fttmr010->base + TIMER_CR); |
99 | 122 | ||
100 | /* Setup counter start from 0 */ | 123 | /* Setup counter start from 0 */ |
101 | writel(0, base + TIMER1_COUNT); | 124 | writel(0, fttmr010->base + TIMER1_COUNT); |
102 | writel(0, base + TIMER1_LOAD); | 125 | writel(0, fttmr010->base + TIMER1_LOAD); |
103 | 126 | ||
104 | /* enable interrupt */ | 127 | /* Enable interrupt */ |
105 | cr = readl(base + TIMER_INTR_MASK); | 128 | cr = readl(fttmr010->base + TIMER_INTR_MASK); |
106 | cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2); | 129 | cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2); |
107 | cr |= TIMER_1_INT_MATCH1; | 130 | cr |= TIMER_1_INT_MATCH1; |
108 | writel(cr, base + TIMER_INTR_MASK); | 131 | writel(cr, fttmr010->base + TIMER_INTR_MASK); |
109 | 132 | ||
110 | /* start the timer */ | 133 | /* Start the timer */ |
111 | cr = readl(base + TIMER_CR); | 134 | cr = readl(fttmr010->base + TIMER_CR); |
112 | cr |= TIMER_1_CR_ENABLE; | 135 | cr |= TIMER_1_CR_ENABLE; |
113 | writel(cr, base + TIMER_CR); | 136 | writel(cr, fttmr010->base + TIMER_CR); |
114 | 137 | ||
115 | return 0; | 138 | return 0; |
116 | } | 139 | } |
117 | 140 | ||
118 | static int fttmr010_timer_set_periodic(struct clock_event_device *evt) | 141 | static int fttmr010_timer_set_periodic(struct clock_event_device *evt) |
119 | { | 142 | { |
120 | u32 period = DIV_ROUND_CLOSEST(tick_rate, HZ); | 143 | struct fttmr010 *fttmr010 = to_fttmr010(evt); |
144 | u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ); | ||
121 | u32 cr; | 145 | u32 cr; |
122 | 146 | ||
123 | /* Stop timer and interrupt */ | 147 | /* Stop timer and interrupt */ |
124 | cr = readl(base + TIMER_CR); | 148 | cr = readl(fttmr010->base + TIMER_CR); |
125 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); | 149 | cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); |
126 | writel(cr, base + TIMER_CR); | 150 | writel(cr, fttmr010->base + TIMER_CR); |
127 | 151 | ||
128 | /* Setup timer to fire at 1/HT intervals. */ | 152 | /* Setup timer to fire at 1/HT intervals. */ |
129 | cr = 0xffffffff - (period - 1); | 153 | cr = 0xffffffff - (period - 1); |
130 | writel(cr, base + TIMER1_COUNT); | 154 | writel(cr, fttmr010->base + TIMER1_COUNT); |
131 | writel(cr, base + TIMER1_LOAD); | 155 | writel(cr, fttmr010->base + TIMER1_LOAD); |
132 | 156 | ||
133 | /* enable interrupt on overflow */ | 157 | /* enable interrupt on overflow */ |
134 | cr = readl(base + TIMER_INTR_MASK); | 158 | cr = readl(fttmr010->base + TIMER_INTR_MASK); |
135 | cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2); | 159 | cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2); |
136 | cr |= TIMER_1_INT_OVERFLOW; | 160 | cr |= TIMER_1_INT_OVERFLOW; |
137 | writel(cr, base + TIMER_INTR_MASK); | 161 | writel(cr, fttmr010->base + TIMER_INTR_MASK); |
138 | 162 | ||
139 | /* Start the timer */ | 163 | /* Start the timer */ |
140 | cr = readl(base + TIMER_CR); | 164 | cr = readl(fttmr010->base + TIMER_CR); |
141 | cr |= TIMER_1_CR_ENABLE; | 165 | cr |= TIMER_1_CR_ENABLE; |
142 | cr |= TIMER_1_CR_INT; | 166 | cr |= TIMER_1_CR_INT; |
143 | writel(cr, base + TIMER_CR); | 167 | writel(cr, fttmr010->base + TIMER_CR); |
144 | 168 | ||
145 | return 0; | 169 | return 0; |
146 | } | 170 | } |
147 | 171 | ||
148 | /* Use TIMER1 as clock event */ | ||
149 | static struct clock_event_device fttmr010_clockevent = { | ||
150 | .name = "TIMER1", | ||
151 | /* Reasonably fast and accurate clock event */ | ||
152 | .rating = 300, | ||
153 | .shift = 32, | ||
154 | .features = CLOCK_EVT_FEAT_PERIODIC | | ||
155 | CLOCK_EVT_FEAT_ONESHOT, | ||
156 | .set_next_event = fttmr010_timer_set_next_event, | ||
157 | .set_state_shutdown = fttmr010_timer_shutdown, | ||
158 | .set_state_periodic = fttmr010_timer_set_periodic, | ||
159 | .set_state_oneshot = fttmr010_timer_shutdown, | ||
160 | .tick_resume = fttmr010_timer_shutdown, | ||
161 | }; | ||
162 | |||
163 | /* | 172 | /* |
164 | * IRQ handler for the timer | 173 | * IRQ handler for the timer |
165 | */ | 174 | */ |
166 | static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id) | 175 | static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id) |
167 | { | 176 | { |
168 | struct clock_event_device *evt = &fttmr010_clockevent; | 177 | struct clock_event_device *evt = dev_id; |
169 | 178 | ||
170 | evt->event_handler(evt); | 179 | evt->event_handler(evt); |
171 | return IRQ_HANDLED; | 180 | return IRQ_HANDLED; |
172 | } | 181 | } |
173 | 182 | ||
174 | static struct irqaction fttmr010_timer_irq = { | ||
175 | .name = "Faraday FTTMR010 Timer Tick", | ||
176 | .flags = IRQF_TIMER, | ||
177 | .handler = fttmr010_timer_interrupt, | ||
178 | }; | ||
179 | |||
180 | static int __init fttmr010_timer_init(struct device_node *np) | 183 | static int __init fttmr010_timer_init(struct device_node *np) |
181 | { | 184 | { |
185 | struct fttmr010 *fttmr010; | ||
182 | int irq; | 186 | int irq; |
183 | struct clk *clk; | 187 | struct clk *clk; |
184 | int ret; | 188 | int ret; |
@@ -198,53 +202,91 @@ static int __init fttmr010_timer_init(struct device_node *np) | |||
198 | pr_err("failed to enable PCLK\n"); | 202 | pr_err("failed to enable PCLK\n"); |
199 | return ret; | 203 | return ret; |
200 | } | 204 | } |
201 | tick_rate = clk_get_rate(clk); | ||
202 | 205 | ||
203 | base = of_iomap(np, 0); | 206 | fttmr010 = kzalloc(sizeof(*fttmr010), GFP_KERNEL); |
204 | if (!base) { | 207 | if (!fttmr010) { |
208 | ret = -ENOMEM; | ||
209 | goto out_disable_clock; | ||
210 | } | ||
211 | fttmr010->tick_rate = clk_get_rate(clk); | ||
212 | |||
213 | fttmr010->base = of_iomap(np, 0); | ||
214 | if (!fttmr010->base) { | ||
205 | pr_err("Can't remap registers"); | 215 | pr_err("Can't remap registers"); |
206 | return -ENXIO; | 216 | ret = -ENXIO; |
217 | goto out_free; | ||
207 | } | 218 | } |
208 | /* IRQ for timer 1 */ | 219 | /* IRQ for timer 1 */ |
209 | irq = irq_of_parse_and_map(np, 0); | 220 | irq = irq_of_parse_and_map(np, 0); |
210 | if (irq <= 0) { | 221 | if (irq <= 0) { |
211 | pr_err("Can't parse IRQ"); | 222 | pr_err("Can't parse IRQ"); |
212 | return -EINVAL; | 223 | ret = -EINVAL; |
224 | goto out_unmap; | ||
213 | } | 225 | } |
214 | 226 | ||
215 | /* | 227 | /* |
216 | * Reset the interrupt mask and status | 228 | * Reset the interrupt mask and status |
217 | */ | 229 | */ |
218 | writel(TIMER_INT_ALL_MASK, base + TIMER_INTR_MASK); | 230 | writel(TIMER_INT_ALL_MASK, fttmr010->base + TIMER_INTR_MASK); |
219 | writel(0, base + TIMER_INTR_STATE); | 231 | writel(0, fttmr010->base + TIMER_INTR_STATE); |
220 | writel(TIMER_DEFAULT_FLAGS, base + TIMER_CR); | 232 | writel(TIMER_DEFAULT_FLAGS, fttmr010->base + TIMER_CR); |
221 | 233 | ||
222 | /* | 234 | /* |
223 | * Setup free-running clocksource timer (interrupts | 235 | * Setup free-running clocksource timer (interrupts |
224 | * disabled.) | 236 | * disabled.) |
225 | */ | 237 | */ |
226 | writel(0, base + TIMER3_COUNT); | 238 | local_fttmr = fttmr010; |
227 | writel(0, base + TIMER3_LOAD); | 239 | writel(0, fttmr010->base + TIMER3_COUNT); |
228 | writel(0, base + TIMER3_MATCH1); | 240 | writel(0, fttmr010->base + TIMER3_LOAD); |
229 | writel(0, base + TIMER3_MATCH2); | 241 | writel(0, fttmr010->base + TIMER3_MATCH1); |
230 | clocksource_mmio_init(base + TIMER3_COUNT, | 242 | writel(0, fttmr010->base + TIMER3_MATCH2); |
231 | "fttmr010_clocksource", tick_rate, | 243 | clocksource_mmio_init(fttmr010->base + TIMER3_COUNT, |
244 | "FTTMR010-TIMER3", | ||
245 | fttmr010->tick_rate, | ||
232 | 300, 32, clocksource_mmio_readl_up); | 246 | 300, 32, clocksource_mmio_readl_up); |
233 | sched_clock_register(fttmr010_read_sched_clock, 32, tick_rate); | 247 | sched_clock_register(fttmr010_read_sched_clock, 32, |
248 | fttmr010->tick_rate); | ||
234 | 249 | ||
235 | /* | 250 | /* |
236 | * Setup clockevent timer (interrupt-driven.) | 251 | * Setup clockevent timer (interrupt-driven) on timer 1. |
237 | */ | 252 | */ |
238 | writel(0, base + TIMER1_COUNT); | 253 | writel(0, fttmr010->base + TIMER1_COUNT); |
239 | writel(0, base + TIMER1_LOAD); | 254 | writel(0, fttmr010->base + TIMER1_LOAD); |
240 | writel(0, base + TIMER1_MATCH1); | 255 | writel(0, fttmr010->base + TIMER1_MATCH1); |
241 | writel(0, base + TIMER1_MATCH2); | 256 | writel(0, fttmr010->base + TIMER1_MATCH2); |
242 | setup_irq(irq, &fttmr010_timer_irq); | 257 | ret = request_irq(irq, fttmr010_timer_interrupt, IRQF_TIMER, |
243 | fttmr010_clockevent.cpumask = cpumask_of(0); | 258 | "FTTMR010-TIMER1", &fttmr010->clkevt); |
244 | clockevents_config_and_register(&fttmr010_clockevent, tick_rate, | 259 | if (ret) { |
260 | pr_err("FTTMR010-TIMER1 no IRQ\n"); | ||
261 | goto out_unmap; | ||
262 | } | ||
263 | |||
264 | fttmr010->clkevt.name = "FTTMR010-TIMER1"; | ||
265 | /* Reasonably fast and accurate clock event */ | ||
266 | fttmr010->clkevt.rating = 300; | ||
267 | fttmr010->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | | ||
268 | CLOCK_EVT_FEAT_ONESHOT; | ||
269 | fttmr010->clkevt.set_next_event = fttmr010_timer_set_next_event; | ||
270 | fttmr010->clkevt.set_state_shutdown = fttmr010_timer_shutdown; | ||
271 | fttmr010->clkevt.set_state_periodic = fttmr010_timer_set_periodic; | ||
272 | fttmr010->clkevt.set_state_oneshot = fttmr010_timer_set_oneshot; | ||
273 | fttmr010->clkevt.tick_resume = fttmr010_timer_shutdown; | ||
274 | fttmr010->clkevt.cpumask = cpumask_of(0); | ||
275 | fttmr010->clkevt.irq = irq; | ||
276 | clockevents_config_and_register(&fttmr010->clkevt, | ||
277 | fttmr010->tick_rate, | ||
245 | 1, 0xffffffff); | 278 | 1, 0xffffffff); |
246 | 279 | ||
247 | return 0; | 280 | return 0; |
281 | |||
282 | out_unmap: | ||
283 | iounmap(fttmr010->base); | ||
284 | out_free: | ||
285 | kfree(fttmr010); | ||
286 | out_disable_clock: | ||
287 | clk_disable_unprepare(clk); | ||
288 | |||
289 | return ret; | ||
248 | } | 290 | } |
249 | CLOCKSOURCE_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_init); | 291 | CLOCKSOURCE_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_init); |
250 | CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", fttmr010_timer_init); | 292 | CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", fttmr010_timer_init); |