Commit | Line | Data |
---|---|---|
b89e9691 TG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2012 Broadcom Corporation | |
8011657b CD |
3 | |
4 | #include <linux/init.h> | |
5 | #include <linux/irq.h> | |
6 | #include <linux/interrupt.h> | |
7 | #include <linux/jiffies.h> | |
8 | #include <linux/clockchips.h> | |
9 | #include <linux/types.h> | |
50ac2061 | 10 | #include <linux/clk.h> |
8011657b CD |
11 | |
12 | #include <linux/io.h> | |
8011657b CD |
13 | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/of_irq.h> | |
17 | ||
18 | ||
19 | #define KONA_GPTIMER_STCS_OFFSET 0x00000000 | |
20 | #define KONA_GPTIMER_STCLO_OFFSET 0x00000004 | |
21 | #define KONA_GPTIMER_STCHI_OFFSET 0x00000008 | |
22 | #define KONA_GPTIMER_STCM0_OFFSET 0x0000000C | |
23 | ||
24 | #define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT 0 | |
25 | #define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT 4 | |
26 | ||
27 | struct kona_bcm_timers { | |
28 | int tmr_irq; | |
29 | void __iomem *tmr_regs; | |
30 | }; | |
31 | ||
32 | static struct kona_bcm_timers timers; | |
33 | ||
34 | static u32 arch_timer_rate; | |
35 | ||
36 | /* | |
37 | * We use the peripheral timers for system tick, the cpu global timer for | |
38 | * profile tick | |
39 | */ | |
40 | static void kona_timer_disable_and_clear(void __iomem *base) | |
41 | { | |
42 | uint32_t reg; | |
43 | ||
44 | /* | |
45 | * clear and disable interrupts | |
46 | * We are using compare/match register 0 for our system interrupts | |
47 | */ | |
48 | reg = readl(base + KONA_GPTIMER_STCS_OFFSET); | |
49 | ||
50 | /* Clear compare (0) interrupt */ | |
51 | reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT; | |
52 | /* disable compare */ | |
53 | reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); | |
54 | ||
55 | writel(reg, base + KONA_GPTIMER_STCS_OFFSET); | |
56 | ||
57 | } | |
58 | ||
16c8eba0 | 59 | static int |
ff4bcc84 | 60 | kona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw) |
8011657b | 61 | { |
16c8eba0 | 62 | int loop_limit = 3; |
8011657b CD |
63 | |
64 | /* | |
65 | * Read 64-bit free running counter | |
66 | * 1. Read hi-word | |
67 | * 2. Read low-word | |
68 | * 3. Read hi-word again | |
69 | * 4.1 | |
70 | * if new hi-word is not equal to previously read hi-word, then | |
71 | * start from #1 | |
72 | * 4.2 | |
73 | * if new hi-word is equal to previously read hi-word then stop. | |
74 | */ | |
75 | ||
16c8eba0 | 76 | do { |
ff4bcc84 OJ |
77 | *msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET); |
78 | *lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET); | |
79 | if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET)) | |
8011657b | 80 | break; |
16c8eba0 | 81 | } while (--loop_limit); |
8011657b CD |
82 | if (!loop_limit) { |
83 | pr_err("bcm_kona_timer: getting counter failed.\n"); | |
84 | pr_err(" Timer will be impacted\n"); | |
16c8eba0 | 85 | return -ETIMEDOUT; |
8011657b CD |
86 | } |
87 | ||
16c8eba0 | 88 | return 0; |
8011657b CD |
89 | } |
90 | ||
8011657b CD |
91 | static int kona_timer_set_next_event(unsigned long clc, |
92 | struct clock_event_device *unused) | |
93 | { | |
94 | /* | |
95 | * timer (0) is disabled by the timer interrupt already | |
96 | * so, here we reload the next event value and re-enable | |
97 | * the timer. | |
98 | * | |
99 | * This way, we are potentially losing the time between | |
100 | * timer-interrupt->set_next_event. CPU local timers, when | |
101 | * they come in should get rid of skew. | |
102 | */ | |
103 | ||
104 | uint32_t lsw, msw; | |
105 | uint32_t reg; | |
16c8eba0 | 106 | int ret; |
8011657b | 107 | |
16c8eba0 AB |
108 | ret = kona_timer_get_counter(timers.tmr_regs, &msw, &lsw); |
109 | if (ret) | |
110 | return ret; | |
8011657b CD |
111 | |
112 | /* Load the "next" event tick value */ | |
113 | writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET); | |
114 | ||
115 | /* Enable compare */ | |
116 | reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); | |
117 | reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); | |
118 | writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
b4cf5d71 | 123 | static int kona_timer_shutdown(struct clock_event_device *evt) |
8011657b | 124 | { |
b4cf5d71 VK |
125 | kona_timer_disable_and_clear(timers.tmr_regs); |
126 | return 0; | |
8011657b CD |
127 | } |
128 | ||
129 | static struct clock_event_device kona_clockevent_timer = { | |
130 | .name = "timer 1", | |
131 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
132 | .set_next_event = kona_timer_set_next_event, | |
b4cf5d71 VK |
133 | .set_state_shutdown = kona_timer_shutdown, |
134 | .tick_resume = kona_timer_shutdown, | |
8011657b CD |
135 | }; |
136 | ||
137 | static void __init kona_timer_clockevents_init(void) | |
138 | { | |
139 | kona_clockevent_timer.cpumask = cpumask_of(0); | |
140 | clockevents_config_and_register(&kona_clockevent_timer, | |
141 | arch_timer_rate, 6, 0xffffffff); | |
142 | } | |
143 | ||
144 | static irqreturn_t kona_timer_interrupt(int irq, void *dev_id) | |
145 | { | |
146 | struct clock_event_device *evt = &kona_clockevent_timer; | |
147 | ||
148 | kona_timer_disable_and_clear(timers.tmr_regs); | |
149 | evt->event_handler(evt); | |
150 | return IRQ_HANDLED; | |
151 | } | |
152 | ||
59519774 | 153 | static int __init kona_timer_init(struct device_node *node) |
8011657b | 154 | { |
ad037c1f TK |
155 | u32 freq; |
156 | struct clk *external_clk; | |
157 | ||
ad037c1f TK |
158 | external_clk = of_clk_get_by_name(node, NULL); |
159 | ||
160 | if (!IS_ERR(external_clk)) { | |
161 | arch_timer_rate = clk_get_rate(external_clk); | |
162 | clk_prepare_enable(external_clk); | |
163 | } else if (!of_property_read_u32(node, "clock-frequency", &freq)) { | |
164 | arch_timer_rate = freq; | |
165 | } else { | |
ac9ce6d1 | 166 | pr_err("Kona Timer v1 unable to determine clock-frequency\n"); |
59519774 | 167 | return -EINVAL; |
ad037c1f TK |
168 | } |
169 | ||
170 | /* Setup IRQ numbers */ | |
171 | timers.tmr_irq = irq_of_parse_and_map(node, 0); | |
172 | ||
173 | /* Setup IO addresses */ | |
174 | timers.tmr_regs = of_iomap(node, 0); | |
175 | ||
176 | kona_timer_disable_and_clear(timers.tmr_regs); | |
177 | ||
8011657b | 178 | kona_timer_clockevents_init(); |
cc2550b4 | 179 | if (request_irq(timers.tmr_irq, kona_timer_interrupt, IRQF_TIMER, |
180 | "Kona Timer Tick", NULL)) | |
181 | pr_err("%s: request_irq() failed\n", "Kona Timer Tick"); | |
8011657b | 182 | kona_timer_set_next_event((arch_timer_rate / HZ), NULL); |
59519774 DL |
183 | |
184 | return 0; | |
8011657b CD |
185 | } |
186 | ||
17273395 | 187 | TIMER_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init); |
aea237bf CD |
188 | /* |
189 | * bcm,kona-timer is deprecated by brcm,kona-timer | |
190 | * being kept here for driver compatibility | |
191 | */ | |
17273395 | 192 | TIMER_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init); |