Blame view

drivers/clocksource/timer-atlas7.c 8.22 KB
a636cd6c4   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
4898de3d1   Barry Song   ARM: PRIMA2: add ...
2
3
4
5
  /*
   * System timer for CSR SiRFprimaII
   *
   * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
4898de3d1   Barry Song   ARM: PRIMA2: add ...
6
7
8
9
10
11
   */
  
  #include <linux/kernel.h>
  #include <linux/interrupt.h>
  #include <linux/clockchips.h>
  #include <linux/clocksource.h>
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
12
  #include <linux/cpu.h>
4898de3d1   Barry Song   ARM: PRIMA2: add ...
13
14
15
16
17
18
19
  #include <linux/bitops.h>
  #include <linux/irq.h>
  #include <linux/clk.h>
  #include <linux/slab.h>
  #include <linux/of.h>
  #include <linux/of_irq.h>
  #include <linux/of_address.h>
38ff87f77   Stephen Boyd   sched_clock: Make...
20
  #include <linux/sched_clock.h>
980c51ab0   Uwe Kleine-König   clocksource: sirf...
21

4898de3d1   Barry Song   ARM: PRIMA2: add ...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  #define SIRFSOC_TIMER_32COUNTER_0_CTRL			0x0000
  #define SIRFSOC_TIMER_32COUNTER_1_CTRL			0x0004
  #define SIRFSOC_TIMER_MATCH_0				0x0018
  #define SIRFSOC_TIMER_MATCH_1				0x001c
  #define SIRFSOC_TIMER_COUNTER_0				0x0048
  #define SIRFSOC_TIMER_COUNTER_1				0x004c
  #define SIRFSOC_TIMER_INTR_STATUS			0x0060
  #define SIRFSOC_TIMER_WATCHDOG_EN			0x0064
  #define SIRFSOC_TIMER_64COUNTER_CTRL			0x0068
  #define SIRFSOC_TIMER_64COUNTER_LO			0x006c
  #define SIRFSOC_TIMER_64COUNTER_HI			0x0070
  #define SIRFSOC_TIMER_64COUNTER_LOAD_LO			0x0074
  #define SIRFSOC_TIMER_64COUNTER_LOAD_HI			0x0078
  #define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO		0x007c
  #define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI		0x0080
  
  #define SIRFSOC_TIMER_REG_CNT 6
5833ac986   Barry Song   clocksource: marc...
39
  static unsigned long atlas7_timer_rate;
ef89af1f4   Yanchang Li   clocksource: sirf...
40

4898de3d1   Barry Song   ARM: PRIMA2: add ...
41
42
43
44
45
46
47
48
49
50
51
52
  static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
  	SIRFSOC_TIMER_WATCHDOG_EN,
  	SIRFSOC_TIMER_32COUNTER_0_CTRL,
  	SIRFSOC_TIMER_32COUNTER_1_CTRL,
  	SIRFSOC_TIMER_64COUNTER_CTRL,
  	SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
  	SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
  };
  
  static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
  
  static void __iomem *sirfsoc_timer_base;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
53
54
55
56
57
58
59
60
61
62
63
  
  /* disable count and interrupt */
  static inline void sirfsoc_timer_count_disable(int idx)
  {
  	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
  		sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
  }
  
  /* enable count and interrupt */
  static inline void sirfsoc_timer_count_enable(int idx)
  {
28cf35675   Hao Liu   clocksource: sirf...
64
  	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x3,
4898de3d1   Barry Song   ARM: PRIMA2: add ...
65
66
67
68
69
70
71
72
73
74
75
  		sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
  }
  
  /* timer interrupt handler */
  static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
  {
  	struct clock_event_device *ce = dev_id;
  	int cpu = smp_processor_id();
  
  	/* clear timer interrupt */
  	writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
1e729d378   Viresh Kumar   clockevents/drive...
76
  	if (clockevent_state_oneshot(ce))
4898de3d1   Barry Song   ARM: PRIMA2: add ...
77
78
79
80
81
82
83
84
  		sirfsoc_timer_count_disable(cpu);
  
  	ce->event_handler(ce);
  
  	return IRQ_HANDLED;
  }
  
  /* read 64-bit timer counter */
a5a1d1c29   Thomas Gleixner   clocksource: Use ...
85
  static u64 sirfsoc_timer_read(struct clocksource *cs)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  {
  	u64 cycles;
  
  	writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
  			BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
  
  	cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
  	cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
  
  	return cycles;
  }
  
  static int sirfsoc_timer_set_next_event(unsigned long delta,
  	struct clock_event_device *ce)
  {
  	int cpu = smp_processor_id();
28cf35675   Hao Liu   clocksource: sirf...
102
103
  	/* disable timer first, then modify the related registers */
  	sirfsoc_timer_count_disable(cpu);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
104
105
106
107
108
109
110
111
112
113
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
  		4 * cpu);
  	writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
  		4 * cpu);
  
  	/* enable the tick */
  	sirfsoc_timer_count_enable(cpu);
  
  	return 0;
  }
