1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Watchdog driver for the wm8350 |
4 | * |
5 | * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com> |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/moduleparam.h> |
12 | #include <linux/types.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/watchdog.h> |
16 | #include <linux/uaccess.h> |
17 | #include <linux/mfd/wm8350/core.h> |
18 | |
19 | static bool nowayout = WATCHDOG_NOWAYOUT; |
20 | module_param(nowayout, bool, 0); |
21 | MODULE_PARM_DESC(nowayout, |
22 | "Watchdog cannot be stopped once started (default=" |
23 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")" ); |
24 | |
25 | static DEFINE_MUTEX(wdt_mutex); |
26 | |
27 | static struct { |
28 | unsigned int time; /* Seconds */ |
29 | u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */ |
30 | } wm8350_wdt_cfgs[] = { |
31 | { 1, 0x02 }, |
32 | { 2, 0x04 }, |
33 | { 4, 0x05 }, |
34 | }; |
35 | |
36 | static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev, |
37 | unsigned int timeout) |
38 | { |
39 | struct wm8350 *wm8350 = watchdog_get_drvdata(wdd: wdt_dev); |
40 | int ret, i; |
41 | u16 reg; |
42 | |
43 | for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) |
44 | if (wm8350_wdt_cfgs[i].time == timeout) |
45 | break; |
46 | if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) |
47 | return -EINVAL; |
48 | |
49 | mutex_lock(&wdt_mutex); |
50 | wm8350_reg_unlock(wm8350); |
51 | |
52 | reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); |
53 | reg &= ~WM8350_WDOG_TO_MASK; |
54 | reg |= wm8350_wdt_cfgs[i].val; |
55 | ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, val: reg); |
56 | |
57 | wm8350_reg_lock(wm8350); |
58 | mutex_unlock(lock: &wdt_mutex); |
59 | |
60 | wdt_dev->timeout = timeout; |
61 | return ret; |
62 | } |
63 | |
64 | static int wm8350_wdt_start(struct watchdog_device *wdt_dev) |
65 | { |
66 | struct wm8350 *wm8350 = watchdog_get_drvdata(wdd: wdt_dev); |
67 | int ret; |
68 | u16 reg; |
69 | |
70 | mutex_lock(&wdt_mutex); |
71 | wm8350_reg_unlock(wm8350); |
72 | |
73 | reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); |
74 | reg &= ~WM8350_WDOG_MODE_MASK; |
75 | reg |= 0x20; |
76 | ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, val: reg); |
77 | |
78 | wm8350_reg_lock(wm8350); |
79 | mutex_unlock(lock: &wdt_mutex); |
80 | |
81 | return ret; |
82 | } |
83 | |
84 | static int wm8350_wdt_stop(struct watchdog_device *wdt_dev) |
85 | { |
86 | struct wm8350 *wm8350 = watchdog_get_drvdata(wdd: wdt_dev); |
87 | int ret; |
88 | u16 reg; |
89 | |
90 | mutex_lock(&wdt_mutex); |
91 | wm8350_reg_unlock(wm8350); |
92 | |
93 | reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); |
94 | reg &= ~WM8350_WDOG_MODE_MASK; |
95 | ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, val: reg); |
96 | |
97 | wm8350_reg_lock(wm8350); |
98 | mutex_unlock(lock: &wdt_mutex); |
99 | |
100 | return ret; |
101 | } |
102 | |
103 | static int wm8350_wdt_ping(struct watchdog_device *wdt_dev) |
104 | { |
105 | struct wm8350 *wm8350 = watchdog_get_drvdata(wdd: wdt_dev); |
106 | int ret; |
107 | u16 reg; |
108 | |
109 | mutex_lock(&wdt_mutex); |
110 | |
111 | reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); |
112 | ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, val: reg); |
113 | |
114 | mutex_unlock(lock: &wdt_mutex); |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | static const struct watchdog_info wm8350_wdt_info = { |
120 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
121 | .identity = "WM8350 Watchdog" , |
122 | }; |
123 | |
124 | static const struct watchdog_ops wm8350_wdt_ops = { |
125 | .owner = THIS_MODULE, |
126 | .start = wm8350_wdt_start, |
127 | .stop = wm8350_wdt_stop, |
128 | .ping = wm8350_wdt_ping, |
129 | .set_timeout = wm8350_wdt_set_timeout, |
130 | }; |
131 | |
132 | static struct watchdog_device wm8350_wdt = { |
133 | .info = &wm8350_wdt_info, |
134 | .ops = &wm8350_wdt_ops, |
135 | .timeout = 4, |
136 | .min_timeout = 1, |
137 | .max_timeout = 4, |
138 | }; |
139 | |
140 | static int wm8350_wdt_probe(struct platform_device *pdev) |
141 | { |
142 | struct wm8350 *wm8350 = platform_get_drvdata(pdev); |
143 | |
144 | if (!wm8350) { |
145 | pr_err("No driver data supplied\n" ); |
146 | return -ENODEV; |
147 | } |
148 | |
149 | watchdog_set_nowayout(wdd: &wm8350_wdt, nowayout); |
150 | watchdog_set_drvdata(wdd: &wm8350_wdt, data: wm8350); |
151 | wm8350_wdt.parent = &pdev->dev; |
152 | |
153 | /* Default to 4s timeout */ |
154 | wm8350_wdt_set_timeout(wdt_dev: &wm8350_wdt, timeout: 4); |
155 | |
156 | return devm_watchdog_register_device(dev: &pdev->dev, &wm8350_wdt); |
157 | } |
158 | |
159 | static struct platform_driver wm8350_wdt_driver = { |
160 | .probe = wm8350_wdt_probe, |
161 | .driver = { |
162 | .name = "wm8350-wdt" , |
163 | }, |
164 | }; |
165 | |
166 | module_platform_driver(wm8350_wdt_driver); |
167 | |
168 | MODULE_AUTHOR("Mark Brown" ); |
169 | MODULE_DESCRIPTION("WM8350 Watchdog" ); |
170 | MODULE_LICENSE("GPL" ); |
171 | MODULE_ALIAS("platform:wm8350-wdt" ); |
172 | |