diff options
| -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 | ||
