diff options
-rw-r--r-- | Documentation/powerpc/booting-without-of.txt | 32 | ||||
-rw-r--r-- | arch/powerpc/Kconfig | 6 | ||||
-rw-r--r-- | arch/powerpc/sysdev/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_gtm.c | 434 | ||||
-rw-r--r-- | include/asm-powerpc/fsl_gtm.h | 47 |
5 files changed, 520 insertions, 0 deletions
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index 948f6417a40b..8675ebca2cfd 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt | |||
@@ -61,6 +61,7 @@ Table of Contents | |||
61 | r) Freescale Display Interface Unit | 61 | r) Freescale Display Interface Unit |
62 | s) Freescale on board FPGA | 62 | s) Freescale on board FPGA |
63 | t) Freescael MSI interrupt controller | 63 | t) Freescael MSI interrupt controller |
64 | u) Freescale General-purpose Timers Module | ||
64 | 65 | ||
65 | VII - Marvell Discovery mv64[345]6x System Controller chips | 66 | VII - Marvell Discovery mv64[345]6x System Controller chips |
66 | 1) The /system-controller node | 67 | 1) The /system-controller node |
@@ -2907,6 +2908,37 @@ platforms are moved over to use the flattened-device-tree model. | |||
2907 | interrupt-parent = <&mpic>; | 2908 | interrupt-parent = <&mpic>; |
2908 | }; | 2909 | }; |
2909 | 2910 | ||
2911 | u) Freescale General-purpose Timers Module | ||
2912 | |||
2913 | Required properties: | ||
2914 | - compatible : should be | ||
2915 | "fsl,<chip>-gtm", "fsl,gtm" for SOC GTMs | ||
2916 | "fsl,<chip>-qe-gtm", "fsl,qe-gtm", "fsl,gtm" for QE GTMs | ||
2917 | "fsl,<chip>-cpm2-gtm", "fsl,cpm2-gtm", "fsl,gtm" for CPM2 GTMs | ||
2918 | - reg : should contain gtm registers location and length (0x40). | ||
2919 | - interrupts : should contain four interrupts. | ||
2920 | - interrupt-parent : interrupt source phandle. | ||
2921 | - clock-frequency : specifies the frequency driving the timer. | ||
2922 | |||
2923 | Example: | ||
2924 | |||
2925 | timer@500 { | ||
2926 | compatible = "fsl,mpc8360-gtm", "fsl,gtm"; | ||
2927 | reg = <0x500 0x40>; | ||
2928 | interrupts = <90 8 78 8 84 8 72 8>; | ||
2929 | interrupt-parent = <&ipic>; | ||
2930 | /* filled by u-boot */ | ||
2931 | clock-frequency = <0>; | ||
2932 | }; | ||
2933 | |||
2934 | timer@440 { | ||
2935 | compatible = "fsl,mpc8360-qe-gtm", "fsl,qe-gtm", "fsl,gtm"; | ||
2936 | reg = <0x440 0x40>; | ||
2937 | interrupts = <12 13 14 15>; | ||
2938 | interrupt-parent = <&qeic>; | ||
2939 | /* filled by u-boot */ | ||
2940 | clock-frequency = <0>; | ||
2941 | }; | ||
2910 | 2942 | ||
2911 | VII - Marvell Discovery mv64[345]6x System Controller chips | 2943 | VII - Marvell Discovery mv64[345]6x System Controller chips |
2912 | =========================================================== | 2944 | =========================================================== |
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 3934e2659407..2cde4e333fd5 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -538,6 +538,12 @@ config FSL_LBC | |||
538 | help | 538 | help |
539 | Freescale Localbus support | 539 | Freescale Localbus support |
540 | 540 | ||
541 | config FSL_GTM | ||
542 | bool | ||
543 | depends on PPC_83xx || QUICC_ENGINE || CPM2 | ||
544 | help | ||
545 | Freescale General-purpose Timers support | ||
546 | |||
541 | # Yes MCA RS/6000s exist but Linux-PPC does not currently support any | 547 | # Yes MCA RS/6000s exist but Linux-PPC does not currently support any |
542 | config MCA | 548 | config MCA |
543 | bool | 549 | bool |
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index dd6dff3ffb0f..2bfbb39380e9 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o | |||
14 | obj-$(CONFIG_FSL_SOC) += fsl_soc.o | 14 | obj-$(CONFIG_FSL_SOC) += fsl_soc.o |
15 | obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) | 15 | obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) |
16 | obj-$(CONFIG_FSL_LBC) += fsl_lbc.o | 16 | obj-$(CONFIG_FSL_LBC) += fsl_lbc.o |
17 | obj-$(CONFIG_FSL_GTM) += fsl_gtm.o | ||
17 | obj-$(CONFIG_RAPIDIO) += fsl_rio.o | 18 | obj-$(CONFIG_RAPIDIO) += fsl_rio.o |
18 | obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o | 19 | obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o |
19 | obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ | 20 | obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ |
diff --git a/arch/powerpc/sysdev/fsl_gtm.c b/arch/powerpc/sysdev/fsl_gtm.c new file mode 100644 index 000000000000..714ec02fed2e --- /dev/null +++ b/arch/powerpc/sysdev/fsl_gtm.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Freescale General-purpose Timers Module | ||
3 | * | ||
4 | * Copyright (c) Freescale Semicondutor, Inc. 2006. | ||
5 | * Shlomi Gridish <gridish@freescale.com> | ||
6 | * Jerry Huang <Chang-Ming.Huang@freescale.com> | ||
7 | * Copyright (c) MontaVista Software, Inc. 2008. | ||
8 | * Anton Vorontsov <avorontsov@ru.mvista.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/bitops.h> | ||
23 | #include <asm/fsl_gtm.h> | ||
24 | |||
25 | #define GTCFR_STP(x) ((x) & 1 ? 1 << 5 : 1 << 1) | ||
26 | #define GTCFR_RST(x) ((x) & 1 ? 1 << 4 : 1 << 0) | ||
27 | |||
28 | #define GTMDR_ICLK_MASK (3 << 1) | ||
29 | #define GTMDR_ICLK_ICAS (0 << 1) | ||
30 | #define GTMDR_ICLK_ICLK (1 << 1) | ||
31 | #define GTMDR_ICLK_SLGO (2 << 1) | ||
32 | #define GTMDR_FRR (1 << 3) | ||
33 | #define GTMDR_ORI (1 << 4) | ||
34 | #define GTMDR_SPS(x) ((x) << 8) | ||
35 | |||
36 | struct gtm_timers_regs { | ||
37 | u8 gtcfr1; /* Timer 1, Timer 2 global config register */ | ||
38 | u8 res0[0x3]; | ||
39 | u8 gtcfr2; /* Timer 3, timer 4 global config register */ | ||
40 | u8 res1[0xB]; | ||
41 | __be16 gtmdr1; /* Timer 1 mode register */ | ||
42 | __be16 gtmdr2; /* Timer 2 mode register */ | ||
43 | __be16 gtrfr1; /* Timer 1 reference register */ | ||
44 | __be16 gtrfr2; /* Timer 2 reference register */ | ||
45 | __be16 gtcpr1; /* Timer 1 capture register */ | ||
46 | __be16 gtcpr2; /* Timer 2 capture register */ | ||
47 | __be16 gtcnr1; /* Timer 1 counter */ | ||
48 | __be16 gtcnr2; /* Timer 2 counter */ | ||
49 | __be16 gtmdr3; /* Timer 3 mode register */ | ||
50 | __be16 gtmdr4; /* Timer 4 mode register */ | ||
51 | __be16 gtrfr3; /* Timer 3 reference register */ | ||
52 | __be16 gtrfr4; /* Timer 4 reference register */ | ||
53 | __be16 gtcpr3; /* Timer 3 capture register */ | ||
54 | __be16 gtcpr4; /* Timer 4 capture register */ | ||
55 | __be16 gtcnr3; /* Timer 3 counter */ | ||
56 | __be16 gtcnr4; /* Timer 4 counter */ | ||
57 | __be16 gtevr1; /* Timer 1 event register */ | ||
58 | __be16 gtevr2; /* Timer 2 event register */ | ||
59 | __be16 gtevr3; /* Timer 3 event register */ | ||
60 | __be16 gtevr4; /* Timer 4 event register */ | ||
61 | __be16 gtpsr1; /* Timer 1 prescale register */ | ||
62 | __be16 gtpsr2; /* Timer 2 prescale register */ | ||
63 | __be16 gtpsr3; /* Timer 3 prescale register */ | ||
64 | __be16 gtpsr4; /* Timer 4 prescale register */ | ||
65 | u8 res2[0x40]; | ||
66 | } __attribute__ ((packed)); | ||
67 | |||
68 | struct gtm { | ||
69 | unsigned int clock; | ||
70 | struct gtm_timers_regs __iomem *regs; | ||
71 | struct gtm_timer timers[4]; | ||
72 | spinlock_t lock; | ||
73 | struct list_head list_node; | ||
74 | }; | ||
75 | |||
76 | static LIST_HEAD(gtms); | ||
77 | |||
78 | /** | ||
79 | * gtm_get_timer - request GTM timer to use it with the rest of GTM API | ||
80 | * Context: non-IRQ | ||
81 | * | ||
82 | * This function reserves GTM timer for later use. It returns gtm_timer | ||
83 | * structure to use with the rest of GTM API, you should use timer->irq | ||
84 | * to manage timer interrupt. | ||
85 | */ | ||
86 | struct gtm_timer *gtm_get_timer16(void) | ||
87 | { | ||
88 | struct gtm *gtm = NULL; | ||
89 | int i; | ||
90 | |||
91 | list_for_each_entry(gtm, >ms, list_node) { | ||
92 | spin_lock_irq(>m->lock); | ||
93 | |||
94 | for (i = 0; i < ARRAY_SIZE(gtm->timers); i++) { | ||
95 | if (!gtm->timers[i].requested) { | ||
96 | gtm->timers[i].requested = true; | ||
97 | spin_unlock_irq(>m->lock); | ||
98 | return >m->timers[i]; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | spin_unlock_irq(>m->lock); | ||
103 | } | ||
104 | |||
105 | if (gtm) | ||
106 | return ERR_PTR(-EBUSY); | ||
107 | return ERR_PTR(-ENODEV); | ||
108 | } | ||
109 | EXPORT_SYMBOL(gtm_get_timer16); | ||
110 | |||
111 | /** | ||
112 | * gtm_get_specific_timer - request specific GTM timer | ||
113 | * @gtm: specific GTM, pass here GTM's device_node->data | ||
114 | * @timer: specific timer number, Timer1 is 0. | ||
115 | * Context: non-IRQ | ||
116 | * | ||
117 | * This function reserves GTM timer for later use. It returns gtm_timer | ||
118 | * structure to use with the rest of GTM API, you should use timer->irq | ||
119 | * to manage timer interrupt. | ||
120 | */ | ||
121 | struct gtm_timer *gtm_get_specific_timer16(struct gtm *gtm, | ||
122 | unsigned int timer) | ||
123 | { | ||
124 | struct gtm_timer *ret = ERR_PTR(-EBUSY); | ||
125 | |||
126 | if (timer > 3) | ||
127 | return ERR_PTR(-EINVAL); | ||
128 | |||
129 | spin_lock_irq(>m->lock); | ||
130 | |||
131 | if (gtm->timers[timer].requested) | ||
132 | goto out; | ||
133 | |||
134 | ret = >m->timers[timer]; | ||
135 | ret->requested = true; | ||
136 | |||
137 | out: | ||
138 | spin_unlock_irq(>m->lock); | ||
139 | return ret; | ||
140 | } | ||
141 | EXPORT_SYMBOL(gtm_get_specific_timer16); | ||
142 | |||
143 | /** | ||
144 | * gtm_put_timer16 - release 16 bits GTM timer | ||
145 | * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer | ||
146 | * Context: any | ||
147 | * | ||
148 | * This function releases GTM timer so others may request it. | ||
149 | */ | ||
150 | void gtm_put_timer16(struct gtm_timer *tmr) | ||
151 | { | ||
152 | gtm_stop_timer16(tmr); | ||
153 | |||
154 | spin_lock_irq(&tmr->gtm->lock); | ||
155 | tmr->requested = false; | ||
156 | spin_unlock_irq(&tmr->gtm->lock); | ||
157 | } | ||
158 | EXPORT_SYMBOL(gtm_put_timer16); | ||
159 | |||
160 | /* | ||
161 | * This is back-end for the exported functions, it's used to reset single | ||
162 | * timer in reference mode. | ||
163 | */ | ||
164 | static int gtm_set_ref_timer16(struct gtm_timer *tmr, int frequency, | ||
165 | int reference_value, bool free_run) | ||
166 | { | ||
167 | struct gtm *gtm = tmr->gtm; | ||
168 | int num = tmr - >m->timers[0]; | ||
169 | unsigned int prescaler; | ||
170 | u8 iclk = GTMDR_ICLK_ICLK; | ||
171 | u8 psr; | ||
172 | u8 sps; | ||
173 | unsigned long flags; | ||
174 | int max_prescaler = 256 * 256 * 16; | ||
175 | |||
176 | /* CPM2 doesn't have primary prescaler */ | ||
177 | if (!tmr->gtpsr) | ||
178 | max_prescaler /= 256; | ||
179 | |||
180 | prescaler = gtm->clock / frequency; | ||
181 | /* | ||
182 | * We have two 8 bit prescalers -- primary and secondary (psr, sps), | ||
183 | * plus "slow go" mode (clk / 16). So, total prescale value is | ||
184 | * 16 * (psr + 1) * (sps + 1). Though, for CPM2 GTMs we losing psr. | ||
185 | */ | ||
186 | if (prescaler > max_prescaler) | ||
187 | return -EINVAL; | ||
188 | |||
189 | if (prescaler > max_prescaler / 16) { | ||
190 | iclk = GTMDR_ICLK_SLGO; | ||
191 | prescaler /= 16; | ||
192 | } | ||
193 | |||
194 | if (prescaler <= 256) { | ||
195 | psr = 0; | ||
196 | sps = prescaler - 1; | ||
197 | } else { | ||
198 | psr = 256 - 1; | ||
199 | sps = prescaler / 256 - 1; | ||
200 | } | ||
201 | |||
202 | spin_lock_irqsave(>m->lock, flags); | ||
203 | |||
204 | /* | ||
205 | * Properly reset timers: stop, reset, set up prescalers, reference | ||
206 | * value and clear event register. | ||
207 | */ | ||
208 | clrsetbits_8(tmr->gtcfr, ~(GTCFR_STP(num) | GTCFR_RST(num)), | ||
209 | GTCFR_STP(num) | GTCFR_RST(num)); | ||
210 | |||
211 | setbits8(tmr->gtcfr, GTCFR_STP(num)); | ||
212 | |||
213 | if (tmr->gtpsr) | ||
214 | out_be16(tmr->gtpsr, psr); | ||
215 | clrsetbits_be16(tmr->gtmdr, 0xFFFF, iclk | GTMDR_SPS(sps) | | ||
216 | GTMDR_ORI | (free_run ? GTMDR_FRR : 0)); | ||
217 | out_be16(tmr->gtcnr, 0); | ||
218 | out_be16(tmr->gtrfr, reference_value); | ||
219 | out_be16(tmr->gtevr, 0xFFFF); | ||
220 | |||
221 | /* Let it be. */ | ||
222 | clrbits8(tmr->gtcfr, GTCFR_STP(num)); | ||
223 | |||
224 | spin_unlock_irqrestore(>m->lock, flags); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * gtm_set_timer16 - (re)set 16 bit timer with arbitrary precision | ||
231 | * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer | ||
232 | * @usec: timer interval in microseconds | ||
233 | * @reload: if set, the timer will reset upon expiry rather than | ||
234 | * continue running free. | ||
235 | * Context: any | ||
236 | * | ||
237 | * This function (re)sets the GTM timer so that it counts up to the requested | ||
238 | * interval value, and fires the interrupt when the value is reached. This | ||
239 | * function will reduce the precision of the timer as needed in order for the | ||
240 | * requested timeout to fit in a 16-bit register. | ||
241 | */ | ||
242 | int gtm_set_timer16(struct gtm_timer *tmr, unsigned long usec, bool reload) | ||
243 | { | ||
244 | /* quite obvious, frequency which is enough for µSec precision */ | ||
245 | int freq = 1000000; | ||
246 | unsigned int bit; | ||
247 | |||
248 | bit = fls_long(usec); | ||
249 | if (bit > 15) { | ||
250 | freq >>= bit - 15; | ||
251 | usec >>= bit - 15; | ||
252 | } | ||
253 | |||
254 | if (!freq) | ||
255 | return -EINVAL; | ||
256 | |||
257 | return gtm_set_ref_timer16(tmr, freq, usec, reload); | ||
258 | } | ||
259 | EXPORT_SYMBOL(gtm_set_timer16); | ||
260 | |||
261 | /** | ||
262 | * gtm_set_exact_utimer16 - (re)set 16 bits timer | ||
263 | * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer | ||
264 | * @usec: timer interval in microseconds | ||
265 | * @reload: if set, the timer will reset upon expiry rather than | ||
266 | * continue running free. | ||
267 | * Context: any | ||
268 | * | ||
269 | * This function (re)sets GTM timer so that it counts up to the requested | ||
270 | * interval value, and fires the interrupt when the value is reached. If reload | ||
271 | * flag was set, timer will also reset itself upon reference value, otherwise | ||
272 | * it continues to increment. | ||
273 | * | ||
274 | * The _exact_ bit in the function name states that this function will not | ||
275 | * crop precision of the "usec" argument, thus usec is limited to 16 bits | ||
276 | * (single timer width). | ||
277 | */ | ||
278 | int gtm_set_exact_timer16(struct gtm_timer *tmr, u16 usec, bool reload) | ||
279 | { | ||
280 | /* quite obvious, frequency which is enough for µSec precision */ | ||
281 | const int freq = 1000000; | ||
282 | |||
283 | /* | ||
284 | * We can lower the frequency (and probably power consumption) by | ||
285 | * dividing both frequency and usec by 2 until there is no remainder. | ||
286 | * But we won't bother with this unless savings are measured, so just | ||
287 | * run the timer as is. | ||
288 | */ | ||
289 | |||
290 | return gtm_set_ref_timer16(tmr, freq, usec, reload); | ||
291 | } | ||
292 | EXPORT_SYMBOL(gtm_set_exact_timer16); | ||
293 | |||
294 | /** | ||
295 | * gtm_stop_timer16 - stop single timer | ||
296 | * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer | ||
297 | * Context: any | ||
298 | * | ||
299 | * This function simply stops the GTM timer. | ||
300 | */ | ||
301 | void gtm_stop_timer16(struct gtm_timer *tmr) | ||
302 | { | ||
303 | struct gtm *gtm = tmr->gtm; | ||
304 | int num = tmr - >m->timers[0]; | ||
305 | unsigned long flags; | ||
306 | |||
307 | spin_lock_irqsave(>m->lock, flags); | ||
308 | |||
309 | setbits8(tmr->gtcfr, GTCFR_STP(num)); | ||
310 | out_be16(tmr->gtevr, 0xFFFF); | ||
311 | |||
312 | spin_unlock_irqrestore(>m->lock, flags); | ||
313 | } | ||
314 | EXPORT_SYMBOL(gtm_stop_timer16); | ||
315 | |||
316 | /** | ||
317 | * gtm_ack_timer16 - acknowledge timer event (free-run timers only) | ||
318 | * @tmr: pointer to the gtm_timer structure obtained from gtm_get_timer | ||
319 | * @events: events mask to ack | ||
320 | * Context: any | ||
321 | * | ||
322 | * Thus function used to acknowledge timer interrupt event, use it inside the | ||
323 | * interrupt handler. | ||
324 | */ | ||
325 | void gtm_ack_timer16(struct gtm_timer *tmr, u16 events) | ||
326 | { | ||
327 | out_be16(tmr->gtevr, events); | ||
328 | } | ||
329 | EXPORT_SYMBOL(gtm_ack_timer16); | ||
330 | |||
331 | static void __init gtm_set_shortcuts(struct device_node *np, | ||
332 | struct gtm_timer *timers, | ||
333 | struct gtm_timers_regs __iomem *regs) | ||
334 | { | ||
335 | /* | ||
336 | * Yeah, I don't like this either, but timers' registers a bit messed, | ||
337 | * so we have to provide shortcuts to write timer independent code. | ||
338 | * Alternative option is to create gt*() accessors, but that will be | ||
339 | * even uglier and cryptic. | ||
340 | */ | ||
341 | timers[0].gtcfr = ®s->gtcfr1; | ||
342 | timers[0].gtmdr = ®s->gtmdr1; | ||
343 | timers[0].gtcnr = ®s->gtcnr1; | ||
344 | timers[0].gtrfr = ®s->gtrfr1; | ||
345 | timers[0].gtevr = ®s->gtevr1; | ||
346 | |||
347 | timers[1].gtcfr = ®s->gtcfr1; | ||
348 | timers[1].gtmdr = ®s->gtmdr2; | ||
349 | timers[1].gtcnr = ®s->gtcnr2; | ||
350 | timers[1].gtrfr = ®s->gtrfr2; | ||
351 | timers[1].gtevr = ®s->gtevr2; | ||
352 | |||
353 | timers[2].gtcfr = ®s->gtcfr2; | ||
354 | timers[2].gtmdr = ®s->gtmdr3; | ||
355 | timers[2].gtcnr = ®s->gtcnr3; | ||
356 | timers[2].gtrfr = ®s->gtrfr3; | ||
357 | timers[2].gtevr = ®s->gtevr3; | ||
358 | |||
359 | timers[3].gtcfr = ®s->gtcfr2; | ||
360 | timers[3].gtmdr = ®s->gtmdr4; | ||
361 | timers[3].gtcnr = ®s->gtcnr4; | ||
362 | timers[3].gtrfr = ®s->gtrfr4; | ||
363 | timers[3].gtevr = ®s->gtevr4; | ||
364 | |||
365 | /* CPM2 doesn't have primary prescaler */ | ||
366 | if (!of_device_is_compatible(np, "fsl,cpm2-gtm")) { | ||
367 | timers[0].gtpsr = ®s->gtpsr1; | ||
368 | timers[1].gtpsr = ®s->gtpsr2; | ||
369 | timers[2].gtpsr = ®s->gtpsr3; | ||
370 | timers[3].gtpsr = ®s->gtpsr4; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | static int __init fsl_gtm_init(void) | ||
375 | { | ||
376 | struct device_node *np; | ||
377 | |||
378 | for_each_compatible_node(np, NULL, "fsl,gtm") { | ||
379 | int i; | ||
380 | struct gtm *gtm; | ||
381 | const u32 *clock; | ||
382 | int size; | ||
383 | |||
384 | gtm = kzalloc(sizeof(*gtm), GFP_KERNEL); | ||
385 | if (!gtm) { | ||
386 | pr_err("%s: unable to allocate memory\n", | ||
387 | np->full_name); | ||
388 | continue; | ||
389 | } | ||
390 | |||
391 | spin_lock_init(>m->lock); | ||
392 | |||
393 | clock = of_get_property(np, "clock-frequency", &size); | ||
394 | if (!clock || size != sizeof(*clock)) { | ||
395 | pr_err("%s: no clock-frequency\n", np->full_name); | ||
396 | goto err; | ||
397 | } | ||
398 | gtm->clock = *clock; | ||
399 | |||
400 | for (i = 0; i < ARRAY_SIZE(gtm->timers); i++) { | ||
401 | int ret; | ||
402 | struct resource irq; | ||
403 | |||
404 | ret = of_irq_to_resource(np, i, &irq); | ||
405 | if (ret == NO_IRQ) { | ||
406 | pr_err("%s: not enough interrupts specified\n", | ||
407 | np->full_name); | ||
408 | goto err; | ||
409 | } | ||
410 | gtm->timers[i].irq = irq.start; | ||
411 | gtm->timers[i].gtm = gtm; | ||
412 | } | ||
413 | |||
414 | gtm->regs = of_iomap(np, 0); | ||
415 | if (!gtm->regs) { | ||
416 | pr_err("%s: unable to iomap registers\n", | ||
417 | np->full_name); | ||
418 | goto err; | ||
419 | } | ||
420 | |||
421 | gtm_set_shortcuts(np, gtm->timers, gtm->regs); | ||
422 | list_add(>m->list_node, >ms); | ||
423 | |||
424 | /* We don't want to lose the node and its ->data */ | ||
425 | np->data = gtm; | ||
426 | of_node_get(np); | ||
427 | |||
428 | continue; | ||
429 | err: | ||
430 | kfree(gtm); | ||
431 | } | ||
432 | return 0; | ||
433 | } | ||
434 | arch_initcall(fsl_gtm_init); | ||
diff --git a/include/asm-powerpc/fsl_gtm.h b/include/asm-powerpc/fsl_gtm.h new file mode 100644 index 000000000000..8e8c9b5032d3 --- /dev/null +++ b/include/asm-powerpc/fsl_gtm.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Freescale General-purpose Timers Module | ||
3 | * | ||
4 | * Copyright (c) Freescale Semicondutor, Inc. 2006. | ||
5 | * Shlomi Gridish <gridish@freescale.com> | ||
6 | * Jerry Huang <Chang-Ming.Huang@freescale.com> | ||
7 | * Copyright (c) MontaVista Software, Inc. 2008. | ||
8 | * Anton Vorontsov <avorontsov@ru.mvista.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #ifndef __ASM_FSL_GTM_H | ||
17 | #define __ASM_FSL_GTM_H | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | |||
21 | struct gtm; | ||
22 | |||
23 | struct gtm_timer { | ||
24 | unsigned int irq; | ||
25 | |||
26 | struct gtm *gtm; | ||
27 | bool requested; | ||
28 | u8 __iomem *gtcfr; | ||
29 | __be16 __iomem *gtmdr; | ||
30 | __be16 __iomem *gtpsr; | ||
31 | __be16 __iomem *gtcnr; | ||
32 | __be16 __iomem *gtrfr; | ||
33 | __be16 __iomem *gtevr; | ||
34 | }; | ||
35 | |||
36 | extern struct gtm_timer *gtm_get_timer16(void); | ||
37 | extern struct gtm_timer *gtm_get_specific_timer16(struct gtm *gtm, | ||
38 | unsigned int timer); | ||
39 | extern void gtm_put_timer16(struct gtm_timer *tmr); | ||
40 | extern int gtm_set_timer16(struct gtm_timer *tmr, unsigned long usec, | ||
41 | bool reload); | ||
42 | extern int gtm_set_exact_timer16(struct gtm_timer *tmr, u16 usec, | ||
43 | bool reload); | ||
44 | extern void gtm_stop_timer16(struct gtm_timer *tmr); | ||
45 | extern void gtm_ack_timer16(struct gtm_timer *tmr, u16 events); | ||
46 | |||
47 | #endif /* __ASM_FSL_GTM_H */ | ||