aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorAlbrecht Dreß <albrecht.dress@arcor.de>2009-11-13 13:09:31 -0500
committerGrant Likely <grant.likely@secretlab.ca>2009-11-13 13:09:31 -0500
commiteda43d16ef3d0bd59e3b762de3ffc73bab02efe9 (patch)
treeb183892818f6106e59e480ba31faf055127e15d7 /arch/powerpc
parent13b600b59df287a175b1476d2d588ab935092b58 (diff)
mpc52xx/wdt: merge WDT code into the GPT driver
Merge the WDT code into the GPT interface. Signed-off-by: Albrecht Dreß <albrecht.dress@arcor.de> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/include/asm/mpc52xx.h3
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_gpt.c321
2 files changed, 307 insertions, 17 deletions
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h
index 933fb8f6e797..b664ce79a172 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -281,7 +281,8 @@ struct mpc52xx_gpt_priv;
281extern struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq); 281extern struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq);
282extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period, 282extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
283 int continuous); 283 int continuous);
284extern void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt); 284extern u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt);
285extern int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt);
285 286
286/* mpc52xx_lpbfifo.c */ 287/* mpc52xx_lpbfifo.c */
287#define MPC52XX_LPBFIFO_FLAG_READ (0) 288#define MPC52XX_LPBFIFO_FLAG_READ (0)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
index 77572abca6c3..7085e4c60ba1 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
@@ -16,8 +16,14 @@
16 * output signals or measure input signals. 16 * output signals or measure input signals.
17 * 17 *
18 * This driver supports the GPIO and IRQ controller functions of the GPT 18 * This driver supports the GPIO and IRQ controller functions of the GPT
19 * device. Timer functions are not yet supported, nor is the watchdog 19 * device. Timer functions are not yet supported.
20 * timer. 20 *
21 * The timer gpt0 can be used as watchdog (wdt). If the wdt mode is used,
22 * this prevents the use of any gpt0 gpt function (i.e. they will fail with
23 * -EBUSY). Thus, the safety wdt function always has precedence over the gpt
24 * function. If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT,
25 * this means that gpt0 is locked in wdt mode until the next reboot - this
26 * may be a requirement in safety applications.
21 * 27 *
22 * To use the GPIO function, the following two properties must be added 28 * To use the GPIO function, the following two properties must be added
23 * to the device tree node for the gpt device (typically in the .dts file 29 * to the device tree node for the gpt device (typically in the .dts file
@@ -56,11 +62,14 @@
56#include <linux/of_platform.h> 62#include <linux/of_platform.h>
57#include <linux/of_gpio.h> 63#include <linux/of_gpio.h>
58#include <linux/kernel.h> 64#include <linux/kernel.h>
65#include <linux/watchdog.h>
66#include <linux/miscdevice.h>
67#include <linux/uaccess.h>
59#include <asm/div64.h> 68#include <asm/div64.h>
60#include <asm/mpc52xx.h> 69#include <asm/mpc52xx.h>
61 70
62MODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); 71MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
63MODULE_AUTHOR("Sascha Hauer, Grant Likely"); 72MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß");
64MODULE_LICENSE("GPL"); 73MODULE_LICENSE("GPL");
65 74
66/** 75/**
@@ -70,6 +79,9 @@ MODULE_LICENSE("GPL");
70 * @lock: spinlock to coordinate between different functions. 79 * @lock: spinlock to coordinate between different functions.
71 * @of_gc: of_gpio_chip instance structure; used when GPIO is enabled 80 * @of_gc: of_gpio_chip instance structure; used when GPIO is enabled
72 * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported 81 * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported
82 * @wdt_mode: only relevant for gpt0: bit 0 (MPC52xx_GPT_CAN_WDT) indicates
83 * if the gpt may be used as wdt, bit 1 (MPC52xx_GPT_IS_WDT) indicates
84 * if the timer is actively used as wdt which blocks gpt functions
73 */ 85 */
74struct mpc52xx_gpt_priv { 86struct mpc52xx_gpt_priv {
75 struct list_head list; /* List of all GPT devices */ 87 struct list_head list; /* List of all GPT devices */
@@ -78,6 +90,7 @@ struct mpc52xx_gpt_priv {
78 spinlock_t lock; 90 spinlock_t lock;
79 struct irq_host *irqhost; 91 struct irq_host *irqhost;
80 u32 ipb_freq; 92 u32 ipb_freq;
93 u8 wdt_mode;
81 94
82#if defined(CONFIG_GPIOLIB) 95#if defined(CONFIG_GPIOLIB)
83 struct of_gpio_chip of_gc; 96 struct of_gpio_chip of_gc;
@@ -101,14 +114,21 @@ DEFINE_MUTEX(mpc52xx_gpt_list_mutex);
101#define MPC52xx_GPT_MODE_CONTINUOUS (0x0400) 114#define MPC52xx_GPT_MODE_CONTINUOUS (0x0400)
102#define MPC52xx_GPT_MODE_OPEN_DRAIN (0x0200) 115#define MPC52xx_GPT_MODE_OPEN_DRAIN (0x0200)
103#define MPC52xx_GPT_MODE_IRQ_EN (0x0100) 116#define MPC52xx_GPT_MODE_IRQ_EN (0x0100)
117#define MPC52xx_GPT_MODE_WDT_EN (0x8000)
104 118
105#define MPC52xx_GPT_MODE_ICT_MASK (0x030000) 119#define MPC52xx_GPT_MODE_ICT_MASK (0x030000)
106#define MPC52xx_GPT_MODE_ICT_RISING (0x010000) 120#define MPC52xx_GPT_MODE_ICT_RISING (0x010000)
107#define MPC52xx_GPT_MODE_ICT_FALLING (0x020000) 121#define MPC52xx_GPT_MODE_ICT_FALLING (0x020000)
108#define MPC52xx_GPT_MODE_ICT_TOGGLE (0x030000) 122#define MPC52xx_GPT_MODE_ICT_TOGGLE (0x030000)
109 123
124#define MPC52xx_GPT_MODE_WDT_PING (0xa5)
125
110#define MPC52xx_GPT_STATUS_IRQMASK (0x000f) 126#define MPC52xx_GPT_STATUS_IRQMASK (0x000f)
111 127
128#define MPC52xx_GPT_CAN_WDT (1 << 0)
129#define MPC52xx_GPT_IS_WDT (1 << 1)
130
131
112/* --------------------------------------------------------------------- 132/* ---------------------------------------------------------------------
113 * Cascaded interrupt controller hooks 133 * Cascaded interrupt controller hooks
114 */ 134 */
@@ -375,16 +395,8 @@ struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq)
375} 395}
376EXPORT_SYMBOL(mpc52xx_gpt_from_irq); 396EXPORT_SYMBOL(mpc52xx_gpt_from_irq);
377 397
378/** 398static int mpc52xx_gpt_do_start(struct mpc52xx_gpt_priv *gpt, u64 period,
379 * mpc52xx_gpt_start_timer - Set and enable the GPT timer 399 int continuous, int as_wdt)
380 * @gpt: Pointer to gpt private data structure
381 * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock
382 * @continuous: set to 1 to make timer continuous free running
383 *
384 * An interrupt will be generated every time the timer fires
385 */
386int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
387 int continuous)
388{ 400{
389 u32 clear, set; 401 u32 clear, set;
390 u64 clocks; 402 u64 clocks;
@@ -393,7 +405,10 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
393 405
394 clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS; 406 clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS;
395 set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE; 407 set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE;
396 if (continuous) 408 if (as_wdt) {
409 clear |= MPC52xx_GPT_MODE_IRQ_EN;
410 set |= MPC52xx_GPT_MODE_WDT_EN;
411 } else if (continuous)
397 set |= MPC52xx_GPT_MODE_CONTINUOUS; 412 set |= MPC52xx_GPT_MODE_CONTINUOUS;
398 413
399 /* Determine the number of clocks in the requested period. 64 bit 414 /* Determine the number of clocks in the requested period. 64 bit
@@ -427,22 +442,279 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
427 return -EINVAL; 442 return -EINVAL;
428 } 443 }
429 444
430 /* Set and enable the timer */ 445 /* Set and enable the timer, reject an attempt to use a wdt as gpt */
431 spin_lock_irqsave(&gpt->lock, flags); 446 spin_lock_irqsave(&gpt->lock, flags);
447 if (as_wdt)
448 gpt->wdt_mode |= MPC52xx_GPT_IS_WDT;
449 else if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) {
450 spin_unlock_irqrestore(&gpt->lock, flags);
451 return -EBUSY;
452 }
432 out_be32(&gpt->regs->count, prescale << 16 | clocks); 453 out_be32(&gpt->regs->count, prescale << 16 | clocks);
433 clrsetbits_be32(&gpt->regs->mode, clear, set); 454 clrsetbits_be32(&gpt->regs->mode, clear, set);
434 spin_unlock_irqrestore(&gpt->lock, flags); 455 spin_unlock_irqrestore(&gpt->lock, flags);
435 456
436 return 0; 457 return 0;
437} 458}
459
460/**
461 * mpc52xx_gpt_start_timer - Set and enable the GPT timer
462 * @gpt: Pointer to gpt private data structure
463 * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock
464 * @continuous: set to 1 to make timer continuous free running
465 *
466 * An interrupt will be generated every time the timer fires
467 */
468int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
469 int continuous)
470{
471 return mpc52xx_gpt_do_start(gpt, period, continuous, 0);
472}
438EXPORT_SYMBOL(mpc52xx_gpt_start_timer); 473EXPORT_SYMBOL(mpc52xx_gpt_start_timer);
439 474
440void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt) 475/**
476 * mpc52xx_gpt_stop_timer - Stop a gpt
477 * @gpt: Pointer to gpt private data structure
478 *
479 * Returns an error if attempting to stop a wdt
480 */
481int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
441{ 482{
483 unsigned long flags;
484
485 /* reject the operation if the timer is used as watchdog (gpt 0 only) */
486 spin_lock_irqsave(&gpt->lock, flags);
487 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) {
488 spin_unlock_irqrestore(&gpt->lock, flags);
489 return -EBUSY;
490 }
491
442 clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE); 492 clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE);
493 spin_unlock_irqrestore(&gpt->lock, flags);
494 return 0;
443} 495}
444EXPORT_SYMBOL(mpc52xx_gpt_stop_timer); 496EXPORT_SYMBOL(mpc52xx_gpt_stop_timer);
445 497
498/**
499 * mpc52xx_gpt_timer_period - Read the timer period
500 * @gpt: Pointer to gpt private data structure
501 *
502 * Returns the timer period in ns
503 */
504u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt)
505{
506 u64 period;
507 u64 prescale;
508 unsigned long flags;
509
510 spin_lock_irqsave(&gpt->lock, flags);
511 period = in_be32(&gpt->regs->count);
512 spin_unlock_irqrestore(&gpt->lock, flags);
513
514 prescale = period >> 16;
515 period &= 0xffff;
516 if (prescale == 0)
517 prescale = 0x10000;
518 period = period * prescale * 1000000000ULL;
519 do_div(period, (u64)gpt->ipb_freq);
520 return period;
521}
522EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
523
524#if defined(CONFIG_MPC5200_WDT)
525/***********************************************************************
526 * Watchdog API for gpt0
527 */
528
529#define WDT_IDENTITY "mpc52xx watchdog on GPT0"
530
531/* wdt_is_active stores wether or not the /dev/watchdog device is opened */
532static unsigned long wdt_is_active;
533
534/* wdt-capable gpt */
535static struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt;
536
537/* low-level wdt functions */
538static inline void mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv *gpt_wdt)
539{
540 unsigned long flags;
541
542 spin_lock_irqsave(&gpt_wdt->lock, flags);
543 out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING);
544 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
545}
546
547/* wdt misc device api */
548static ssize_t mpc52xx_wdt_write(struct file *file, const char __user *data,
549 size_t len, loff_t *ppos)
550{
551 struct mpc52xx_gpt_priv *gpt_wdt = file->private_data;
552 mpc52xx_gpt_wdt_ping(gpt_wdt);
553 return 0;
554}
555
556static struct watchdog_info mpc5200_wdt_info = {
557 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
558 .identity = WDT_IDENTITY,
559};
560
561static long mpc52xx_wdt_ioctl(struct file *file, unsigned int cmd,
562 unsigned long arg)
563{
564 struct mpc52xx_gpt_priv *gpt_wdt = file->private_data;
565 int __user *data = (int __user *)arg;
566 int timeout;
567 u64 real_timeout;
568 int ret = 0;
569
570 switch (cmd) {
571 case WDIOC_GETSUPPORT:
572 ret = copy_to_user(data, &mpc5200_wdt_info,
573 sizeof(mpc5200_wdt_info));
574 if (ret)
575 ret = -EFAULT;
576 break;
577
578 case WDIOC_GETSTATUS:
579 case WDIOC_GETBOOTSTATUS:
580 ret = put_user(0, data);
581 break;
582
583 case WDIOC_KEEPALIVE:
584 mpc52xx_gpt_wdt_ping(gpt_wdt);
585 break;
586
587 case WDIOC_SETTIMEOUT:
588 ret = get_user(timeout, data);
589 if (ret)
590 break;
591 real_timeout = (u64) timeout * 1000000000ULL;
592 ret = mpc52xx_gpt_do_start(gpt_wdt, real_timeout, 0, 1);
593 if (ret)
594 break;
595 /* fall through and return the timeout */
596
597 case WDIOC_GETTIMEOUT:
598 /* we need to round here as to avoid e.g. the following
599 * situation:
600 * - timeout requested is 1 second;
601 * - real timeout @33MHz is 999997090ns
602 * - the int divide by 10^9 will return 0.
603 */
604 real_timeout =
605 mpc52xx_gpt_timer_period(gpt_wdt) + 500000000ULL;
606 do_div(real_timeout, 1000000000ULL);
607 timeout = (int) real_timeout;
608 ret = put_user(timeout, data);
609 break;
610
611 default:
612 ret = -ENOTTY;
613 }
614 return ret;
615}
616
617static int mpc52xx_wdt_open(struct inode *inode, struct file *file)
618{
619 int ret;
620
621 /* sanity check */
622 if (!mpc52xx_gpt_wdt)
623 return -ENODEV;
624
625 /* /dev/watchdog can only be opened once */
626 if (test_and_set_bit(0, &wdt_is_active))
627 return -EBUSY;
628
629 /* Set and activate the watchdog with 30 seconds timeout */
630 ret = mpc52xx_gpt_do_start(mpc52xx_gpt_wdt, 30ULL * 1000000000ULL,
631 0, 1);
632 if (ret) {
633 clear_bit(0, &wdt_is_active);
634 return ret;
635 }
636
637 file->private_data = mpc52xx_gpt_wdt;
638 return nonseekable_open(inode, file);
639}
640
641static int mpc52xx_wdt_release(struct inode *inode, struct file *file)
642{
643 /* note: releasing the wdt in NOWAYOUT-mode does not stop it */
644#if !defined(CONFIG_WATCHDOG_NOWAYOUT)
645 struct mpc52xx_gpt_priv *gpt_wdt = file->private_data;
646 unsigned long flags;
647
648 spin_lock_irqsave(&gpt_wdt->lock, flags);
649 clrbits32(&gpt_wdt->regs->mode,
650 MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN);
651 gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT;
652 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
653#endif
654 clear_bit(0, &wdt_is_active);
655 return 0;
656}
657
658
659static const struct file_operations mpc52xx_wdt_fops = {
660 .owner = THIS_MODULE,
661 .llseek = no_llseek,
662 .write = mpc52xx_wdt_write,
663 .unlocked_ioctl = mpc52xx_wdt_ioctl,
664 .open = mpc52xx_wdt_open,
665 .release = mpc52xx_wdt_release,
666};
667
668static struct miscdevice mpc52xx_wdt_miscdev = {
669 .minor = WATCHDOG_MINOR,
670 .name = "watchdog",
671 .fops = &mpc52xx_wdt_fops,
672};
673
674static int __devinit mpc52xx_gpt_wdt_init(void)
675{
676 int err;
677
678 /* try to register the watchdog misc device */
679 err = misc_register(&mpc52xx_wdt_miscdev);
680 if (err)
681 pr_err("%s: cannot register watchdog device\n", WDT_IDENTITY);
682 else
683 pr_info("%s: watchdog device registered\n", WDT_IDENTITY);
684 return err;
685}
686
687static int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt,
688 const u32 *period)
689{
690 u64 real_timeout;
691
692 /* remember the gpt for the wdt operation */
693 mpc52xx_gpt_wdt = gpt;
694
695 /* configure the wdt if the device tree contained a timeout */
696 if (!period || *period == 0)
697 return 0;
698
699 real_timeout = (u64) *period * 1000000000ULL;
700 if (mpc52xx_gpt_do_start(gpt, real_timeout, 0, 1))
701 dev_warn(gpt->dev, "starting as wdt failed\n");
702 else
703 dev_info(gpt->dev, "watchdog set to %us timeout\n", *period);
704 return 0;
705}
706
707#else
708
709static int __devinit mpc52xx_gpt_wdt_init(void)
710{
711 return 0;
712}
713
714#define mpc52xx_gpt_wdt_setup(x, y) (0)
715
716#endif /* CONFIG_MPC5200_WDT */
717
446/* --------------------------------------------------------------------- 718/* ---------------------------------------------------------------------
447 * of_platform bus binding code 719 * of_platform bus binding code
448 */ 720 */
@@ -473,6 +745,22 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev,
473 list_add(&gpt->list, &mpc52xx_gpt_list); 745 list_add(&gpt->list, &mpc52xx_gpt_list);
474 mutex_unlock(&mpc52xx_gpt_list_mutex); 746 mutex_unlock(&mpc52xx_gpt_list_mutex);
475 747
748 /* check if this device could be a watchdog */
749 if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) ||
750 of_get_property(ofdev->node, "has-wdt", NULL)) {
751 const u32 *on_boot_wdt;
752
753 gpt->wdt_mode = MPC52xx_GPT_CAN_WDT;
754 on_boot_wdt = of_get_property(ofdev->node, "fsl,wdt-on-boot",
755 NULL);
756 if (on_boot_wdt) {
757 dev_info(gpt->dev, "used as watchdog\n");
758 gpt->wdt_mode |= MPC52xx_GPT_IS_WDT;
759 } else
760 dev_info(gpt->dev, "can function as watchdog\n");
761 mpc52xx_gpt_wdt_setup(gpt, on_boot_wdt);
762 }
763
476 return 0; 764 return 0;
477} 765}
478 766
@@ -507,3 +795,4 @@ static int __init mpc52xx_gpt_init(void)
507 795
508/* Make sure GPIOs and IRQs get set up before anyone tries to use them */ 796/* Make sure GPIOs and IRQs get set up before anyone tries to use them */
509subsys_initcall(mpc52xx_gpt_init); 797subsys_initcall(mpc52xx_gpt_init);
798device_initcall(mpc52xx_gpt_wdt_init);