Blame view

drivers/clocksource/bcm_kona_timer.c 5.2 KB
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * Copyright (C) 2012 Broadcom Corporation
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation version 2.
   *
   * This program is distributed "as is" WITHOUT ANY WARRANTY of any
   * kind, whether express or implied; without even the implied warranty
   * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   */
  
  #include <linux/init.h>
  #include <linux/irq.h>
  #include <linux/interrupt.h>
  #include <linux/jiffies.h>
  #include <linux/clockchips.h>
  #include <linux/types.h>
50ac20610   Tim Kryger   clocksource: kona...
20
  #include <linux/clk.h>
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
21
22
  
  #include <linux/io.h>
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
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
  
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  
  
  #define KONA_GPTIMER_STCS_OFFSET			0x00000000
  #define KONA_GPTIMER_STCLO_OFFSET			0x00000004
  #define KONA_GPTIMER_STCHI_OFFSET			0x00000008
  #define KONA_GPTIMER_STCM0_OFFSET			0x0000000C
  
  #define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT		0
  #define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT		4
  
  struct kona_bcm_timers {
  	int tmr_irq;
  	void __iomem *tmr_regs;
  };
  
  static struct kona_bcm_timers timers;
  
  static u32 arch_timer_rate;
  
  /*
   * We use the peripheral timers for system tick, the cpu global timer for
   * profile tick
   */
  static void kona_timer_disable_and_clear(void __iomem *base)
  {
  	uint32_t reg;
  
  	/*
  	 * clear and disable interrupts
  	 * We are using compare/match register 0 for our system interrupts
  	 */
  	reg = readl(base + KONA_GPTIMER_STCS_OFFSET);
  
  	/* Clear compare (0) interrupt */
  	reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT;
  	/* disable compare */
  	reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
  
  	writel(reg, base + KONA_GPTIMER_STCS_OFFSET);
  
  }
16c8eba0f   Arnd Bergmann   clocksource/drive...
68
  static int
ff4bcc84a   Olof Johansson   clocksource: kona...
69
  kona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw)
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
70
  {
16c8eba0f   Arnd Bergmann   clocksource/drive...
71
  	int loop_limit = 3;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
72
73
74
75
76
77
78
79
80
81
82
83
  
  	/*
  	 * Read 64-bit free running counter
  	 * 1. Read hi-word
  	 * 2. Read low-word
  	 * 3. Read hi-word again
  	 * 4.1
  	 *      if new hi-word is not equal to previously read hi-word, then
  	 *      start from #1
  	 * 4.2
  	 *      if new hi-word is equal to previously read hi-word then stop.
  	 */
16c8eba0f   Arnd Bergmann   clocksource/drive...
84
  	do {
ff4bcc84a   Olof Johansson   clocksource: kona...
85
86
87
  		*msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET);
  		*lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET);
  		if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET))
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
88
  			break;
16c8eba0f   Arnd Bergmann   clocksource/drive...
89
  	} while (--loop_limit);
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
90
91
92
93
94
  	if (!loop_limit) {
  		pr_err("bcm_kona_timer: getting counter failed.
  ");
  		pr_err(" Timer will be impacted
  ");
16c8eba0f   Arnd Bergmann   clocksource/drive...
95
  		return -ETIMEDOUT;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
96
  	}
16c8eba0f   Arnd Bergmann   clocksource/drive...
97
  	return 0;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
98
  }
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  static int kona_timer_set_next_event(unsigned long clc,
  				  struct clock_event_device *unused)
  {
  	/*
  	 * timer (0) is disabled by the timer interrupt already
  	 * so, here we reload the next event value and re-enable
  	 * the timer.
  	 *
  	 * This way, we are potentially losing the time between
  	 * timer-interrupt->set_next_event. CPU local timers, when
  	 * they come in should get rid of skew.
  	 */
  
  	uint32_t lsw, msw;
  	uint32_t reg;
16c8eba0f   Arnd Bergmann   clocksource/drive...
114
  	int ret;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
115

16c8eba0f   Arnd Bergmann   clocksource/drive...
116
117
118
  	ret = kona_timer_get_counter(timers.tmr_regs, &msw, &lsw);
  	if (ret)
  		return ret;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
119
120
121
122
123
124
125
126
127
128
129
  
  	/* Load the "next" event tick value */
  	writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET);
  
  	/* Enable compare */
  	reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
  	reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT);
  	writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET);
  
  	return 0;
  }
