Blame view

drivers/clocksource/timer-of.c 5.21 KB
9952f6918   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
dc11bae78   Daniel Lezcano   clocksource/drive...
2
3
4
5
  /*
   * Copyright (c) 2017, Linaro Ltd.  All rights reserved.
   *
   * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
dc11bae78   Daniel Lezcano   clocksource/drive...
6
7
8
9
10
11
12
13
14
   */
  #include <linux/clk.h>
  #include <linux/interrupt.h>
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  #include <linux/slab.h>
  
  #include "timer-of.h"
cf7f46b9b   Daniel Lezcano   clocksource/drive...
15
16
17
18
19
20
  /**
   * timer_of_irq_exit - Release the interrupt
   * @of_irq: an of_timer_irq structure pointer
   *
   * Free the irq resource
   */
bc8537c44   Saravana Kannan   FROMLIST: drivers...
21
  static void timer_of_irq_exit(struct of_timer_irq *of_irq)
dc11bae78   Daniel Lezcano   clocksource/drive...
22
23
24
25
  {
  	struct timer_of *to = container_of(of_irq, struct timer_of, of_irq);
  
  	struct clock_event_device *clkevt = &to->clkevt;
0f1a7b3fa   Linus Torvalds   timer-of: don't u...
26
27
28
  	if (of_irq->percpu)
  		free_percpu_irq(of_irq->irq, clkevt);
  	else
dc11bae78   Daniel Lezcano   clocksource/drive...
29
30
  		free_irq(of_irq->irq, clkevt);
  }
cf7f46b9b   Daniel Lezcano   clocksource/drive...
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  /**
   * timer_of_irq_init - Request the interrupt
   * @np: a device tree node pointer
   * @of_irq: an of_timer_irq structure pointer
   *
   * Get the interrupt number from the DT from its definition and
   * request it. The interrupt is gotten by falling back the following way:
   *
   * - Get interrupt number by name
   * - Get interrupt number by index
   *
   * When the interrupt is per CPU, 'request_percpu_irq()' is called,
   * otherwise 'request_irq()' is used.
   *
   * Returns 0 on success, < 0 otherwise
   */
bc8537c44   Saravana Kannan   FROMLIST: drivers...
47
  static int timer_of_irq_init(struct device_node *np,
5bbf4ad94   Daniel Lezcano   clocksource/drive...
48
  				    struct of_timer_irq *of_irq)
