]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
546a3954 AJ |
2 | /* |
3 | * Copyright (C) 2010, 2011 Texas Instruments Incorporated | |
4 | * Contributed by: Mark Salter (msalter@redhat.com) | |
546a3954 AJ |
5 | */ |
6 | ||
7 | #include <linux/clockchips.h> | |
8 | #include <linux/interrupt.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/of_irq.h> | |
12 | #include <linux/of_address.h> | |
13 | #include <asm/soc.h> | |
14 | #include <asm/dscr.h> | |
6a846f3f | 15 | #include <asm/special_insns.h> |
546a3954 AJ |
16 | #include <asm/timer64.h> |
17 | ||
18 | struct timer_regs { | |
19 | u32 reserved0; | |
20 | u32 emumgt; | |
21 | u32 reserved1; | |
22 | u32 reserved2; | |
23 | u32 cntlo; | |
24 | u32 cnthi; | |
25 | u32 prdlo; | |
26 | u32 prdhi; | |
27 | u32 tcr; | |
28 | u32 tgcr; | |
29 | u32 wdtcr; | |
30 | }; | |
31 | ||
32 | static struct timer_regs __iomem *timer; | |
33 | ||
34 | #define TCR_TSTATLO 0x001 | |
35 | #define TCR_INVOUTPLO 0x002 | |
36 | #define TCR_INVINPLO 0x004 | |
37 | #define TCR_CPLO 0x008 | |
38 | #define TCR_ENAMODELO_ONCE 0x040 | |
39 | #define TCR_ENAMODELO_CONT 0x080 | |
40 | #define TCR_ENAMODELO_MASK 0x0c0 | |
41 | #define TCR_PWIDLO_MASK 0x030 | |
42 | #define TCR_CLKSRCLO 0x100 | |
43 | #define TCR_TIENLO 0x200 | |
44 | #define TCR_TSTATHI (0x001 << 16) | |
45 | #define TCR_INVOUTPHI (0x002 << 16) | |
46 | #define TCR_CPHI (0x008 << 16) | |
47 | #define TCR_PWIDHI_MASK (0x030 << 16) | |
48 | #define TCR_ENAMODEHI_ONCE (0x040 << 16) | |
49 | #define TCR_ENAMODEHI_CONT (0x080 << 16) | |
50 | #define TCR_ENAMODEHI_MASK (0x0c0 << 16) | |
51 | ||
52 | #define TGCR_TIMLORS 0x001 | |
53 | #define TGCR_TIMHIRS 0x002 | |
54 | #define TGCR_TIMMODE_UD32 0x004 | |
55 | #define TGCR_TIMMODE_WDT64 0x008 | |
56 | #define TGCR_TIMMODE_CD32 0x00c | |
57 | #define TGCR_TIMMODE_MASK 0x00c | |
58 | #define TGCR_PSCHI_MASK (0x00f << 8) | |
59 | #define TGCR_TDDRHI_MASK (0x00f << 12) | |
60 | ||
61 | /* | |
62 | * Timer clocks are divided down from the CPU clock | |
63 | * The divisor is in the EMUMGTCLKSPD register | |
64 | */ | |
65 | #define TIMER_DIVISOR \ | |
66 | ((soc_readl(&timer->emumgt) & (0xf << 16)) >> 16) | |
67 | ||
68 | #define TIMER64_RATE (c6x_core_freq / TIMER_DIVISOR) | |
69 | ||
70 | #define TIMER64_MODE_DISABLED 0 | |
71 | #define TIMER64_MODE_ONE_SHOT TCR_ENAMODELO_ONCE | |
72 | #define TIMER64_MODE_PERIODIC TCR_ENAMODELO_CONT | |
73 | ||
74 | static int timer64_mode; | |
75 | static int timer64_devstate_id = -1; | |
76 | ||
77 | static void timer64_config(unsigned long period) | |
78 | { | |
79 | u32 tcr = soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK; | |
80 | ||
81 | soc_writel(tcr, &timer->tcr); | |
82 | soc_writel(period - 1, &timer->prdlo); | |
83 | soc_writel(0, &timer->cntlo); | |
84 | tcr |= timer64_mode; | |
85 | soc_writel(tcr, &timer->tcr); | |
86 | } | |
87 | ||
88 | static void timer64_enable(void) | |
89 | { | |
90 | u32 val; | |
91 | ||
92 | if (timer64_devstate_id >= 0) | |
93 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); | |
94 | ||
95 | /* disable timer, reset count */ | |
96 | soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); | |
97 | soc_writel(0, &timer->prdlo); | |
98 | ||
99 | /* use internal clock and 1 cycle pulse width */ | |
100 | val = soc_readl(&timer->tcr); | |
101 | soc_writel(val & ~(TCR_CLKSRCLO | TCR_PWIDLO_MASK), &timer->tcr); | |
102 | ||
103 | /* dual 32-bit unchained mode */ | |
104 | val = soc_readl(&timer->tgcr) & ~TGCR_TIMMODE_MASK; | |
105 | soc_writel(val, &timer->tgcr); | |
106 | soc_writel(val | (TGCR_TIMLORS | TGCR_TIMMODE_UD32), &timer->tgcr); | |
107 | } | |
108 | ||
109 | static void timer64_disable(void) | |
110 | { | |
111 | /* disable timer, reset count */ | |
112 | soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); | |
113 | soc_writel(0, &timer->prdlo); | |
114 | ||
115 | if (timer64_devstate_id >= 0) | |
116 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_DISABLED); | |
117 | } | |
118 | ||
119 | static int next_event(unsigned long delta, | |
120 | struct clock_event_device *evt) | |
121 | { | |
122 | timer64_config(delta); | |
123 | return 0; | |
124 | } | |
125 | ||
677e6fe0 | 126 | static int set_periodic(struct clock_event_device *evt) |
546a3954 | 127 | { |
677e6fe0 VK |
128 | timer64_enable(); |
129 | timer64_mode = TIMER64_MODE_PERIODIC; | |
130 | timer64_config(TIMER64_RATE / HZ); | |
131 | return 0; | |
132 | } | |
133 | ||
134 | static int set_oneshot(struct clock_event_device *evt) | |
135 | { | |
136 | timer64_enable(); | |
137 | timer64_mode = TIMER64_MODE_ONE_SHOT; | |
138 | return 0; | |
139 | } | |
140 | ||
141 | static int shutdown(struct clock_event_device *evt) | |
142 | { | |
143 | timer64_mode = TIMER64_MODE_DISABLED; | |
144 | timer64_disable(); | |
145 | return 0; | |
546a3954 AJ |
146 | } |
147 | ||
148 | static struct clock_event_device t64_clockevent_device = { | |
677e6fe0 VK |
149 | .name = "TIMER64_EVT32_TIMER", |
150 | .features = CLOCK_EVT_FEAT_ONESHOT | | |
151 | CLOCK_EVT_FEAT_PERIODIC, | |
152 | .rating = 200, | |
153 | .set_state_shutdown = shutdown, | |
154 | .set_state_periodic = set_periodic, | |
155 | .set_state_oneshot = set_oneshot, | |
156 | .set_next_event = next_event, | |
546a3954 AJ |
157 | }; |
158 | ||
159 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | |
160 | { | |
161 | struct clock_event_device *cd = &t64_clockevent_device; | |
162 | ||
163 | cd->event_handler(cd); | |
164 | ||
165 | return IRQ_HANDLED; | |
166 | } | |
167 | ||
168 | static struct irqaction timer_iact = { | |
169 | .name = "timer", | |
170 | .flags = IRQF_TIMER, | |
171 | .handler = timer_interrupt, | |
172 | .dev_id = &t64_clockevent_device, | |
173 | }; | |
174 | ||
175 | void __init timer64_init(void) | |
176 | { | |
177 | struct clock_event_device *cd = &t64_clockevent_device; | |
178 | struct device_node *np, *first = NULL; | |
179 | u32 val; | |
180 | int err, found = 0; | |
181 | ||
182 | for_each_compatible_node(np, NULL, "ti,c64x+timer64") { | |
183 | err = of_property_read_u32(np, "ti,core-mask", &val); | |
184 | if (!err) { | |
185 | if (val & (1 << get_coreid())) { | |
186 | found = 1; | |
187 | break; | |
188 | } | |
189 | } else if (!first) | |
190 | first = np; | |
191 | } | |
192 | if (!found) { | |
193 | /* try first one with no core-mask */ | |
194 | if (first) | |
195 | np = of_node_get(first); | |
196 | else { | |
197 | pr_debug("Cannot find ti,c64x+timer64 timer.\n"); | |
198 | return; | |
199 | } | |
200 | } | |
201 | ||
202 | timer = of_iomap(np, 0); | |
203 | if (!timer) { | |
636d4211 | 204 | pr_debug("%pOF: Cannot map timer registers.\n", np); |
546a3954 AJ |
205 | goto out; |
206 | } | |
636d4211 | 207 | pr_debug("%pOF: Timer registers=%p.\n", np, timer); |
546a3954 AJ |
208 | |
209 | cd->irq = irq_of_parse_and_map(np, 0); | |
210 | if (cd->irq == NO_IRQ) { | |
636d4211 | 211 | pr_debug("%pOF: Cannot find interrupt.\n", np); |
546a3954 AJ |
212 | iounmap(timer); |
213 | goto out; | |
214 | } | |
215 | ||
216 | /* If there is a device state control, save the ID. */ | |
217 | err = of_property_read_u32(np, "ti,dscr-dev-enable", &val); | |
25b48ff8 | 218 | if (!err) { |
546a3954 AJ |
219 | timer64_devstate_id = val; |
220 | ||
25b48ff8 MS |
221 | /* |
222 | * It is necessary to enable the timer block here because | |
223 | * the TIMER_DIVISOR macro needs to read a timer register | |
224 | * to get the divisor. | |
225 | */ | |
226 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); | |
227 | } | |
228 | ||
636d4211 | 229 | pr_debug("%pOF: Timer irq=%d.\n", np, cd->irq); |
546a3954 AJ |
230 | |
231 | clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5); | |
232 | ||
233 | cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); | |
e1e5fc15 | 234 | cd->max_delta_ticks = 0x7fffffff; |
546a3954 | 235 | cd->min_delta_ns = clockevent_delta2ns(250, cd); |
e1e5fc15 | 236 | cd->min_delta_ticks = 250; |
546a3954 AJ |
237 | |
238 | cd->cpumask = cpumask_of(smp_processor_id()); | |
239 | ||
240 | clockevents_register_device(cd); | |
241 | setup_irq(cd->irq, &timer_iact); | |
242 | ||
243 | out: | |
244 | of_node_put(np); | |
245 | return; | |
246 | } |