Blame view

drivers/clocksource/timer-keystone.c 5.3 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
2
3
4
5
6
7
  /*
   * Keystone broadcast clock-event
   *
   * Copyright 2013 Texas Instruments, Inc.
   *
   * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
   */
  
  #include <linux/clk.h>
  #include <linux/clockchips.h>
  #include <linux/clocksource.h>
  #include <linux/interrupt.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  
  #define TIMER_NAME			"timer-keystone"
  
  /* Timer register offsets */
  #define TIM12				0x10
  #define TIM34				0x14
  #define PRD12				0x18
  #define PRD34				0x1c
  #define TCR				0x20
  #define TGCR				0x24
  #define INTCTLSTAT			0x44
  
  /* Timer register bitfields */
  #define TCR_ENAMODE_MASK		0xC0
  #define TCR_ENAMODE_ONESHOT_MASK	0x40
  #define TCR_ENAMODE_PERIODIC_MASK	0x80
  
  #define TGCR_TIM_UNRESET_MASK		0x03
  #define INTCTLSTAT_ENINT_MASK		0x01
  
  /**
   * struct keystone_timer: holds timer's data
   * @base: timer memory base address
   * @hz_period: cycles per HZ period
   * @event_dev: event device based on timer
   */
  static struct keystone_timer {
  	void __iomem *base;
  	unsigned long hz_period;
  	struct clock_event_device event_dev;
  } timer;
  
  static inline u32 keystone_timer_readl(unsigned long rg)
  {
  	return readl_relaxed(timer.base + rg);
  }
  
  static inline void keystone_timer_writel(u32 val, unsigned long rg)
  {
  	writel_relaxed(val, timer.base + rg);
  }
  
  /**
   * keystone_timer_barrier: write memory barrier
   * use explicit barrier to avoid using readl/writel non relaxed function
   * variants, because in our case non relaxed variants hide the true places
   * where barrier is needed.
   */
  static inline void keystone_timer_barrier(void)
  {
  	__iowmb();
  }
  
  /**
   * keystone_timer_config: configures timer to work in oneshot/periodic modes.
634eb0ec8   Viresh Kumar   clockevents/drive...
71
   * @ mask: mask of the mode to configure
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
72
73
   * @ period: cycles number to configure for
   */
634eb0ec8   Viresh Kumar   clockevents/drive...
74
  static int keystone_timer_config(u64 period, int mask)
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
75
76
77
78
79
80
81
82
  {
  	u32 tcr;
  	u32 off;
  
  	tcr = keystone_timer_readl(TCR);
  	off = tcr & ~(TCR_ENAMODE_MASK);
  
  	/* set enable mode */
634eb0ec8   Viresh Kumar   clockevents/drive...
83
  	tcr |= mask;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  
  	/* disable timer */
  	keystone_timer_writel(off, TCR);
  	/* here we have to be sure the timer has been disabled */
  	keystone_timer_barrier();
  
  	/* reset counter to zero, set new period */
  	keystone_timer_writel(0, TIM12);
  	keystone_timer_writel(0, TIM34);
  	keystone_timer_writel(period & 0xffffffff, PRD12);
  	keystone_timer_writel(period >> 32, PRD34);
  
  	/*
  	 * enable timer
  	 * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers
  	 * have been written.
  	 */
  	keystone_timer_barrier();
  	keystone_timer_writel(tcr, TCR);
  	return 0;
  }
  
  static void keystone_timer_disable(void)
  {
  	u32 tcr;
  
  	tcr = keystone_timer_readl(TCR);
  
  	/* disable timer */
  	tcr &= ~(TCR_ENAMODE_MASK);
  	keystone_timer_writel(tcr, TCR);
  }
  
  static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id)
  {
  	struct clock_event_device *evt = dev_id;
  
  	evt->event_handler(evt);
  	return IRQ_HANDLED;
  }
  
  static int keystone_set_next_event(unsigned long cycles,
  				  struct clock_event_device *evt)
  {
634eb0ec8   Viresh Kumar   clockevents/drive...
128
  	return keystone_timer_config(cycles, TCR_ENAMODE_ONESHOT_MASK);
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
129
  }
