aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/sec_watchdog.c
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /arch/arm/mach-exynos/sec_watchdog.c
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'arch/arm/mach-exynos/sec_watchdog.c')
-rw-r--r--arch/arm/mach-exynos/sec_watchdog.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/sec_watchdog.c b/arch/arm/mach-exynos/sec_watchdog.c
new file mode 100644
index 0000000..b903b80
--- /dev/null
+++ b/arch/arm/mach-exynos/sec_watchdog.c
@@ -0,0 +1,205 @@
+/* sec_watchdog.c copied from herring-watchdog.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <plat/regs-watchdog.h>
+#include <mach/map.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/cpu.h>
+
+#ifdef CONFIG_SEC_DEBUG
+#include <mach/sec_debug.h>
+#endif
+
+/* PCLK(=PERIR=ACLK_100)/256/128 (~3200:1s) */
+#define TPS 3200
+#if defined(CONFIG_MACH_P4) && defined(CONFIG_TARGET_LOCALE_USA)
+#define PET_BY_WORKQUEUE
+#else
+#define PET_BY_DIRECT_TIMER
+#endif
+
+/* reset timeout in seconds */
+static unsigned watchdog_reset = 20;
+module_param_named(sec_reset, watchdog_reset, uint, 0644);
+
+/* pet timeout in seconds
+ * 0 means off, 5 is proper */
+static unsigned watchdog_pet = CONFIG_SEC_WATCHDOG_PET_TIME;
+module_param_named(sec_pet, watchdog_pet, uint, 0644);
+
+#if defined(PET_BY_WORKQUEUE)
+static struct workqueue_struct *watchdog_wq;
+static void watchdog_workfunc(struct work_struct *work);
+static DECLARE_DELAYED_WORK(watchdog_work, watchdog_workfunc);
+#elif defined(PET_BY_DIRECT_TIMER)
+static struct timer_list pet_watchdog_timer;
+static void pet_watchdog_timer_fn(unsigned long data);
+#else
+static struct hrtimer watchdog_timer;
+static enum hrtimer_restart watchdog_timerfunc(struct hrtimer *timer);
+#endif
+
+static struct clk *wd_clk;
+static spinlock_t wdt_lock;
+
+#if defined(PET_BY_WORKQUEUE)
+static void watchdog_workfunc(struct work_struct *work)
+{
+ pr_info("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT));
+ writel(watchdog_reset * TPS, S3C2410_WTCNT);
+ queue_delayed_work_on(0, watchdog_wq, &watchdog_work,
+ watchdog_pet * HZ);
+}
+#elif defined(PET_BY_DIRECT_TIMER)
+static void pet_watchdog_timer_fn(unsigned long data)
+{
+ pr_info("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT));
+ writel(watchdog_reset * TPS, S3C2410_WTCNT);
+ pet_watchdog_timer.expires += watchdog_pet * HZ;
+ add_timer_on(&pet_watchdog_timer, 0);
+}
+#else
+static enum hrtimer_restart watchdog_timerfunc(struct hrtimer *timer)
+{
+ pr_info("%s kicking...%x\n", __func__, readl(S3C2410_WTCNT));
+ writel(watchdog_reset * TPS, S3C2410_WTCNT);
+ hrtimer_start(&watchdog_timer,
+ ktime_set(watchdog_pet, 0), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+#endif
+
+static void watchdog_start(void)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt_lock, flags);
+
+ /* set to PCLK / 256 / 128 */
+ val = S3C2410_WTCON_DIV128;
+ val |= S3C2410_WTCON_PRESCALE(255);
+ writel(val, S3C2410_WTCON);
+
+ /* program initial count */
+ writel(watchdog_reset * TPS, S3C2410_WTCNT);
+ writel(watchdog_reset * TPS, S3C2410_WTDAT);
+
+ /* start timer */
+ val |= S3C2410_WTCON_RSTEN | S3C2410_WTCON_ENABLE;
+ writel(val, S3C2410_WTCON);
+ spin_unlock_irqrestore(&wdt_lock, flags);
+
+ /* make sure we're ready to pet the dog */
+#if defined(PET_BY_WORKQUEUE)
+ queue_delayed_work_on(0, watchdog_wq, &watchdog_work,
+ watchdog_pet * HZ);
+#elif defined(PET_BY_DIRECT_TIMER)
+ pet_watchdog_timer.expires = jiffies + watchdog_pet * HZ;
+ add_timer_on(&pet_watchdog_timer, 0);
+#else
+ hrtimer_start(&watchdog_timer,
+ ktime_set(watchdog_pet, 0), HRTIMER_MODE_REL);
+#endif
+}
+
+static void watchdog_stop(void)
+{
+ writel(0, S3C2410_WTCON);
+#if defined(PET_BY_WORKQUEUE)
+ /* do nothing? */
+#elif defined(PET_BY_DIRECT_TIMER)
+ del_timer(&pet_watchdog_timer);
+#else
+ hrtimer_cancel(&watchdog_timer);
+#endif
+}
+
+static int watchdog_probe(struct platform_device *pdev)
+{
+ wd_clk = clk_get(NULL, "watchdog");
+ BUG_ON(!wd_clk);
+ clk_enable(wd_clk);
+
+ spin_lock_init(&wdt_lock);
+
+ /* watchdog can be disabled by providing either
+ * "exynos4210_watchdog.sec_pet=0" or
+ * "exynos4210_watchdog.sec_reset=0" to CMDLINE */
+ if (!watchdog_reset || !watchdog_pet) {
+ clk_disable(wd_clk);
+ return -ENODEV;
+ }
+
+#if defined(PET_BY_WORKQUEUE)
+ watchdog_wq = create_singlethread_workqueue("pet_watchdog");
+ watchdog_start();
+#elif defined(PET_BY_DIRECT_TIMER)
+ init_timer(&pet_watchdog_timer);
+ pet_watchdog_timer.function = pet_watchdog_timer_fn;
+ watchdog_start();
+#else
+ hrtimer_init(&watchdog_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ watchdog_timer.function = watchdog_timerfunc;
+ watchdog_start();
+#endif
+
+ return 0;
+}
+
+static int watchdog_suspend(struct device *dev)
+{
+ watchdog_stop();
+ return 0;
+}
+
+static int watchdog_resume(struct device *dev)
+{
+ watchdog_start();
+ return 0;
+}
+
+static const struct dev_pm_ops watchdog_pm_ops = {
+ .suspend_noirq = watchdog_suspend,
+ .resume_noirq = watchdog_resume,
+};
+
+static struct platform_driver watchdog_driver = {
+ .probe = watchdog_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "watchdog-reset",
+ .pm = &watchdog_pm_ops,
+ },
+};
+
+static int __init watchdog_init(void)
+{
+ return platform_driver_register(&watchdog_driver);
+}
+
+module_init(watchdog_init);