1e729d378   Viresh Kumar   clockevents/drive...
114
115
  /* Oneshot is enabled in set_next_event */
  static int sirfsoc_timer_shutdown(struct clock_event_device *evt)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
116
  {
4898de3d1   Barry Song   ARM: PRIMA2: add ...
117
  	sirfsoc_timer_count_disable(smp_processor_id());
1e729d378   Viresh Kumar   clockevents/drive...
118
  	return 0;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  }
  
  static void sirfsoc_clocksource_suspend(struct clocksource *cs)
  {
  	int i;
  
  	for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
  		sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
  }
  
  static void sirfsoc_clocksource_resume(struct clocksource *cs)
  {
  	int i;
  
  	for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
  		writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
  
  	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
  		sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
  	writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
  		sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
  
  	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
  		BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
  }
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
144
  static struct clock_event_device __percpu *sirfsoc_clockevent;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
145
146
147
148
149
150
151
152
153
154
  
  static struct clocksource sirfsoc_clocksource = {
  	.name = "sirfsoc_clocksource",
  	.rating = 200,
  	.mask = CLOCKSOURCE_MASK(64),
  	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
  	.read = sirfsoc_timer_read,
  	.suspend = sirfsoc_clocksource_suspend,
  	.resume = sirfsoc_clocksource_resume,
  };
cc2550b42   afzal mohammed   clocksource: Repl...
155
  static unsigned int sirfsoc_timer_irq, sirfsoc_timer1_irq;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
156

eb0a9d8c6   Richard Cochran   clocksource/atlas...
157
  static int sirfsoc_local_timer_starting_cpu(unsigned int cpu)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
158
  {
eb0a9d8c6   Richard Cochran   clocksource/atlas...
159
  	struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu);
cc2550b42   afzal mohammed   clocksource: Repl...
160
161
162
163
164
165
166
167
168
169
  	unsigned int irq;
  	const char *name;
  
  	if (cpu == 0) {
  		irq = sirfsoc_timer_irq;
  		name = "sirfsoc_timer0";
  	} else {
  		irq = sirfsoc_timer1_irq;
  		name = "sirfsoc_timer1";
  	}
4898de3d1   Barry Song   ARM: PRIMA2: add ...
170

cc2550b42   afzal mohammed   clocksource: Repl...
171
  	ce->irq = irq;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
172
  	ce->name = "local_timer";
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
173
174
  	ce->features = CLOCK_EVT_FEAT_ONESHOT;
  	ce->rating = 200;
1e729d378   Viresh Kumar   clockevents/drive...
175
176
177
  	ce->set_state_shutdown = sirfsoc_timer_shutdown;
  	ce->set_state_oneshot = sirfsoc_timer_shutdown;
  	ce->tick_resume = sirfsoc_timer_shutdown;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
178
  	ce->set_next_event = sirfsoc_timer_set_next_event;
5833ac986   Barry Song   clocksource: marc...
179
  	clockevents_calc_mult_shift(ce, atlas7_timer_rate, 60);
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
180
  	ce->max_delta_ns = clockevent_delta2ns(-2, ce);
547733c58   Nicolai Stange   clockevents/drive...
181
  	ce->max_delta_ticks = (unsigned long)-2;
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
182
  	ce->min_delta_ns = clockevent_delta2ns(2, ce);
547733c58   Nicolai Stange   clockevents/drive...
183
  	ce->min_delta_ticks = 2;
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
184
  	ce->cpumask = cpumask_of(cpu);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
185

cc2550b42   afzal mohammed   clocksource: Repl...
186
187
188
  	BUG_ON(request_irq(ce->irq, sirfsoc_timer_interrupt,
  			   IRQF_TIMER | IRQF_NOBALANCING, name, ce));
  	irq_force_affinity(ce->irq, cpumask_of(cpu));
4898de3d1   Barry Song   ARM: PRIMA2: add ...
189
190
191
192
  
  	clockevents_register_device(ce);
  	return 0;
  }
eb0a9d8c6   Richard Cochran   clocksource/atlas...
193
  static int sirfsoc_local_timer_dying_cpu(unsigned int cpu)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