634eb0ec8   Viresh Kumar   clockevents/drive...
130
  static int keystone_shutdown(struct clock_event_device *evt)
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
131
  {
634eb0ec8   Viresh Kumar   clockevents/drive...
132
133
134
135
136
137
138
139
  	keystone_timer_disable();
  	return 0;
  }
  
  static int keystone_set_periodic(struct clock_event_device *evt)
  {
  	keystone_timer_config(timer.hz_period, TCR_ENAMODE_PERIODIC_MASK);
  	return 0;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
140
  }
531865058   Daniel Lezcano   clocksource/drive...
141
  static int __init keystone_timer_init(struct device_node *np)
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
142
143
144
145
146
  {
  	struct clock_event_device *event_dev = &timer.event_dev;
  	unsigned long rate;
  	struct clk *clk;
  	int irq, error;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
147
148
  
  	irq  = irq_of_parse_and_map(np, 0);
bdf7344e1   Daniel Lezcano   clocksource/drive...
149
  	if (!irq) {
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
150
151
  		pr_err("%s: failed to map interrupts
  ", __func__);
531865058   Daniel Lezcano   clocksource/drive...
152
  		return -EINVAL;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
153
154
155
156
157
158
  	}
  
  	timer.base = of_iomap(np, 0);
  	if (!timer.base) {
  		pr_err("%s: failed to map registers
  ", __func__);
531865058   Daniel Lezcano   clocksource/drive...
159
  		return -ENXIO;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
160
161
162
163
164
165
166
  	}
  
  	clk = of_clk_get(np, 0);
  	if (IS_ERR(clk)) {
  		pr_err("%s: failed to get clock
  ", __func__);
  		iounmap(timer.base);
531865058   Daniel Lezcano   clocksource/drive...
167
  		return PTR_ERR(clk);
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  	}
  
  	error = clk_prepare_enable(clk);
  	if (error) {
  		pr_err("%s: failed to enable clock
  ", __func__);
  		goto err;
  	}
  
  	rate = clk_get_rate(clk);
  
  	/* disable, use internal clock source */
  	keystone_timer_writel(0, TCR);
  	/* here we have to be sure the timer has been disabled */
  	keystone_timer_barrier();
  
  	/* reset timer as 64-bit, no pre-scaler, plus features are disabled */
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
185
186
187
  	keystone_timer_writel(0, TGCR);
  
  	/* unreset timer */
e099d01eb   Matthias Brugger   clocksource: time...
188
  	keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR);
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  
  	/* init counter to zero */
  	keystone_timer_writel(0, TIM12);
  	keystone_timer_writel(0, TIM34);
  
  	timer.hz_period = DIV_ROUND_UP(rate, HZ);
  
  	/* enable timer interrupts */
  	keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT);
  
  	error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER,
  			    TIMER_NAME, event_dev);
  	if (error) {
  		pr_err("%s: failed to setup irq
  ", __func__);
  		goto err;
  	}
  
  	/* setup clockevent */
  	event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
  	event_dev->set_next_event = keystone_set_next_event;
634eb0ec8   Viresh Kumar   clockevents/drive...
210
211
212
  	event_dev->set_state_shutdown = keystone_shutdown;
  	event_dev->set_state_periodic = keystone_set_periodic;
  	event_dev->set_state_oneshot = keystone_shutdown;
f8f5fe86f   Sudeep Holla   clocksource/drive...
213
  	event_dev->cpumask = cpu_possible_mask;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
214
215
216
217
218
219
220
221
  	event_dev->owner = THIS_MODULE;
  	event_dev->name = TIMER_NAME;
  	event_dev->irq = irq;
  
  	clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX);
  
  	pr_info("keystone timer clock @%lu Hz
  ", rate);
531865058   Daniel Lezcano   clocksource/drive...
222
  	return 0;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
223
224
225
  err:
  	clk_put(clk);
  	iounmap(timer.base);
531865058   Daniel Lezcano   clocksource/drive...
226
  	return error;
e6b7f580b   Ivan Khoronzhuk   clocksource: time...
227
  }
172733959   Daniel Lezcano   clocksource/drive...
228
  TIMER_OF_DECLARE(keystone_timer, "ti,keystone-timer",
531865058   Daniel Lezcano   clocksource/drive...
229
  			   keystone_timer_init);