aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/timer-of.c
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2017-06-04 18:18:43 -0400
committerDaniel Lezcano <daniel.lezcano@linaro.org>2017-06-14 06:02:32 -0400
commitdc11bae78529526605c5c45c369c9512fd012093 (patch)
tree4d062102c61a527005f4b0532e390578148b2ea5 /drivers/clocksource/timer-of.c
parent2a515e5d7c52d5cf1e9153cb03efa133e3459c88 (diff)
clocksource/drivers: Add timer-of common init routine
The different drivers are all using the same pattern when initializing. 1. Get the base address 2. Get the irq number 3. Get the clock 4. Prepare and enable the clock 5. Get the rate 6. Request an interrupt Instead of repeating again and again these steps in all the drivers, let's provide a common init routine to give the opportunity to factor all of them out. We can expect a significant kernel size improvement when the common routine will be used in all the drivers. Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource/timer-of.c')
-rw-r--r--drivers/clocksource/timer-of.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c
new file mode 100644
index 000000000000..be1dbee11c20
--- /dev/null
+++ b/drivers/clocksource/timer-of.c
@@ -0,0 +1,172 @@
1/*
2 * Copyright (c) 2017, Linaro Ltd. All rights reserved.
3 *
4 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/clk.h>
19#include <linux/interrupt.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/of_irq.h>
23#include <linux/slab.h>
24
25#include "timer-of.h"
26
27static __init void timer_irq_exit(struct of_timer_irq *of_irq)
28{
29 struct timer_of *to = container_of(of_irq, struct timer_of, of_irq);
30
31 struct clock_event_device *clkevt = &to->clkevt;
32
33 of_irq->percpu ? free_percpu_irq(of_irq->irq, clkevt) :
34 free_irq(of_irq->irq, clkevt);
35}
36
37static __init int timer_irq_init(struct device_node *np,
38 struct of_timer_irq *of_irq)
39{
40 int ret;
41 struct timer_of *to = container_of(of_irq, struct timer_of, of_irq);
42 struct clock_event_device *clkevt = &to->clkevt;
43
44 of_irq->irq = of_irq->name ? of_irq_get_byname(np, of_irq->name):
45 irq_of_parse_and_map(np, of_irq->index);
46 if (!of_irq->irq) {
47 pr_err("Failed to map interrupt for %s\n", np->full_name);
48 return -EINVAL;
49 }
50
51 ret = of_irq->percpu ?
52 request_percpu_irq(of_irq->irq, of_irq->handler,
53 np->full_name, clkevt) :
54 request_irq(of_irq->irq, of_irq->handler,
55 of_irq->flags ? of_irq->flags : IRQF_TIMER,
56 np->full_name, clkevt);
57 if (ret) {
58 pr_err("Failed to request irq %d for %s\n", of_irq->irq,
59 np->full_name);
60 return ret;
61 }
62
63 clkevt->irq = of_irq->irq;
64
65 return 0;
66}
67
68static __init void timer_clk_exit(struct of_timer_clk *of_clk)
69{
70 of_clk->rate = 0;
71 clk_disable_unprepare(of_clk->clk);
72 clk_put(of_clk->clk);
73}
74
75static __init int timer_clk_init(struct device_node *np,
76 struct of_timer_clk *of_clk)
77{
78 int ret;
79
80 of_clk->clk = of_clk->name ? of_clk_get_by_name(np, of_clk->name) :
81 of_clk_get(np, of_clk->index);
82 if (IS_ERR(of_clk->clk)) {
83 pr_err("Failed to get clock for %s\n", np->full_name);
84 return PTR_ERR(of_clk->clk);
85 }
86
87 ret = clk_prepare_enable(of_clk->clk);
88 if (ret) {
89 pr_err("Failed for enable clock for %s\n", np->full_name);
90 goto out_clk_put;
91 }
92
93 of_clk->rate = clk_get_rate(of_clk->clk);
94 if (!of_clk->rate) {
95 ret = -EINVAL;
96 pr_err("Failed to get clock rate for %s\n", np->full_name);
97 goto out_clk_disable;
98 }
99
100 of_clk->period = DIV_ROUND_UP(of_clk->rate, HZ);
101out:
102 return ret;
103
104out_clk_disable:
105 clk_disable_unprepare(of_clk->clk);
106out_clk_put:
107 clk_put(of_clk->clk);
108
109 goto out;
110}
111
112static __init void timer_base_exit(struct of_timer_base *of_base)
113{
114 iounmap(of_base->base);
115}
116
117static __init int timer_base_init(struct device_node *np,
118 struct of_timer_base *of_base)
119{
120 const char *name = of_base->name ? of_base->name : np->full_name;
121
122 of_base->base = of_io_request_and_map(np, of_base->index, name);
123 if (of_base->base) {
124 pr_err("Failed to iomap (%s)\n", name);
125 return -ENXIO;
126 }
127
128 return 0;
129}
130
131int __init timer_of_init(struct device_node *np, struct timer_of *to)
132{
133 int ret;
134 int flags = 0;
135
136 if (to->flags & TIMER_OF_BASE) {
137 ret = timer_base_init(np, &to->of_base);
138 if (ret)
139 goto out_fail;
140 flags |= TIMER_OF_BASE;
141 }
142
143 if (to->flags & TIMER_OF_CLOCK) {
144 ret = timer_clk_init(np, &to->of_clk);
145 if (ret)
146 goto out_fail;
147 flags |= TIMER_OF_CLOCK;
148 }
149
150 if (to->flags & TIMER_OF_IRQ) {
151 ret = timer_irq_init(np, &to->of_irq);
152 if (ret)
153 goto out_fail;
154 flags |= TIMER_OF_IRQ;
155 }
156
157 if (!to->clkevt.name)
158 to->clkevt.name = np->name;
159out:
160 return ret;
161
162out_fail:
163 if (flags & TIMER_OF_IRQ)
164 timer_irq_exit(&to->of_irq);
165
166 if (flags & TIMER_OF_CLOCK)
167 timer_clk_exit(&to->of_clk);
168
169 if (flags & TIMER_OF_BASE)
170 timer_base_exit(&to->of_base);
171 goto out;
172}