194
  {
cc2550b42   afzal mohammed   clocksource: Repl...
195
  	struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
196
  	sirfsoc_timer_count_disable(1);
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
197
  	if (cpu == 0)
cc2550b42   afzal mohammed   clocksource: Repl...
198
  		free_irq(sirfsoc_timer_irq, ce);
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
199
  	else
cc2550b42   afzal mohammed   clocksource: Repl...
200
  		free_irq(sirfsoc_timer1_irq, ce);
eb0a9d8c6   Richard Cochran   clocksource/atlas...
201
  	return 0;
4898de3d1   Barry Song   ARM: PRIMA2: add ...
202
  }
c41c96dda   Daniel Lezcano   clocksource/drive...
203
  static int __init sirfsoc_clockevent_init(void)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
204
  {
05a654855   Stephen Boyd   ARM: PRIMA2: Divo...
205
206
  	sirfsoc_clockevent = alloc_percpu(struct clock_event_device);
  	BUG_ON(!sirfsoc_clockevent);
eb0a9d8c6   Richard Cochran   clocksource/atlas...
207
208
  	/* Install and invoke hotplug callbacks */
  	return cpuhp_setup_state(CPUHP_AP_MARCO_TIMER_STARTING,
73c1b41e6   Thomas Gleixner   cpu/hotplug: Clea...
209
  				 "clockevents/marco:starting",
eb0a9d8c6   Richard Cochran   clocksource/atlas...
210
211
  				 sirfsoc_local_timer_starting_cpu,
  				 sirfsoc_local_timer_dying_cpu);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
212
213
214
  }
  
  /* initialize the kernel jiffy timer source */
c41c96dda   Daniel Lezcano   clocksource/drive...
215
  static int __init sirfsoc_atlas7_timer_init(struct device_node *np)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
216
  {
4898de3d1   Barry Song   ARM: PRIMA2: add ...
217
  	struct clk *clk;
c7cff54d5   Zhiwu Song   clocksource:sirf:...
218
  	clk = of_clk_get(np, 0);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
219
  	BUG_ON(IS_ERR(clk));
38941522e   Zhiwu Song   clocksource: sirf...
220
221
  
  	BUG_ON(clk_prepare_enable(clk));
5833ac986   Barry Song   clocksource: marc...
222
  	atlas7_timer_rate = clk_get_rate(clk);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
223

ef89af1f4   Yanchang Li   clocksource: sirf...
224
225
226
227
  	/* timer dividers: 0, not divided */
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
228
229
230
231
232
233
234
235
236
237
238
  
  	/* Initialize timer counters to 0 */
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
  	writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
  		BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
  	writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
  
  	/* Clear all interrupts */
  	writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
5833ac986   Barry Song   clocksource: marc...
239
  	BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, atlas7_timer_rate));
4898de3d1   Barry Song   ARM: PRIMA2: add ...
240

c41c96dda   Daniel Lezcano   clocksource/drive...
241
  	return sirfsoc_clockevent_init();
4898de3d1   Barry Song   ARM: PRIMA2: add ...
242
  }
c41c96dda   Daniel Lezcano   clocksource/drive...
243
  static int __init sirfsoc_of_timer_init(struct device_node *np)
4898de3d1   Barry Song   ARM: PRIMA2: add ...
244
  {
4898de3d1   Barry Song   ARM: PRIMA2: add ...
245
  	sirfsoc_timer_base = of_iomap(np, 0);
c41c96dda   Daniel Lezcano   clocksource/drive...
246
247
248
249
250
  	if (!sirfsoc_timer_base) {
  		pr_err("unable to map timer cpu registers
  ");
  		return -ENXIO;
  	}
4898de3d1   Barry Song   ARM: PRIMA2: add ...
251

cc2550b42   afzal mohammed   clocksource: Repl...
252
253
  	sirfsoc_timer_irq = irq_of_parse_and_map(np, 0);
  	if (!sirfsoc_timer_irq) {
c41c96dda   Daniel Lezcano   clocksource/drive...
254
255
256
257
  		pr_err("No irq passed for timer0 via DT
  ");
  		return -EINVAL;
  	}
4898de3d1   Barry Song   ARM: PRIMA2: add ...
258

cc2550b42   afzal mohammed   clocksource: Repl...
259
260
  	sirfsoc_timer1_irq = irq_of_parse_and_map(np, 1);
  	if (!sirfsoc_timer1_irq) {
c41c96dda   Daniel Lezcano   clocksource/drive...
261
262
263
264
  		pr_err("No irq passed for timer1 via DT
  ");
  		return -EINVAL;
  	}
4898de3d1   Barry Song   ARM: PRIMA2: add ...
265

c41c96dda   Daniel Lezcano   clocksource/drive...
266
  	return sirfsoc_atlas7_timer_init(np);
4898de3d1   Barry Song   ARM: PRIMA2: add ...
267
  }
172733959   Daniel Lezcano   clocksource/drive...
268
  TIMER_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);