aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Camus <noamca@mellanox.com>2016-11-17 02:12:43 -0500
committerVineet Gupta <vgupta@synopsys.com>2016-11-30 14:54:25 -0500
commit60263dcd821b9558ea08b112d9d31ffbe3ac643f (patch)
treef77ab6f38364a802f839b6ec310bbafd4837df3b
parent0465fb495f9c9698de08ff103905008e5f38e8f1 (diff)
clocksource: Add clockevent support to NPS400 driver
Till now we used clockevent from generic ARC driver. This was enough as long as we worked with simple multicore SoC. When we are working with multithread SoC each HW thread can be scheduled to receive timer interrupt using timer mask register. This patch will provide a way to control clock events per HW thread. The design idea is that for each core there is dedicated register (TSI) serving all 16 HW threads. The register is a bitmask with one bit for each HW thread. When HW thread wants that next expiration of timer interrupt will hit it then the proper bit should be set in this dedicated register. When timer expires all HW threads within this core which their bit is set at the TSI register will be interrupted. Driver can be used from device tree by: compatible = "ezchip,nps400-timer0" <-- for clocksource compatible = "ezchip,nps400-timer1" <-- for clockevent Note that name convention for timer0/timer1 was taken from legacy ARC design. This design is our base before adding HW threads. For backward compatibility we keep "ezchip,nps400-timer" for clocksource Signed-off-by: Noam Camus <noamca@mellanox.com> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Rob Herring <robh@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt17
-rw-r--r--Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt (renamed from Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt)6
-rw-r--r--drivers/clocksource/timer-nps.c170
3 files changed, 190 insertions, 3 deletions
diff --git a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt
new file mode 100644
index 000000000000..e3cfce8fecc5
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer0.txt
@@ -0,0 +1,17 @@
1NPS Network Processor
2
3Required properties:
4
5- compatible : should be "ezchip,nps400-timer0"
6
7Clocks required for compatible = "ezchip,nps400-timer0":
8- interrupts : The interrupt of the first timer
9- clocks : Must contain a single entry describing the clock input
10
11Example:
12
13timer {
14 compatible = "ezchip,nps400-timer0";
15 interrupts = <3>;
16 clocks = <&sysclk>;
17};
diff --git a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt
index c8c03d700382..c0ab4190b8fb 100644
--- a/Documentation/devicetree/bindings/timer/ezchip,nps400-timer.txt
+++ b/Documentation/devicetree/bindings/timer/ezchip,nps400-timer1.txt
@@ -2,14 +2,14 @@ NPS Network Processor
2 2
3Required properties: 3Required properties:
4 4
5- compatible : should be "ezchip,nps400-timer" 5- compatible : should be "ezchip,nps400-timer1"
6 6
7Clocks required for compatible = "ezchip,nps400-timer": 7Clocks required for compatible = "ezchip,nps400-timer1":
8- clocks : Must contain a single entry describing the clock input 8- clocks : Must contain a single entry describing the clock input
9 9
10Example: 10Example:
11 11
12timer { 12timer {
13 compatible = "ezchip,nps400-timer"; 13 compatible = "ezchip,nps400-timer1";
14 clocks = <&sysclk>; 14 clocks = <&sysclk>;
15}; 15};
diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index 0c8e21f905d7..b4c8a023a2d4 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -111,3 +111,173 @@ static int __init nps_setup_clocksource(struct device_node *node)
111 111
112CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer", 112CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
113 nps_setup_clocksource); 113 nps_setup_clocksource);
114CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clk_src, "ezchip,nps400-timer1",
115 nps_setup_clocksource);
116
117#ifdef CONFIG_EZNPS_MTM_EXT
118#include <soc/nps/mtm.h>
119
120/* Timer related Aux registers */
121#define NPS_REG_TIMER0_TSI 0xFFFFF850
122#define NPS_REG_TIMER0_LIMIT 0x23
123#define NPS_REG_TIMER0_CTRL 0x22
124#define NPS_REG_TIMER0_CNT 0x21
125
126/*
127 * Interrupt Enabled (IE) - re-arm the timer
128 * Not Halted (NH) - is cleared when working with JTAG (for debug)
129 */
130#define TIMER0_CTRL_IE BIT(0)
131#define TIMER0_CTRL_NH BIT(1)
132
133static unsigned long nps_timer0_freq;
134static unsigned long nps_timer0_irq;
135
136static void nps_clkevent_rm_thread(void)
137{
138 int thread;
139 unsigned int cflags, enabled_threads;
140
141 hw_schd_save(&cflags);
142
143 enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
144
145 /* remove thread from TSI1 */
146 thread = read_aux_reg(CTOP_AUX_THREAD_ID);
147 enabled_threads &= ~(1 << thread);
148 write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
149
150 /* Acknowledge and if needed re-arm the timer */
151 if (!enabled_threads)
152 write_aux_reg(NPS_REG_TIMER0_CTRL, TIMER0_CTRL_NH);
153 else
154 write_aux_reg(NPS_REG_TIMER0_CTRL,
155 TIMER0_CTRL_IE | TIMER0_CTRL_NH);
156
157 hw_schd_restore(cflags);
158}
159
160static void nps_clkevent_add_thread(unsigned long delta)
161{
162 int thread;
163 unsigned int cflags, enabled_threads;
164
165 hw_schd_save(&cflags);
166
167 /* add thread to TSI1 */
168 thread = read_aux_reg(CTOP_AUX_THREAD_ID);
169 enabled_threads = read_aux_reg(NPS_REG_TIMER0_TSI);
170 enabled_threads |= (1 << thread);
171 write_aux_reg(NPS_REG_TIMER0_TSI, enabled_threads);
172
173 /* set next timer event */
174 write_aux_reg(NPS_REG_TIMER0_LIMIT, delta);
175 write_aux_reg(NPS_REG_TIMER0_CNT, 0);
176 write_aux_reg(NPS_REG_TIMER0_CTRL,
177 TIMER0_CTRL_IE | TIMER0_CTRL_NH);
178
179 hw_schd_restore(cflags);
180}
181
182/*
183 * Whenever anyone tries to change modes, we just mask interrupts
184 * and wait for the next event to get set.
185 */
186static int nps_clkevent_set_state(struct clock_event_device *dev)
187{
188 nps_clkevent_rm_thread();
189 disable_percpu_irq(nps_timer0_irq);
190
191 return 0;
192}
193
194static int nps_clkevent_set_next_event(unsigned long delta,
195 struct clock_event_device *dev)
196{
197 nps_clkevent_add_thread(delta);
198 enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
199
200 return 0;
201}
202
203static DEFINE_PER_CPU(struct clock_event_device, nps_clockevent_device) = {
204 .name = "NPS Timer0",
205 .features = CLOCK_EVT_FEAT_ONESHOT,
206 .rating = 300,
207 .set_next_event = nps_clkevent_set_next_event,
208 .set_state_oneshot = nps_clkevent_set_state,
209 .set_state_oneshot_stopped = nps_clkevent_set_state,
210 .set_state_shutdown = nps_clkevent_set_state,
211 .tick_resume = nps_clkevent_set_state,
212};
213
214static irqreturn_t timer_irq_handler(int irq, void *dev_id)
215{
216 struct clock_event_device *evt = dev_id;
217
218 nps_clkevent_rm_thread();
219 evt->event_handler(evt);
220
221 return IRQ_HANDLED;
222}
223
224static int nps_timer_starting_cpu(unsigned int cpu)
225{
226 struct clock_event_device *evt = this_cpu_ptr(&nps_clockevent_device);
227
228 evt->cpumask = cpumask_of(smp_processor_id());
229
230 clockevents_config_and_register(evt, nps_timer0_freq, 0, ULONG_MAX);
231 enable_percpu_irq(nps_timer0_irq, IRQ_TYPE_NONE);
232
233 return 0;
234}
235
236static int nps_timer_dying_cpu(unsigned int cpu)
237{
238 disable_percpu_irq(nps_timer0_irq);
239 return 0;
240}
241
242static int __init nps_setup_clockevent(struct device_node *node)
243{
244 struct clk *clk;
245 int ret;
246
247 nps_timer0_irq = irq_of_parse_and_map(node, 0);
248 if (nps_timer0_irq <= 0) {
249 pr_err("clockevent: missing irq");
250 return -EINVAL;
251 }
252
253 ret = nps_get_timer_clk(node, &nps_timer0_freq, &clk);
254 if (ret)
255 return ret;
256
257 /* Needs apriori irq_set_percpu_devid() done in intc map function */
258 ret = request_percpu_irq(nps_timer0_irq, timer_irq_handler,
259 "Timer0 (per-cpu-tick)",
260 &nps_clockevent_device);
261 if (ret) {
262 pr_err("Couldn't request irq\n");
263 clk_disable_unprepare(clk);
264 return ret;
265 }
266
267 ret = cpuhp_setup_state(CPUHP_AP_ARC_TIMER_STARTING,
268 "clockevents/nps:starting",
269 nps_timer_starting_cpu,
270 nps_timer_dying_cpu);
271 if (ret) {
272 pr_err("Failed to setup hotplug state");
273 clk_disable_unprepare(clk);
274 free_percpu_irq(nps_timer0_irq, &nps_clockevent_device);
275 return ret;
276 }
277
278 return 0;
279}
280
281CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clk_evt, "ezchip,nps400-timer0",
282 nps_setup_clockevent);
283#endif /* CONFIG_EZNPS_MTM_EXT */