aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/qcom-wdt.c186
3 files changed, 200 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 786f8c8338fb..17b39b8a2520 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -461,6 +461,19 @@ config TEGRA_WATCHDOG
461 To compile this driver as a module, choose M here: the 461 To compile this driver as a module, choose M here: the
462 module will be called tegra_wdt. 462 module will be called tegra_wdt.
463 463
464config QCOM_WDT
465 tristate "QCOM watchdog"
466 depends on HAS_IOMEM
467 depends on ARCH_QCOM
468 select WATCHDOG_CORE
469 help
470 Say Y here to include Watchdog timer support for the watchdog found
471 on QCOM chipsets. Currently supported targets are the MSM8960,
472 APQ8064, and IPQ8064.
473
474 To compile this driver as a module, choose M here: the
475 module will be called qcom_wdt.
476
464# AVR32 Architecture 477# AVR32 Architecture
465 478
466config AT32AP700X_WDT 479config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 82af9956f96a..44f5d6842fc6 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
58obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o 58obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
59obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o 59obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
60obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o 60obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
61obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
61obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o 62obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
62obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o 63obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
63 64
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
new file mode 100644
index 000000000000..68db322341bc
--- /dev/null
+++ b/drivers/watchdog/qcom-wdt.c
@@ -0,0 +1,186 @@
1/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#include <linux/clk.h>
14#include <linux/io.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/watchdog.h>
20
21#define WDT_RST 0x0
22#define WDT_EN 0x8
23#define WDT_BITE_TIME 0x24
24
25struct qcom_wdt {
26 struct watchdog_device wdd;
27 struct clk *clk;
28 unsigned long rate;
29 void __iomem *base;
30};
31
32static inline
33struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
34{
35 return container_of(wdd, struct qcom_wdt, wdd);
36}
37
38static int qcom_wdt_start(struct watchdog_device *wdd)
39{
40 struct qcom_wdt *wdt = to_qcom_wdt(wdd);
41
42 writel(0, wdt->base + WDT_EN);
43 writel(1, wdt->base + WDT_RST);
44 writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME);
45 writel(1, wdt->base + WDT_EN);
46 return 0;
47}
48
49static int qcom_wdt_stop(struct watchdog_device *wdd)
50{
51 struct qcom_wdt *wdt = to_qcom_wdt(wdd);
52
53 writel(0, wdt->base + WDT_EN);
54 return 0;
55}
56
57static int qcom_wdt_ping(struct watchdog_device *wdd)
58{
59 struct qcom_wdt *wdt = to_qcom_wdt(wdd);
60
61 writel(1, wdt->base + WDT_RST);
62 return 0;
63}
64
65static int qcom_wdt_set_timeout(struct watchdog_device *wdd,
66 unsigned int timeout)
67{
68 wdd->timeout = timeout;
69 return qcom_wdt_start(wdd);
70}
71
72static const struct watchdog_ops qcom_wdt_ops = {
73 .start = qcom_wdt_start,
74 .stop = qcom_wdt_stop,
75 .ping = qcom_wdt_ping,
76 .set_timeout = qcom_wdt_set_timeout,
77 .owner = THIS_MODULE,
78};
79
80static const struct watchdog_info qcom_wdt_info = {
81 .options = WDIOF_KEEPALIVEPING
82 | WDIOF_MAGICCLOSE
83 | WDIOF_SETTIMEOUT,
84 .identity = KBUILD_MODNAME,
85};
86
87static int qcom_wdt_probe(struct platform_device *pdev)
88{
89 struct qcom_wdt *wdt;
90 struct resource *res;
91 int ret;
92
93 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
94 if (!wdt)
95 return -ENOMEM;
96
97 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
98 wdt->base = devm_ioremap_resource(&pdev->dev, res);
99 if (IS_ERR(wdt->base))
100 return PTR_ERR(wdt->base);
101
102 wdt->clk = devm_clk_get(&pdev->dev, NULL);
103 if (IS_ERR(wdt->clk)) {
104 dev_err(&pdev->dev, "failed to get input clock\n");
105 return PTR_ERR(wdt->clk);
106 }
107
108 ret = clk_prepare_enable(wdt->clk);
109 if (ret) {
110 dev_err(&pdev->dev, "failed to setup clock\n");
111 return ret;
112 }
113
114 /*
115 * We use the clock rate to calculate the max timeout, so ensure it's
116 * not zero to avoid a divide-by-zero exception.
117 *
118 * WATCHDOG_CORE assumes units of seconds, if the WDT is clocked such
119 * that it would bite before a second elapses it's usefulness is
120 * limited. Bail if this is the case.
121 */
122 wdt->rate = clk_get_rate(wdt->clk);
123 if (wdt->rate == 0 ||
124 wdt->rate > 0x10000000U) {
125 dev_err(&pdev->dev, "invalid clock rate\n");
126 ret = -EINVAL;
127 goto err_clk_unprepare;
128 }
129
130 wdt->wdd.dev = &pdev->dev;
131 wdt->wdd.info = &qcom_wdt_info;
132 wdt->wdd.ops = &qcom_wdt_ops;
133 wdt->wdd.min_timeout = 1;
134 wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
135
136 /*
137 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
138 * default, unless the max timeout is less than 30 seconds, then use
139 * the max instead.
140 */
141 wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U);
142 watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev);
143
144 ret = watchdog_register_device(&wdt->wdd);
145 if (ret) {
146 dev_err(&pdev->dev, "failed to register watchdog\n");
147 goto err_clk_unprepare;
148 }
149
150 platform_set_drvdata(pdev, wdt);
151 return 0;
152
153err_clk_unprepare:
154 clk_disable_unprepare(wdt->clk);
155 return ret;
156}
157
158static int qcom_wdt_remove(struct platform_device *pdev)
159{
160 struct qcom_wdt *wdt = platform_get_drvdata(pdev);
161
162 watchdog_unregister_device(&wdt->wdd);
163 clk_disable_unprepare(wdt->clk);
164 return 0;
165}
166
167static const struct of_device_id qcom_wdt_of_table[] = {
168 { .compatible = "qcom,kpss-wdt-msm8960", },
169 { .compatible = "qcom,kpss-wdt-apq8064", },
170 { .compatible = "qcom,kpss-wdt-ipq8064", },
171 { },
172};
173MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
174
175static struct platform_driver qcom_watchdog_driver = {
176 .probe = qcom_wdt_probe,
177 .remove = qcom_wdt_remove,
178 .driver = {
179 .name = KBUILD_MODNAME,
180 .of_match_table = qcom_wdt_of_table,
181 },
182};
183module_platform_driver(qcom_watchdog_driver);
184
185MODULE_DESCRIPTION("QCOM KPSS Watchdog Driver");
186MODULE_LICENSE("GPL v2");