dc11bae78   Daniel Lezcano   clocksource/drive...
49
50
51
52
  {
  	int ret;
  	struct timer_of *to = container_of(of_irq, struct timer_of, of_irq);
  	struct clock_event_device *clkevt = &to->clkevt;
32f2fea6e   Sergei Shtylyov   clocksource/drive...
53
54
55
  	if (of_irq->name) {
  		of_irq->irq = ret = of_irq_get_byname(np, of_irq->name);
  		if (ret < 0) {
ccb800124   Geert Uytterhoeven   clocksource/drive...
56
57
58
  			pr_err("Failed to get interrupt %s for %pOF
  ",
  			       of_irq->name, np);
32f2fea6e   Sergei Shtylyov   clocksource/drive...
59
60
61
62
63
  			return ret;
  		}
  	} else	{
  		of_irq->irq = irq_of_parse_and_map(np, of_irq->index);
  	}
dc11bae78   Daniel Lezcano   clocksource/drive...
64
  	if (!of_irq->irq) {
469869d18   Rob Herring   clocksource: Conv...
65
66
  		pr_err("Failed to map interrupt for %pOF
  ", np);
dc11bae78   Daniel Lezcano   clocksource/drive...
67
68
69
70
71
72
73
74
75
76
  		return -EINVAL;
  	}
  
  	ret = of_irq->percpu ?
  		request_percpu_irq(of_irq->irq, of_irq->handler,
  				   np->full_name, clkevt) :
  		request_irq(of_irq->irq, of_irq->handler,
  			    of_irq->flags ? of_irq->flags : IRQF_TIMER,
  			    np->full_name, clkevt);
  	if (ret) {
469869d18   Rob Herring   clocksource: Conv...
77
78
  		pr_err("Failed to request irq %d for %pOF
  ", of_irq->irq, np);
dc11bae78   Daniel Lezcano   clocksource/drive...
79
80
81
82
83
84
85
  		return ret;
  	}
  
  	clkevt->irq = of_irq->irq;
  
  	return 0;
  }
cf7f46b9b   Daniel Lezcano   clocksource/drive...
86
87
88
89
90
91
  /**
   * timer_of_clk_exit - Release the clock resources
   * @of_clk: a of_timer_clk structure pointer
   *
   * Disables and releases the refcount on the clk
   */
bc8537c44   Saravana Kannan   FROMLIST: drivers...
92
  static void timer_of_clk_exit(struct of_timer_clk *of_clk)
dc11bae78   Daniel Lezcano   clocksource/drive...
93
94
95
96
97
  {
  	of_clk->rate = 0;
  	clk_disable_unprepare(of_clk->clk);
  	clk_put(of_clk->clk);
  }
cf7f46b9b   Daniel Lezcano   clocksource/drive...
98
99
100
101
102
103
104
105
106
  /**
   * timer_of_clk_init - Initialize the clock resources
   * @np: a device tree node pointer
   * @of_clk: a of_timer_clk structure pointer
   *
   * Get the clock by name or by index, enable it and get the rate
   *
   * Returns 0 on success, < 0 otherwise
   */
bc8537c44   Saravana Kannan   FROMLIST: drivers...
107
  static int timer_of_clk_init(struct device_node *np,
5bbf4ad94   Daniel Lezcano   clocksource/drive...
108
  				    struct of_timer_clk *of_clk)
dc11bae78   Daniel Lezcano   clocksource/drive...
109
110
111
112
113
114
  {
  	int ret;
  
  	of_clk->clk = of_clk->name ? of_clk_get_by_name(np, of_clk->name) :
  		of_clk_get(np, of_clk->index);
  	if (IS_ERR(of_clk->clk)) {
763719771   Jon Hunter   clocksource/drive...
115
116
117
118
119
  		ret = PTR_ERR(of_clk->clk);
  		if (ret != -EPROBE_DEFER)
  			pr_err("Failed to get clock for %pOF
  ", np);
  		goto out;
dc11bae78   Daniel Lezcano   clocksource/drive...
120
121
122
123
  	}
  
  	ret = clk_prepare_enable(of_clk->clk);
  	if (ret) {
469869d18   Rob Herring   clocksource: Conv...
124
125
  		pr_err("Failed for enable clock for %pOF
  ", np);
dc11bae78   Daniel Lezcano   clocksource/drive...
126
127
128
129
130
131
  		goto out_clk_put;
  	}
  
  	of_clk->rate = clk_get_rate(of_clk->clk);
  	if (!of_clk->rate) {
  		ret = -EINVAL;
469869d18   Rob Herring   clocksource: Conv...
132
133
  		pr_err("Failed to get clock rate for %pOF
  ", np);
dc11bae78   Daniel Lezcano   clocksource/drive...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  		goto out_clk_disable;
  	}
  
  	of_clk->period = DIV_ROUND_UP(of_clk->rate, HZ);
  out:
  	return ret;
  
  out_clk_disable:
  	clk_disable_unprepare(of_clk->clk);
  out_clk_put:
  	clk_put(of_clk->clk);
  
  	goto out;
  }
bc8537c44   Saravana Kannan   FROMLIST: drivers...
148
  static void timer_of_base_exit(struct of_timer_base *of_base)
dc11bae78   Daniel Lezcano   clocksource/drive...
149
150
151
  {
  	iounmap(of_base->base);
  }
bc8537c44   Saravana Kannan   FROMLIST: drivers...
152
  static int timer_of_base_init(struct device_node *np,
5bbf4ad94   Daniel Lezcano   clocksource/drive...
153
  				     struct of_timer_base *of_base)
dc11bae78   Daniel Lezcano   clocksource/drive...
154
  {
9aea417af   Daniel Lezcano   clocksource/drive...
155
156
157
  	of_base->base = of_base->name ?
  		of_io_request_and_map(np, of_base->index, of_base->name) :
  		of_iomap(np, of_base->index);
9e80dbd87   Dan Carpenter   clocksource/drive...
158
  	if (IS_ERR(of_base->base)) {
9aea417af   Daniel Lezcano   clocksource/drive...
159
160
  		pr_err("Failed to iomap (%s)
  ", of_base->name);
9e80dbd87   Dan Carpenter   clocksource/drive...
161
  		return PTR_ERR(of_base->base);
dc11bae78   Daniel Lezcano   clocksource/drive...
162
163
164
165
  	}
  
  	return 0;
  }
bc8537c44   Saravana Kannan   FROMLIST: drivers...
166
  int timer_of_init(struct device_node *np, struct timer_of *to)
dc11bae78   Daniel Lezcano   clocksource/drive...
167
  {
b7dcc4eac   Arnd Bergmann   clocksource/drive...
168
  	int ret = -EINVAL;
dc11bae78   Daniel Lezcano   clocksource/drive...
169
170
171
  	int flags = 0;
  
  	if (to->flags & TIMER_OF_BASE) {
5bbf4ad94   Daniel Lezcano   clocksource/drive...
172
  		ret = timer_of_base_init(np, &to->of_base);
dc11bae78   Daniel Lezcano   clocksource/drive...
173
174
175
176
177
178
  		if (ret)
  			goto out_fail;
  		flags |= TIMER_OF_BASE;
  	}
  
  	if (to->flags & TIMER_OF_CLOCK) {
5bbf4ad94   Daniel Lezcano   clocksource/drive...
179
  		ret = timer_of_clk_init(np, &to->of_clk);
dc11bae78   Daniel Lezcano   clocksource/drive...
180
181
182
183
184
185
  		if (ret)
  			goto out_fail;
  		flags |= TIMER_OF_CLOCK;
  	}
  
  	if (to->flags & TIMER_OF_IRQ) {
5bbf4ad94   Daniel Lezcano   clocksource/drive...
186
  		ret = timer_of_irq_init(np, &to->of_irq);
dc11bae78   Daniel Lezcano   clocksource/drive...
187
188
189
190
191
192
  		if (ret)
  			goto out_fail;
  		flags |= TIMER_OF_IRQ;
  	}
  
  	if (!to->clkevt.name)
4411464d6   Geert Uytterhoeven   clocksource/drive...
193
  		to->clkevt.name = np->full_name;
1c63c1c08   Daniel Lezcano   clocksource/drive...
194
195
  
  	to->np = np;
dc11bae78   Daniel Lezcano   clocksource/drive...
196
197
198
199
  	return ret;
  
  out_fail:
  	if (flags & TIMER_OF_IRQ)
5bbf4ad94   Daniel Lezcano   clocksource/drive...
200
  		timer_of_irq_exit(&to->of_irq);
dc11bae78   Daniel Lezcano   clocksource/drive...
201
202
  
  	if (flags & TIMER_OF_CLOCK)
5bbf4ad94   Daniel Lezcano   clocksource/drive...
203
  		timer_of_clk_exit(&to->of_clk);
dc11bae78   Daniel Lezcano   clocksource/drive...
204
205
  
  	if (flags & TIMER_OF_BASE)
5bbf4ad94   Daniel Lezcano   clocksource/drive...
206
  		timer_of_base_exit(&to->of_base);
b7dcc4eac   Arnd Bergmann   clocksource/drive...
207
  	return ret;
dc11bae78   Daniel Lezcano   clocksource/drive...
208
  }
bc8537c44   Saravana Kannan   FROMLIST: drivers...
209
  EXPORT_SYMBOL_GPL(timer_of_init);
f48729a99   Benjamin Gaignard   clocksource/drive...
210

558de2824   Benjamin Gaignard   clocksource/timer...
211
212
213
214
215
216
217
  /**
   * timer_of_cleanup - release timer_of ressources
   * @to: timer_of structure
   *
   * Release the ressources that has been used in timer_of_init().
   * This function should be called in init error cases
   */
bc8537c44   Saravana Kannan   FROMLIST: drivers...
218
  void timer_of_cleanup(struct timer_of *to)
f48729a99   Benjamin Gaignard   clocksource/drive...
219
220
  {
  	if (to->flags & TIMER_OF_IRQ)
5bbf4ad94   Daniel Lezcano   clocksource/drive...
221
  		timer_of_irq_exit(&to->of_irq);
f48729a99   Benjamin Gaignard   clocksource/drive...
222
223
  
  	if (to->flags & TIMER_OF_CLOCK)
5bbf4ad94   Daniel Lezcano   clocksource/drive...
224
  		timer_of_clk_exit(&to->of_clk);
f48729a99   Benjamin Gaignard   clocksource/drive...
225
226
  
  	if (to->flags & TIMER_OF_BASE)
5bbf4ad94   Daniel Lezcano   clocksource/drive...
227
  		timer_of_base_exit(&to->of_base);
f48729a99   Benjamin Gaignard   clocksource/drive...
228
  }