diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2009-11-04 18:42:33 -0500 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2009-11-04 18:42:33 -0500 |
commit | 4f59ecfa9b87da09bdc346f2c443e25fa2c0674c (patch) | |
tree | 39a935e77cfe9119f9b6b2aa9ab6ded6ef09a897 | |
parent | 42bbb70980f3720b0ae6da6af862af0e95a04351 (diff) |
powerpc/5200: add general purpose timer API for the MPC5200
This patch adds an interface for controlling the timer function of the
MPC5200 GPT devices.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
-rw-r--r-- | arch/powerpc/include/asm/mpc52xx.h | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_gpt.c | 133 |
2 files changed, 130 insertions, 10 deletions
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h index 1b4f697abbdd..671685011a23 100644 --- a/arch/powerpc/include/asm/mpc52xx.h +++ b/arch/powerpc/include/asm/mpc52xx.h | |||
@@ -276,6 +276,13 @@ extern int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv); | |||
276 | extern unsigned int mpc52xx_get_xtal_freq(struct device_node *node); | 276 | extern unsigned int mpc52xx_get_xtal_freq(struct device_node *node); |
277 | extern void mpc52xx_restart(char *cmd); | 277 | extern void mpc52xx_restart(char *cmd); |
278 | 278 | ||
279 | /* mpc52xx_gpt.c */ | ||
280 | struct mpc52xx_gpt_priv; | ||
281 | extern struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq); | ||
282 | extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period, | ||
283 | int continuous); | ||
284 | extern void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt); | ||
285 | |||
279 | /* mpc52xx_pic.c */ | 286 | /* mpc52xx_pic.c */ |
280 | extern void mpc52xx_init_irq(void); | 287 | extern void mpc52xx_init_irq(void); |
281 | extern unsigned int mpc52xx_get_irq(void); | 288 | extern unsigned int mpc52xx_get_irq(void); |
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c index bfbcd418e690..2c3fa13571ce 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c | |||
@@ -46,13 +46,17 @@ | |||
46 | * the output mode. This driver does not change the output mode setting. | 46 | * the output mode. This driver does not change the output mode setting. |
47 | */ | 47 | */ |
48 | 48 | ||
49 | #include <linux/device.h> | ||
49 | #include <linux/irq.h> | 50 | #include <linux/irq.h> |
50 | #include <linux/interrupt.h> | 51 | #include <linux/interrupt.h> |
51 | #include <linux/io.h> | 52 | #include <linux/io.h> |
53 | #include <linux/list.h> | ||
54 | #include <linux/mutex.h> | ||
52 | #include <linux/of.h> | 55 | #include <linux/of.h> |
53 | #include <linux/of_platform.h> | 56 | #include <linux/of_platform.h> |
54 | #include <linux/of_gpio.h> | 57 | #include <linux/of_gpio.h> |
55 | #include <linux/kernel.h> | 58 | #include <linux/kernel.h> |
59 | #include <asm/div64.h> | ||
56 | #include <asm/mpc52xx.h> | 60 | #include <asm/mpc52xx.h> |
57 | 61 | ||
58 | MODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); | 62 | MODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); |
@@ -68,16 +72,21 @@ MODULE_LICENSE("GPL"); | |||
68 | * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported | 72 | * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported |
69 | */ | 73 | */ |
70 | struct mpc52xx_gpt_priv { | 74 | struct mpc52xx_gpt_priv { |
75 | struct list_head list; /* List of all GPT devices */ | ||
71 | struct device *dev; | 76 | struct device *dev; |
72 | struct mpc52xx_gpt __iomem *regs; | 77 | struct mpc52xx_gpt __iomem *regs; |
73 | spinlock_t lock; | 78 | spinlock_t lock; |
74 | struct irq_host *irqhost; | 79 | struct irq_host *irqhost; |
80 | u32 ipb_freq; | ||
75 | 81 | ||
76 | #if defined(CONFIG_GPIOLIB) | 82 | #if defined(CONFIG_GPIOLIB) |
77 | struct of_gpio_chip of_gc; | 83 | struct of_gpio_chip of_gc; |
78 | #endif | 84 | #endif |
79 | }; | 85 | }; |
80 | 86 | ||
87 | LIST_HEAD(mpc52xx_gpt_list); | ||
88 | DEFINE_MUTEX(mpc52xx_gpt_list_mutex); | ||
89 | |||
81 | #define MPC52xx_GPT_MODE_MS_MASK (0x07) | 90 | #define MPC52xx_GPT_MODE_MS_MASK (0x07) |
82 | #define MPC52xx_GPT_MODE_MS_IC (0x01) | 91 | #define MPC52xx_GPT_MODE_MS_IC (0x01) |
83 | #define MPC52xx_GPT_MODE_MS_OC (0x02) | 92 | #define MPC52xx_GPT_MODE_MS_OC (0x02) |
@@ -88,6 +97,9 @@ struct mpc52xx_gpt_priv { | |||
88 | #define MPC52xx_GPT_MODE_GPIO_OUT_LOW (0x20) | 97 | #define MPC52xx_GPT_MODE_GPIO_OUT_LOW (0x20) |
89 | #define MPC52xx_GPT_MODE_GPIO_OUT_HIGH (0x30) | 98 | #define MPC52xx_GPT_MODE_GPIO_OUT_HIGH (0x30) |
90 | 99 | ||
100 | #define MPC52xx_GPT_MODE_COUNTER_ENABLE (0x1000) | ||
101 | #define MPC52xx_GPT_MODE_CONTINUOUS (0x0400) | ||
102 | #define MPC52xx_GPT_MODE_OPEN_DRAIN (0x0200) | ||
91 | #define MPC52xx_GPT_MODE_IRQ_EN (0x0100) | 103 | #define MPC52xx_GPT_MODE_IRQ_EN (0x0100) |
92 | 104 | ||
93 | #define MPC52xx_GPT_MODE_ICT_MASK (0x030000) | 105 | #define MPC52xx_GPT_MODE_ICT_MASK (0x030000) |
@@ -190,7 +202,7 @@ static int mpc52xx_gpt_irq_xlate(struct irq_host *h, struct device_node *ct, | |||
190 | 202 | ||
191 | dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]); | 203 | dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]); |
192 | 204 | ||
193 | if ((intsize < 1) || (intspec[0] < 1) || (intspec[0] > 3)) { | 205 | if ((intsize < 1) || (intspec[0] > 3)) { |
194 | dev_err(gpt->dev, "bad irq specifier in %s\n", ct->full_name); | 206 | dev_err(gpt->dev, "bad irq specifier in %s\n", ct->full_name); |
195 | return -EINVAL; | 207 | return -EINVAL; |
196 | } | 208 | } |
@@ -211,13 +223,11 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) | |||
211 | { | 223 | { |
212 | int cascade_virq; | 224 | int cascade_virq; |
213 | unsigned long flags; | 225 | unsigned long flags; |
214 | 226 | u32 mode; | |
215 | /* Only setup cascaded IRQ if device tree claims the GPT is | ||
216 | * an interrupt controller */ | ||
217 | if (!of_find_property(node, "interrupt-controller", NULL)) | ||
218 | return; | ||
219 | 227 | ||
220 | cascade_virq = irq_of_parse_and_map(node, 0); | 228 | cascade_virq = irq_of_parse_and_map(node, 0); |
229 | if (!cascade_virq) | ||
230 | return; | ||
221 | 231 | ||
222 | gpt->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, 1, | 232 | gpt->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, 1, |
223 | &mpc52xx_gpt_irq_ops, -1); | 233 | &mpc52xx_gpt_irq_ops, -1); |
@@ -227,14 +237,16 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) | |||
227 | } | 237 | } |
228 | 238 | ||
229 | gpt->irqhost->host_data = gpt; | 239 | gpt->irqhost->host_data = gpt; |
230 | |||
231 | set_irq_data(cascade_virq, gpt); | 240 | set_irq_data(cascade_virq, gpt); |
232 | set_irq_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade); | 241 | set_irq_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade); |
233 | 242 | ||
234 | /* Set to Input Capture mode */ | 243 | /* If the GPT is currently disabled, then change it to be in Input |
244 | * Capture mode. If the mode is non-zero, then the pin could be | ||
245 | * already in use for something. */ | ||
235 | spin_lock_irqsave(&gpt->lock, flags); | 246 | spin_lock_irqsave(&gpt->lock, flags); |
236 | clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK, | 247 | mode = in_be32(&gpt->regs->mode); |
237 | MPC52xx_GPT_MODE_MS_IC); | 248 | if ((mode & MPC52xx_GPT_MODE_MS_MASK) == 0) |
249 | out_be32(&gpt->regs->mode, mode | MPC52xx_GPT_MODE_MS_IC); | ||
238 | spin_unlock_irqrestore(&gpt->lock, flags); | 250 | spin_unlock_irqrestore(&gpt->lock, flags); |
239 | 251 | ||
240 | dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq); | 252 | dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq); |
@@ -335,6 +347,102 @@ static void | |||
335 | mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) { } | 347 | mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) { } |
336 | #endif /* defined(CONFIG_GPIOLIB) */ | 348 | #endif /* defined(CONFIG_GPIOLIB) */ |
337 | 349 | ||
350 | /*********************************************************************** | ||
351 | * Timer API | ||
352 | */ | ||
353 | |||
354 | /** | ||
355 | * mpc52xx_gpt_from_irq - Return the GPT device associated with an IRQ number | ||
356 | * @irq: irq of timer. | ||
357 | */ | ||
358 | struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq) | ||
359 | { | ||
360 | struct mpc52xx_gpt_priv *gpt; | ||
361 | struct list_head *pos; | ||
362 | |||
363 | /* Iterate over the list of timers looking for a matching device */ | ||
364 | mutex_lock(&mpc52xx_gpt_list_mutex); | ||
365 | list_for_each(pos, &mpc52xx_gpt_list) { | ||
366 | gpt = container_of(pos, struct mpc52xx_gpt_priv, list); | ||
367 | if (gpt->irqhost && irq == irq_linear_revmap(gpt->irqhost, 0)) { | ||
368 | mutex_unlock(&mpc52xx_gpt_list_mutex); | ||
369 | return gpt; | ||
370 | } | ||
371 | } | ||
372 | mutex_unlock(&mpc52xx_gpt_list_mutex); | ||
373 | |||
374 | return NULL; | ||
375 | } | ||
376 | EXPORT_SYMBOL(mpc52xx_gpt_from_irq); | ||
377 | |||
378 | /** | ||
379 | * mpc52xx_gpt_start_timer - Set and enable the GPT timer | ||
380 | * @gpt: Pointer to gpt private data structure | ||
381 | * @period: period of timer | ||
382 | * @continuous: set to 1 to make timer continuous free running | ||
383 | * | ||
384 | * An interrupt will be generated every time the timer fires | ||
385 | */ | ||
386 | int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period, | ||
387 | int continuous) | ||
388 | { | ||
389 | u32 clear, set; | ||
390 | u64 clocks; | ||
391 | u32 prescale; | ||
392 | unsigned long flags; | ||
393 | |||
394 | clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS; | ||
395 | set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE; | ||
396 | if (continuous) | ||
397 | set |= MPC52xx_GPT_MODE_CONTINUOUS; | ||
398 | |||
399 | /* Determine the number of clocks in the requested period. 64 bit | ||
400 | * arithmatic is done here to preserve the precision until the value | ||
401 | * is scaled back down into the u32 range. Period is in 'ns', bus | ||
402 | * frequency is in Hz. */ | ||
403 | clocks = (u64)period * (u64)gpt->ipb_freq; | ||
404 | do_div(clocks, 1000000000); /* Scale it down to ns range */ | ||
405 | |||
406 | /* This device cannot handle a clock count greater than 32 bits */ | ||
407 | if (clocks > 0xffffffff) | ||
408 | return -EINVAL; | ||
409 | |||
410 | /* Calculate the prescaler and count values from the clocks value. | ||
411 | * 'clocks' is the number of clock ticks in the period. The timer | ||
412 | * has 16 bit precision and a 16 bit prescaler. Prescaler is | ||
413 | * calculated by integer dividing the clocks by 0x10000 (shifting | ||
414 | * down 16 bits) to obtain the smallest possible divisor for clocks | ||
415 | * to get a 16 bit count value. | ||
416 | * | ||
417 | * Note: the prescale register is '1' based, not '0' based. ie. a | ||
418 | * value of '1' means divide the clock by one. 0xffff divides the | ||
419 | * clock by 0xffff. '0x0000' does not divide by zero, but wraps | ||
420 | * around and divides by 0x10000. That is why prescale must be | ||
421 | * a u32 variable, not a u16, for this calculation. */ | ||
422 | prescale = (clocks >> 16) + 1; | ||
423 | do_div(clocks, prescale); | ||
424 | if (clocks > 0xffff) { | ||
425 | pr_err("calculation error; prescale:%x clocks:%llx\n", | ||
426 | prescale, clocks); | ||
427 | return -EINVAL; | ||
428 | } | ||
429 | |||
430 | /* Set and enable the timer */ | ||
431 | spin_lock_irqsave(&gpt->lock, flags); | ||
432 | out_be32(&gpt->regs->count, prescale << 16 | clocks); | ||
433 | clrsetbits_be32(&gpt->regs->mode, clear, set); | ||
434 | spin_unlock_irqrestore(&gpt->lock, flags); | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | EXPORT_SYMBOL(mpc52xx_gpt_start_timer); | ||
439 | |||
440 | void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt) | ||
441 | { | ||
442 | clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE); | ||
443 | } | ||
444 | EXPORT_SYMBOL(mpc52xx_gpt_stop_timer); | ||
445 | |||
338 | /* --------------------------------------------------------------------- | 446 | /* --------------------------------------------------------------------- |
339 | * of_platform bus binding code | 447 | * of_platform bus binding code |
340 | */ | 448 | */ |
@@ -349,6 +457,7 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev, | |||
349 | 457 | ||
350 | spin_lock_init(&gpt->lock); | 458 | spin_lock_init(&gpt->lock); |
351 | gpt->dev = &ofdev->dev; | 459 | gpt->dev = &ofdev->dev; |
460 | gpt->ipb_freq = mpc5xxx_get_bus_frequency(ofdev->node); | ||
352 | gpt->regs = of_iomap(ofdev->node, 0); | 461 | gpt->regs = of_iomap(ofdev->node, 0); |
353 | if (!gpt->regs) { | 462 | if (!gpt->regs) { |
354 | kfree(gpt); | 463 | kfree(gpt); |
@@ -360,6 +469,10 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev, | |||
360 | mpc52xx_gpt_gpio_setup(gpt, ofdev->node); | 469 | mpc52xx_gpt_gpio_setup(gpt, ofdev->node); |
361 | mpc52xx_gpt_irq_setup(gpt, ofdev->node); | 470 | mpc52xx_gpt_irq_setup(gpt, ofdev->node); |
362 | 471 | ||
472 | mutex_lock(&mpc52xx_gpt_list_mutex); | ||
473 | list_add(&gpt->list, &mpc52xx_gpt_list); | ||
474 | mutex_unlock(&mpc52xx_gpt_list_mutex); | ||
475 | |||
363 | return 0; | 476 | return 0; |
364 | } | 477 | } |
365 | 478 | ||