aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/intel-mid_wdt.c
diff options
context:
space:
mode:
authorDavid Cohen <david.a.cohen@linux.intel.com>2014-04-15 16:06:05 -0400
committerWim Van Sebroeck <wim@iguana.be>2014-06-10 15:48:16 -0400
commit87a1ef8058d9ab26bc289ea4f27bc3c11ce9acb8 (patch)
tree3133c076fe94a89a1870a22ba9ca65150da63a2d /drivers/watchdog/intel-mid_wdt.c
parent938626d96a3ffb9eb54552bb0d3a4f2b30ffdeb0 (diff)
watchdog: add Intel MID watchdog driver support
Add initial Intel MID watchdog driver support. This driver is an initial implementation of generic Intel MID watchdog driver. Currently it supports Intel Merrifield platform. Signed-off-by: Eric Ernst <eric.ernst@intel.com> Signed-off-by: David Cohen <david.a.cohen@linux.intel.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog/intel-mid_wdt.c')
-rw-r--r--drivers/watchdog/intel-mid_wdt.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
new file mode 100644
index 000000000000..ca66e8e74635
--- /dev/null
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -0,0 +1,184 @@
1/*
2 * intel-mid_wdt: generic Intel MID SCU watchdog driver
3 *
4 * Platforms supported so far:
5 * - Merrifield only
6 *
7 * Copyright (C) 2014 Intel Corporation. All rights reserved.
8 * Contact: David Cohen <david.a.cohen@linux.intel.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General
12 * Public License as published by the Free Software Foundation.
13 */
14
15#include <linux/interrupt.h>
16#include <linux/module.h>
17#include <linux/nmi.h>
18#include <linux/platform_device.h>
19#include <linux/watchdog.h>
20#include <linux/platform_data/intel-mid_wdt.h>
21
22#include <asm/intel_scu_ipc.h>
23#include <asm/intel-mid.h>
24
25#define IPC_WATCHDOG 0xf8
26
27#define MID_WDT_PRETIMEOUT 15
28#define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT)
29#define MID_WDT_TIMEOUT_MAX 170
30#define MID_WDT_DEFAULT_TIMEOUT 90
31
32/* SCU watchdog messages */
33enum {
34 SCU_WATCHDOG_START = 0,
35 SCU_WATCHDOG_STOP,
36 SCU_WATCHDOG_KEEPALIVE,
37};
38
39static inline int wdt_command(int sub, u32 *in, int inlen)
40{
41 return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
42}
43
44static int wdt_start(struct watchdog_device *wd)
45{
46 int ret, in_size;
47 int timeout = wd->timeout;
48 struct ipc_wd_start {
49 u32 pretimeout;
50 u32 timeout;
51 } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
52
53 /*
54 * SCU expects the input size for watchdog IPC to
55 * be based on 4 bytes
56 */
57 in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
58
59 ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
60 if (ret) {
61 struct device *dev = watchdog_get_drvdata(wd);
62 dev_crit(dev, "error starting watchdog: %d\n", ret);
63 }
64
65 return ret;
66}
67
68static int wdt_ping(struct watchdog_device *wd)
69{
70 int ret;
71
72 ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
73 if (ret) {
74 struct device *dev = watchdog_get_drvdata(wd);
75 dev_crit(dev, "Error executing keepalive: 0x%x\n", ret);
76 }
77
78 return ret;
79}
80
81static int wdt_stop(struct watchdog_device *wd)
82{
83 int ret;
84
85 ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
86 if (ret) {
87 struct device *dev = watchdog_get_drvdata(wd);
88 dev_crit(dev, "Error stopping watchdog: 0x%x\n", ret);
89 }
90
91 return ret;
92}
93
94static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
95{
96 panic("Kernel Watchdog");
97
98 /* This code should not be reached */
99 return IRQ_HANDLED;
100}
101
102static const struct watchdog_info mid_wdt_info = {
103 .identity = "Intel MID SCU watchdog",
104 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
105};
106
107static const struct watchdog_ops mid_wdt_ops = {
108 .owner = THIS_MODULE,
109 .start = wdt_start,
110 .stop = wdt_stop,
111 .ping = wdt_ping,
112};
113
114static int mid_wdt_probe(struct platform_device *pdev)
115{
116 struct watchdog_device *wdt_dev;
117 struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data;
118 int ret;
119
120 if (!pdata) {
121 dev_err(&pdev->dev, "missing platform data\n");
122 return -EINVAL;
123 }
124
125 if (pdata->probe) {
126 ret = pdata->probe(pdev);
127 if (ret)
128 return ret;
129 }
130
131 wdt_dev = devm_kzalloc(&pdev->dev, sizeof(*wdt_dev), GFP_KERNEL);
132 if (!wdt_dev)
133 return -ENOMEM;
134
135 wdt_dev->info = &mid_wdt_info;
136 wdt_dev->ops = &mid_wdt_ops;
137 wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
138 wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
139 wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
140
141 watchdog_set_drvdata(wdt_dev, &pdev->dev);
142 platform_set_drvdata(pdev, wdt_dev);
143
144 ret = devm_request_irq(&pdev->dev, pdata->irq, mid_wdt_irq,
145 IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
146 wdt_dev);
147 if (ret) {
148 dev_err(&pdev->dev, "error requesting warning irq %d\n",
149 pdata->irq);
150 return ret;
151 }
152
153 ret = watchdog_register_device(wdt_dev);
154 if (ret) {
155 dev_err(&pdev->dev, "error registering watchdog device\n");
156 return ret;
157 }
158
159 dev_info(&pdev->dev, "Intel MID watchdog device probed\n");
160
161 return 0;
162}
163
164static int mid_wdt_remove(struct platform_device *pdev)
165{
166 struct watchdog_device *wd = platform_get_drvdata(pdev);
167 watchdog_unregister_device(wd);
168 return 0;
169}
170
171static struct platform_driver mid_wdt_driver = {
172 .probe = mid_wdt_probe,
173 .remove = mid_wdt_remove,
174 .driver = {
175 .owner = THIS_MODULE,
176 .name = "intel_mid_wdt",
177 },
178};
179
180module_platform_driver(mid_wdt_driver);
181
182MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
183MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
184MODULE_LICENSE("GPL");