b4cf5d710   Viresh Kumar   clockevents/drive...
130
  static int kona_timer_shutdown(struct clock_event_device *evt)
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
131
  {
b4cf5d710   Viresh Kumar   clockevents/drive...
132
133
  	kona_timer_disable_and_clear(timers.tmr_regs);
  	return 0;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
134
135
136
137
138
139
  }
  
  static struct clock_event_device kona_clockevent_timer = {
  	.name = "timer 1",
  	.features = CLOCK_EVT_FEAT_ONESHOT,
  	.set_next_event = kona_timer_set_next_event,
b4cf5d710   Viresh Kumar   clockevents/drive...
140
141
  	.set_state_shutdown = kona_timer_shutdown,
  	.tick_resume = kona_timer_shutdown,
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  };
  
  static void __init kona_timer_clockevents_init(void)
  {
  	kona_clockevent_timer.cpumask = cpumask_of(0);
  	clockevents_config_and_register(&kona_clockevent_timer,
  		arch_timer_rate, 6, 0xffffffff);
  }
  
  static irqreturn_t kona_timer_interrupt(int irq, void *dev_id)
  {
  	struct clock_event_device *evt = &kona_clockevent_timer;
  
  	kona_timer_disable_and_clear(timers.tmr_regs);
  	evt->event_handler(evt);
  	return IRQ_HANDLED;
  }
  
  static struct irqaction kona_timer_irq = {
  	.name = "Kona Timer Tick",
  	.flags = IRQF_TIMER,
  	.handler = kona_timer_interrupt,
  };
595197743   Daniel Lezcano   clocksource/drive...
165
  static int __init kona_timer_init(struct device_node *node)
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
166
  {
ad037c1f4   Tim Kryger   clocksource: Kona...
167
168
  	u32 freq;
  	struct clk *external_clk;
ad037c1f4   Tim Kryger   clocksource: Kona...
169
170
171
172
173
174
175
176
  	external_clk = of_clk_get_by_name(node, NULL);
  
  	if (!IS_ERR(external_clk)) {
  		arch_timer_rate = clk_get_rate(external_clk);
  		clk_prepare_enable(external_clk);
  	} else if (!of_property_read_u32(node, "clock-frequency", &freq)) {
  		arch_timer_rate = freq;
  	} else {
ac9ce6d1a   Rafał Miłecki   clocksource: Add ...
177
178
  		pr_err("Kona Timer v1 unable to determine clock-frequency
  ");
595197743   Daniel Lezcano   clocksource/drive...
179
  		return -EINVAL;
ad037c1f4   Tim Kryger   clocksource: Kona...
180
181
182
183
184
185
186
187
188
  	}
  
  	/* Setup IRQ numbers */
  	timers.tmr_irq = irq_of_parse_and_map(node, 0);
  
  	/* Setup IO addresses */
  	timers.tmr_regs = of_iomap(node, 0);
  
  	kona_timer_disable_and_clear(timers.tmr_regs);
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
189
190
191
  	kona_timer_clockevents_init();
  	setup_irq(timers.tmr_irq, &kona_timer_irq);
  	kona_timer_set_next_event((arch_timer_rate / HZ), NULL);
595197743   Daniel Lezcano   clocksource/drive...
192
193
  
  	return 0;
8011657b9   Christian Daudt   ARM: bcm281xx: Ad...
194
  }
172733959   Daniel Lezcano   clocksource/drive...
195
  TIMER_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
aea237bfa   Christian Daudt   ARM: DT: binding ...
196
197
198
199
  /*
   * bcm,kona-timer is deprecated by brcm,kona-timer
   * being kept here for driver compatibility
   */
172733959   Daniel Lezcano   clocksource/drive...
200
  TIMER_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);