1 // SPDX-License-Identifier: GPL-2.0-only
3 * intel-mid_wdt: generic Intel MID SCU watchdog driver
5 * Platforms supported so far:
8 * Copyright (C) 2014 Intel Corporation. All rights reserved.
9 * Contact: David Cohen <david.a.cohen@linux.intel.com>
12 #include <linux/interrupt.h>
13 #include <linux/module.h>
14 #include <linux/nmi.h>
15 #include <linux/platform_device.h>
16 #include <linux/watchdog.h>
17 #include <linux/platform_data/intel-mid_wdt.h>
19 #include <asm/intel_scu_ipc.h>
20 #include <asm/intel-mid.h>
22 #define IPC_WATCHDOG 0xf8
24 #define MID_WDT_PRETIMEOUT 15
25 #define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT)
26 #define MID_WDT_TIMEOUT_MAX 170
27 #define MID_WDT_DEFAULT_TIMEOUT 90
29 /* SCU watchdog messages */
31 SCU_WATCHDOG_START
= 0,
33 SCU_WATCHDOG_KEEPALIVE
,
37 struct watchdog_device wd
;
39 struct intel_scu_ipc_dev
*scu
;
43 wdt_command(struct mid_wdt
*mid
, int sub
, const void *in
, size_t inlen
, size_t size
)
45 struct intel_scu_ipc_dev
*scu
= mid
->scu
;
47 return intel_scu_ipc_dev_command_with_size(scu
, IPC_WATCHDOG
, sub
, in
,
48 inlen
, size
, NULL
, 0);
51 static int wdt_start(struct watchdog_device
*wd
)
53 struct mid_wdt
*mid
= watchdog_get_drvdata(wd
);
55 int timeout
= wd
->timeout
;
59 } ipc_wd_start
= { timeout
- MID_WDT_PRETIMEOUT
, timeout
};
62 * SCU expects the input size for watchdog IPC to be 2 which is the
63 * size of the structure in dwords. SCU IPC normally takes bytes
64 * but this is a special case where we specify size to be different
67 in_size
= DIV_ROUND_UP(sizeof(ipc_wd_start
), 4);
69 ret
= wdt_command(mid
, SCU_WATCHDOG_START
, &ipc_wd_start
,
70 sizeof(ipc_wd_start
), in_size
);
72 dev_crit(mid
->dev
, "error starting watchdog: %d\n", ret
);
77 static int wdt_ping(struct watchdog_device
*wd
)
79 struct mid_wdt
*mid
= watchdog_get_drvdata(wd
);
82 ret
= wdt_command(mid
, SCU_WATCHDOG_KEEPALIVE
, NULL
, 0, 0);
84 dev_crit(mid
->dev
, "Error executing keepalive: %d\n", ret
);
89 static int wdt_stop(struct watchdog_device
*wd
)
91 struct mid_wdt
*mid
= watchdog_get_drvdata(wd
);
94 ret
= wdt_command(mid
, SCU_WATCHDOG_STOP
, NULL
, 0, 0);
96 dev_crit(mid
->dev
, "Error stopping watchdog: %d\n", ret
);
101 static irqreturn_t
mid_wdt_irq(int irq
, void *dev_id
)
103 panic("Kernel Watchdog");
105 /* This code should not be reached */
109 static const struct watchdog_info mid_wdt_info
= {
110 .identity
= "Intel MID SCU watchdog",
111 .options
= WDIOF_KEEPALIVEPING
| WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
,
114 static const struct watchdog_ops mid_wdt_ops
= {
115 .owner
= THIS_MODULE
,
121 static int mid_wdt_probe(struct platform_device
*pdev
)
123 struct device
*dev
= &pdev
->dev
;
124 struct watchdog_device
*wdt_dev
;
125 struct intel_mid_wdt_pdata
*pdata
= dev
->platform_data
;
130 dev_err(dev
, "missing platform data\n");
135 ret
= pdata
->probe(pdev
);
140 mid
= devm_kzalloc(dev
, sizeof(*mid
), GFP_KERNEL
);
147 wdt_dev
->info
= &mid_wdt_info
;
148 wdt_dev
->ops
= &mid_wdt_ops
;
149 wdt_dev
->min_timeout
= MID_WDT_TIMEOUT_MIN
;
150 wdt_dev
->max_timeout
= MID_WDT_TIMEOUT_MAX
;
151 wdt_dev
->timeout
= MID_WDT_DEFAULT_TIMEOUT
;
152 wdt_dev
->parent
= dev
;
154 watchdog_set_nowayout(wdt_dev
, WATCHDOG_NOWAYOUT
);
155 watchdog_set_drvdata(wdt_dev
, mid
);
157 ret
= devm_request_irq(dev
, pdata
->irq
, mid_wdt_irq
,
158 IRQF_SHARED
| IRQF_NO_SUSPEND
, "watchdog",
161 dev_err(dev
, "error requesting warning irq %d\n", pdata
->irq
);
165 mid
->scu
= devm_intel_scu_ipc_dev_get(dev
);
167 return -EPROBE_DEFER
;
170 * The firmware followed by U-Boot leaves the watchdog running
171 * with the default threshold which may vary. When we get here
172 * we should make a decision to prevent any side effects before
173 * user space daemon will take care of it. The best option,
174 * taking into consideration that there is no way to read values
175 * back from hardware, is to enforce watchdog being run with
176 * deterministic values.
178 ret
= wdt_start(wdt_dev
);
182 /* Make sure the watchdog is serviced */
183 set_bit(WDOG_HW_RUNNING
, &wdt_dev
->status
);
185 ret
= devm_watchdog_register_device(dev
, wdt_dev
);
189 dev_info(dev
, "Intel MID watchdog device probed\n");
194 static struct platform_driver mid_wdt_driver
= {
195 .probe
= mid_wdt_probe
,
197 .name
= "intel_mid_wdt",
201 module_platform_driver(mid_wdt_driver
);
203 MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
204 MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
205 MODULE_LICENSE("GPL");