diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-17 15:15:38 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-17 15:15:38 -0500 |
commit | 6606b342febfd470b4a33acb73e360eeaca1d9bb (patch) | |
tree | 2414cc2ca582aa34be6e6ed5c830658fbf21db41 | |
parent | a016af2e70bfca23f2f5de7d8708157b86ea374d (diff) | |
parent | ac36856fe4321454b6789c019c96c3ec854094ed (diff) |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
"This adds following items:
- watchdog restart handler support
- watchdog reboot notifier support
- watchdog sysfs attributes
- support for the following new devices: AMD Mullins platform, AMD
Carrizo platform, meson8b SoC, CSRatlas7, TS-4800, Alphascale
asm9260-wdt, Zodiac, Sigma Designs SMP86xx/SMP87xx
- Changes in refcounting for the watchdog core
- watchdog core improvements
- and small fixes"
* git://www.linux-watchdog.org/linux-watchdog: (60 commits)
watchdog: asm9260: remove __init and __exit annotations
watchdog: Drop pointer to watchdog device from struct watchdog_device
watchdog: ziirave: Use watchdog infrastructure to create sysfs attributes
watchdog: Add support for creating driver specific sysfs attributes
watchdog: kill unref/ref ops
watchdog: stmp3xxx: Remove unused variables
watchdog: add MT7621 watchdog support
hwmon: (sch56xx) Drop watchdog driver data reference count callbacks
watchdog: da9055_wdt: Drop reference counting
watchdog: da9052_wdt: Drop reference counting
watchdog: Separate and maintain variables based on variable lifetime
watchdog: diag288: Stop re-using watchdog core internal flags
watchdog: Create watchdog device in watchdog_dev.c
watchdog: qcom-wdt: Do not set 'dev' in struct watchdog_device
watchdog: mena21: Do not use device pointer from struct watchdog_device
watchdog: gpio: Do not use device pointer from struct watchdog_device
watchdog: tangox: Print info message using pointer to platform device
watchdog: bcm2835_wdt: Drop log message if watchdog is stopped
devicetree: watchdog: add binding for Sigma Designs SMP8642 watchdog
watchdog: add support for Sigma Designs SMP86xx/SMP87xx
...
48 files changed, 2671 insertions, 815 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-watchdog b/Documentation/ABI/testing/sysfs-class-watchdog new file mode 100644 index 000000000000..736046b33040 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-watchdog | |||
@@ -0,0 +1,51 @@ | |||
1 | What: /sys/class/watchdog/watchdogn/bootstatus | ||
2 | Date: August 2015 | ||
3 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
4 | Description: | ||
5 | It is a read only file. It contains status of the watchdog | ||
6 | device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of | ||
7 | ioctl interface. | ||
8 | |||
9 | What: /sys/class/watchdog/watchdogn/identity | ||
10 | Date: August 2015 | ||
11 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
12 | Description: | ||
13 | It is a read only file. It contains identity string of | ||
14 | watchdog device. | ||
15 | |||
16 | What: /sys/class/watchdog/watchdogn/nowayout | ||
17 | Date: August 2015 | ||
18 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
19 | Description: | ||
20 | It is a read only file. While reading, it gives '1' if that | ||
21 | device supports nowayout feature else, it gives '0'. | ||
22 | |||
23 | What: /sys/class/watchdog/watchdogn/state | ||
24 | Date: August 2015 | ||
25 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
26 | Description: | ||
27 | It is a read only file. It gives active/inactive status of | ||
28 | watchdog device. | ||
29 | |||
30 | What: /sys/class/watchdog/watchdogn/status | ||
31 | Date: August 2015 | ||
32 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
33 | Description: | ||
34 | It is a read only file. It contains watchdog device's | ||
35 | internal status bits. It is equivalent to WDIOC_GETSTATUS | ||
36 | of ioctl interface. | ||
37 | |||
38 | What: /sys/class/watchdog/watchdogn/timeleft | ||
39 | Date: August 2015 | ||
40 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
41 | Description: | ||
42 | It is a read only file. It contains value of time left for | ||
43 | reset generation. It is equivalent to WDIOC_GETTIMELEFT of | ||
44 | ioctl interface. | ||
45 | |||
46 | What: /sys/class/watchdog/watchdogn/timeout | ||
47 | Date: August 2015 | ||
48 | Contact: Wim Van Sebroeck <wim@iguana.be> | ||
49 | Description: | ||
50 | It is a read only file. It is read to know about current | ||
51 | value of timeout programmed. | ||
diff --git a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt new file mode 100644 index 000000000000..75b265a04047 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt | |||
@@ -0,0 +1,35 @@ | |||
1 | Alphascale asm9260 Watchdog timer | ||
2 | |||
3 | Required properties: | ||
4 | |||
5 | - compatible : should be "alphascale,asm9260-wdt". | ||
6 | - reg : Specifies base physical address and size of the registers. | ||
7 | - clocks : the clocks feeding the watchdog timer. See clock-bindings.txt | ||
8 | - clock-names : should be set to | ||
9 | "mod" - source for tick counter. | ||
10 | "ahb" - ahb gate. | ||
11 | - resets : phandle pointing to the system reset controller with | ||
12 | line index for the watchdog. | ||
13 | - reset-names : should be set to "wdt_rst". | ||
14 | |||
15 | Optional properties: | ||
16 | - timeout-sec : shall contain the default watchdog timeout in seconds, | ||
17 | if unset, the default timeout is 30 seconds. | ||
18 | - alphascale,mode : three modes are supported | ||
19 | "hw" - hw reset (default). | ||
20 | "sw" - sw reset. | ||
21 | "debug" - no action is taken. | ||
22 | |||
23 | Example: | ||
24 | |||
25 | watchdog0: watchdog@80048000 { | ||
26 | compatible = "alphascale,asm9260-wdt"; | ||
27 | reg = <0x80048000 0x10>; | ||
28 | clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>; | ||
29 | clock-names = "mod", "ahb"; | ||
30 | interrupts = <55>; | ||
31 | resets = <&rst WDT_RESET>; | ||
32 | reset-names = "wdt_rst"; | ||
33 | timeout-sec = <30>; | ||
34 | alphascale,mode = "hw"; | ||
35 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/mt7621-wdt.txt b/Documentation/devicetree/bindings/watchdog/mt7621-wdt.txt new file mode 100644 index 000000000000..c15ef0ef609f --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/mt7621-wdt.txt | |||
@@ -0,0 +1,12 @@ | |||
1 | Ralink Watchdog Timers | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: must be "mediatek,mt7621-wdt" | ||
5 | - reg: physical base address of the controller and length of the register range | ||
6 | |||
7 | Example: | ||
8 | |||
9 | watchdog@100 { | ||
10 | compatible = "mediatek,mt7621-wdt"; | ||
11 | reg = <0x100 0x10>; | ||
12 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/sigma,smp8642-wdt.txt b/Documentation/devicetree/bindings/watchdog/sigma,smp8642-wdt.txt new file mode 100644 index 000000000000..5b7ec2c707d8 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/sigma,smp8642-wdt.txt | |||
@@ -0,0 +1,18 @@ | |||
1 | Sigma Designs SMP86xx/SMP87xx watchdog | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: Should be "sigma,smp8642-wdt" | ||
5 | - reg: Specifies the physical address region | ||
6 | - clocks: Should be a phandle to the clock | ||
7 | |||
8 | Optional properties: | ||
9 | - timeout-sec: watchdog timeout in seconds | ||
10 | |||
11 | Example: | ||
12 | |||
13 | watchdog@1fd00 { | ||
14 | compatible = "sigma,smp8642-wdt"; | ||
15 | reg = <0x1fd00 8>; | ||
16 | clocks = <&xtal_in_clk>; | ||
17 | timeout-sec = <30>; | ||
18 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt b/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt new file mode 100644 index 000000000000..8f6caad4258d --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt | |||
@@ -0,0 +1,25 @@ | |||
1 | Technologic Systems Watchdog | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: must be "technologic,ts4800-wdt" | ||
5 | - syscon: phandle / integer array that points to the syscon node which | ||
6 | describes the FPGA's syscon registers. | ||
7 | - phandle to FPGA's syscon | ||
8 | - offset to the watchdog register | ||
9 | |||
10 | Optional property: | ||
11 | - timeout-sec: contains the watchdog timeout in seconds. | ||
12 | |||
13 | Example: | ||
14 | |||
15 | syscon: syscon@b0010000 { | ||
16 | compatible = "syscon", "simple-mfd"; | ||
17 | reg = <0xb0010000 0x3d>; | ||
18 | reg-io-width = <2>; | ||
19 | |||
20 | wdt@e { | ||
21 | compatible = "technologic,ts4800-wdt"; | ||
22 | syscon = <&syscon 0xe>; | ||
23 | timeout-sec = <10>; | ||
24 | }; | ||
25 | } | ||
diff --git a/Documentation/devicetree/bindings/watchdog/ziirave-wdt.txt b/Documentation/devicetree/bindings/watchdog/ziirave-wdt.txt new file mode 100644 index 000000000000..3d878184ec3f --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/ziirave-wdt.txt | |||
@@ -0,0 +1,19 @@ | |||
1 | Zodiac RAVE Watchdog Timer | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: must be "zii,rave-wdt" | ||
5 | - reg: i2c slave address of device, usually 0x38 | ||
6 | |||
7 | Optional Properties: | ||
8 | - timeout-sec: Watchdog timeout value in seconds. | ||
9 | - reset-duration-ms: Duration of the pulse generated when the watchdog times | ||
10 | out. Value in milliseconds. | ||
11 | |||
12 | Example: | ||
13 | |||
14 | watchdog@38 { | ||
15 | compatible = "zii,rave-wdt"; | ||
16 | reg = <0x38>; | ||
17 | timeout-sec = <30>; | ||
18 | reset-duration-ms = <30>; | ||
19 | }; | ||
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index d8b0d3367706..55120a055a14 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
@@ -44,17 +44,18 @@ The watchdog device structure looks like this: | |||
44 | 44 | ||
45 | struct watchdog_device { | 45 | struct watchdog_device { |
46 | int id; | 46 | int id; |
47 | struct cdev cdev; | ||
48 | struct device *dev; | ||
49 | struct device *parent; | 47 | struct device *parent; |
48 | const struct attribute_group **groups; | ||
50 | const struct watchdog_info *info; | 49 | const struct watchdog_info *info; |
51 | const struct watchdog_ops *ops; | 50 | const struct watchdog_ops *ops; |
52 | unsigned int bootstatus; | 51 | unsigned int bootstatus; |
53 | unsigned int timeout; | 52 | unsigned int timeout; |
54 | unsigned int min_timeout; | 53 | unsigned int min_timeout; |
55 | unsigned int max_timeout; | 54 | unsigned int max_timeout; |
55 | struct notifier_block reboot_nb; | ||
56 | struct notifier_block restart_nb; | ||
56 | void *driver_data; | 57 | void *driver_data; |
57 | struct mutex lock; | 58 | struct watchdog_core_data *wd_data; |
58 | unsigned long status; | 59 | unsigned long status; |
59 | struct list_head deferred; | 60 | struct list_head deferred; |
60 | }; | 61 | }; |
@@ -64,27 +65,32 @@ It contains following fields: | |||
64 | /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old | 65 | /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old |
65 | /dev/watchdog miscdev. The id is set automatically when calling | 66 | /dev/watchdog miscdev. The id is set automatically when calling |
66 | watchdog_register_device. | 67 | watchdog_register_device. |
67 | * cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This | ||
68 | field is also populated by watchdog_register_device. | ||
69 | * dev: device under the watchdog class (created by watchdog_register_device). | ||
70 | * parent: set this to the parent device (or NULL) before calling | 68 | * parent: set this to the parent device (or NULL) before calling |
71 | watchdog_register_device. | 69 | watchdog_register_device. |
70 | * groups: List of sysfs attribute groups to create when creating the watchdog | ||
71 | device. | ||
72 | * info: a pointer to a watchdog_info structure. This structure gives some | 72 | * info: a pointer to a watchdog_info structure. This structure gives some |
73 | additional information about the watchdog timer itself. (Like it's unique name) | 73 | additional information about the watchdog timer itself. (Like it's unique name) |
74 | * ops: a pointer to the list of watchdog operations that the watchdog supports. | 74 | * ops: a pointer to the list of watchdog operations that the watchdog supports. |
75 | * timeout: the watchdog timer's timeout value (in seconds). | 75 | * timeout: the watchdog timer's timeout value (in seconds). |
76 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). | 76 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). |
77 | * max_timeout: the watchdog timer's maximum timeout value (in seconds). | 77 | * max_timeout: the watchdog timer's maximum timeout value (in seconds). |
78 | * reboot_nb: notifier block that is registered for reboot notifications, for | ||
79 | internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core | ||
80 | will stop the watchdog on such notifications. | ||
81 | * restart_nb: notifier block that is registered for machine restart, for | ||
82 | internal use only. If a watchdog is capable of restarting the machine, it | ||
83 | should define ops->restart. Priority can be changed through | ||
84 | watchdog_set_restart_priority. | ||
78 | * bootstatus: status of the device after booting (reported with watchdog | 85 | * bootstatus: status of the device after booting (reported with watchdog |
79 | WDIOF_* status bits). | 86 | WDIOF_* status bits). |
80 | * driver_data: a pointer to the drivers private data of a watchdog device. | 87 | * driver_data: a pointer to the drivers private data of a watchdog device. |
81 | This data should only be accessed via the watchdog_set_drvdata and | 88 | This data should only be accessed via the watchdog_set_drvdata and |
82 | watchdog_get_drvdata routines. | 89 | watchdog_get_drvdata routines. |
83 | * lock: Mutex for WatchDog Timer Driver Core internal use only. | 90 | * wd_data: a pointer to watchdog core internal data. |
84 | * status: this field contains a number of status bits that give extra | 91 | * status: this field contains a number of status bits that give extra |
85 | information about the status of the device (Like: is the watchdog timer | 92 | information about the status of the device (Like: is the watchdog timer |
86 | running/active, is the nowayout bit set, is the device opened via | 93 | running/active, or is the nowayout bit set). |
87 | the /dev/watchdog interface or not, ...). | ||
88 | * deferred: entry in wtd_deferred_reg_list which is used to | 94 | * deferred: entry in wtd_deferred_reg_list which is used to |
89 | register early initialized watchdogs. | 95 | register early initialized watchdogs. |
90 | 96 | ||
@@ -100,8 +106,9 @@ struct watchdog_ops { | |||
100 | unsigned int (*status)(struct watchdog_device *); | 106 | unsigned int (*status)(struct watchdog_device *); |
101 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 107 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
102 | unsigned int (*get_timeleft)(struct watchdog_device *); | 108 | unsigned int (*get_timeleft)(struct watchdog_device *); |
103 | void (*ref)(struct watchdog_device *); | 109 | int (*restart)(struct watchdog_device *); |
104 | void (*unref)(struct watchdog_device *); | 110 | void (*ref)(struct watchdog_device *) __deprecated; |
111 | void (*unref)(struct watchdog_device *) __deprecated; | ||
105 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 112 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
106 | }; | 113 | }; |
107 | 114 | ||
@@ -110,20 +117,6 @@ driver's operations. This module owner will be used to lock the module when | |||
110 | the watchdog is active. (This to avoid a system crash when you unload the | 117 | the watchdog is active. (This to avoid a system crash when you unload the |
111 | module and /dev/watchdog is still open). | 118 | module and /dev/watchdog is still open). |
112 | 119 | ||
113 | If the watchdog_device struct is dynamically allocated, just locking the module | ||
114 | is not enough and a driver also needs to define the ref and unref operations to | ||
115 | ensure the structure holding the watchdog_device does not go away. | ||
116 | |||
117 | The simplest (and usually sufficient) implementation of this is to: | ||
118 | 1) Add a kref struct to the same structure which is holding the watchdog_device | ||
119 | 2) Define a release callback for the kref which frees the struct holding both | ||
120 | 3) Call kref_init on this kref *before* calling watchdog_register_device() | ||
121 | 4) Define a ref operation calling kref_get on this kref | ||
122 | 5) Define a unref operation calling kref_put on this kref | ||
123 | 6) When it is time to cleanup: | ||
124 | * Do not kfree() the struct holding both, the last kref_put will do this! | ||
125 | * *After* calling watchdog_unregister_device() call kref_put on the kref | ||
126 | |||
127 | Some operations are mandatory and some are optional. The mandatory operations | 120 | Some operations are mandatory and some are optional. The mandatory operations |
128 | are: | 121 | are: |
129 | * start: this is a pointer to the routine that starts the watchdog timer | 122 | * start: this is a pointer to the routine that starts the watchdog timer |
@@ -164,34 +157,23 @@ they are supported. These optional routines/operations are: | |||
164 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the | 157 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the |
165 | watchdog's info structure). | 158 | watchdog's info structure). |
166 | * get_timeleft: this routines returns the time that's left before a reset. | 159 | * get_timeleft: this routines returns the time that's left before a reset. |
167 | * ref: the operation that calls kref_get on the kref of a dynamically | 160 | * restart: this routine restarts the machine. It returns 0 on success or a |
168 | allocated watchdog_device struct. | 161 | negative errno code for failure. |
169 | * unref: the operation that calls kref_put on the kref of a dynamically | ||
170 | allocated watchdog_device struct. | ||
171 | * ioctl: if this routine is present then it will be called first before we do | 162 | * ioctl: if this routine is present then it will be called first before we do |
172 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD | 163 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD |
173 | if a command is not supported. The parameters that are passed to the ioctl | 164 | if a command is not supported. The parameters that are passed to the ioctl |
174 | call are: watchdog_device, cmd and arg. | 165 | call are: watchdog_device, cmd and arg. |
175 | 166 | ||
167 | The 'ref' and 'unref' operations are no longer used and deprecated. | ||
168 | |||
176 | The status bits should (preferably) be set with the set_bit and clear_bit alike | 169 | The status bits should (preferably) be set with the set_bit and clear_bit alike |
177 | bit-operations. The status bits that are defined are: | 170 | bit-operations. The status bits that are defined are: |
178 | * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device | 171 | * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device |
179 | is active or not. When the watchdog is active after booting, then you should | 172 | is active or not. When the watchdog is active after booting, then you should |
180 | set this status bit (Note: when you register the watchdog timer device with | 173 | set this status bit (Note: when you register the watchdog timer device with |
181 | this bit set, then opening /dev/watchdog will skip the start operation) | 174 | this bit set, then opening /dev/watchdog will skip the start operation) |
182 | * WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device | ||
183 | was opened via /dev/watchdog. | ||
184 | (This bit should only be used by the WatchDog Timer Driver Core). | ||
185 | * WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character | ||
186 | has been sent (so that we can support the magic close feature). | ||
187 | (This bit should only be used by the WatchDog Timer Driver Core). | ||
188 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. | 175 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. |
189 | If this bit is set then the watchdog timer will not be able to stop. | 176 | If this bit is set then the watchdog timer will not be able to stop. |
190 | * WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core | ||
191 | after calling watchdog_unregister_device, and then checked before calling | ||
192 | any watchdog_ops, so that you can be sure that no operations (other then | ||
193 | unref) will get called after unregister, even if userspace still holds a | ||
194 | reference to /dev/watchdog | ||
195 | 177 | ||
196 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog | 178 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog |
197 | timer device) you can either: | 179 | timer device) you can either: |
@@ -231,3 +213,18 @@ the device tree (if the module timeout parameter is invalid). Best practice is | |||
231 | to set the default timeout value as timeout value in the watchdog_device and | 213 | to set the default timeout value as timeout value in the watchdog_device and |
232 | then use this function to set the user "preferred" timeout value. | 214 | then use this function to set the user "preferred" timeout value. |
233 | This routine returns zero on success and a negative errno code for failure. | 215 | This routine returns zero on success and a negative errno code for failure. |
216 | |||
217 | To disable the watchdog on reboot, the user must call the following helper: | ||
218 | |||
219 | static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd); | ||
220 | |||
221 | To change the priority of the restart handler the following helper should be | ||
222 | used: | ||
223 | |||
224 | void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); | ||
225 | |||
226 | User should follow the following guidelines for setting the priority: | ||
227 | * 0: should be called in last resort, has limited restart capabilities | ||
228 | * 128: default restart handler, use if no other handler is expected to be | ||
229 | available, and/or if restart is sufficient to restart the entire system | ||
230 | * 255: highest priority, will preempt all other restart handlers | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 04d62b1e8b17..11073db4c0a0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -11676,6 +11676,7 @@ F: drivers/input/tablet/wacom_serial4.c | |||
11676 | 11676 | ||
11677 | WATCHDOG DEVICE DRIVERS | 11677 | WATCHDOG DEVICE DRIVERS |
11678 | M: Wim Van Sebroeck <wim@iguana.be> | 11678 | M: Wim Van Sebroeck <wim@iguana.be> |
11679 | R: Guenter Roeck <linux@roeck-us.net> | ||
11679 | L: linux-watchdog@vger.kernel.org | 11680 | L: linux-watchdog@vger.kernel.org |
11680 | W: http://www.linux-watchdog.org/ | 11681 | W: http://www.linux-watchdog.org/ |
11681 | T: git git://www.linux-watchdog.org/linux-watchdog.git | 11682 | T: git git://www.linux-watchdog.org/linux-watchdog.git |
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index 738681983284..68c350c704fb 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <linux/watchdog.h> | 30 | #include <linux/watchdog.h> |
31 | #include <linux/miscdevice.h> | 31 | #include <linux/miscdevice.h> |
32 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
33 | #include <linux/kref.h> | ||
34 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
35 | #include "sch56xx-common.h" | 34 | #include "sch56xx-common.h" |
36 | 35 | ||
@@ -67,7 +66,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | |||
67 | struct sch56xx_watchdog_data { | 66 | struct sch56xx_watchdog_data { |
68 | u16 addr; | 67 | u16 addr; |
69 | struct mutex *io_lock; | 68 | struct mutex *io_lock; |
70 | struct kref kref; | ||
71 | struct watchdog_info wdinfo; | 69 | struct watchdog_info wdinfo; |
72 | struct watchdog_device wddev; | 70 | struct watchdog_device wddev; |
73 | u8 watchdog_preset; | 71 | u8 watchdog_preset; |
@@ -258,15 +256,6 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12); | |||
258 | * Watchdog routines | 256 | * Watchdog routines |
259 | */ | 257 | */ |
260 | 258 | ||
261 | /* Release our data struct when we're unregistered *and* | ||
262 | all references to our watchdog device are released */ | ||
263 | static void watchdog_release_resources(struct kref *r) | ||
264 | { | ||
265 | struct sch56xx_watchdog_data *data = | ||
266 | container_of(r, struct sch56xx_watchdog_data, kref); | ||
267 | kfree(data); | ||
268 | } | ||
269 | |||
270 | static int watchdog_set_timeout(struct watchdog_device *wddev, | 259 | static int watchdog_set_timeout(struct watchdog_device *wddev, |
271 | unsigned int timeout) | 260 | unsigned int timeout) |
272 | { | 261 | { |
@@ -395,28 +384,12 @@ static int watchdog_stop(struct watchdog_device *wddev) | |||
395 | return 0; | 384 | return 0; |
396 | } | 385 | } |
397 | 386 | ||
398 | static void watchdog_ref(struct watchdog_device *wddev) | ||
399 | { | ||
400 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | ||
401 | |||
402 | kref_get(&data->kref); | ||
403 | } | ||
404 | |||
405 | static void watchdog_unref(struct watchdog_device *wddev) | ||
406 | { | ||
407 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | ||
408 | |||
409 | kref_put(&data->kref, watchdog_release_resources); | ||
410 | } | ||
411 | |||
412 | static const struct watchdog_ops watchdog_ops = { | 387 | static const struct watchdog_ops watchdog_ops = { |
413 | .owner = THIS_MODULE, | 388 | .owner = THIS_MODULE, |
414 | .start = watchdog_start, | 389 | .start = watchdog_start, |
415 | .stop = watchdog_stop, | 390 | .stop = watchdog_stop, |
416 | .ping = watchdog_trigger, | 391 | .ping = watchdog_trigger, |
417 | .set_timeout = watchdog_set_timeout, | 392 | .set_timeout = watchdog_set_timeout, |
418 | .ref = watchdog_ref, | ||
419 | .unref = watchdog_unref, | ||
420 | }; | 393 | }; |
421 | 394 | ||
422 | struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, | 395 | struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, |
@@ -448,7 +421,6 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, | |||
448 | 421 | ||
449 | data->addr = addr; | 422 | data->addr = addr; |
450 | data->io_lock = io_lock; | 423 | data->io_lock = io_lock; |
451 | kref_init(&data->kref); | ||
452 | 424 | ||
453 | strlcpy(data->wdinfo.identity, "sch56xx watchdog", | 425 | strlcpy(data->wdinfo.identity, "sch56xx watchdog", |
454 | sizeof(data->wdinfo.identity)); | 426 | sizeof(data->wdinfo.identity)); |
@@ -494,8 +466,7 @@ EXPORT_SYMBOL(sch56xx_watchdog_register); | |||
494 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) | 466 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) |
495 | { | 467 | { |
496 | watchdog_unregister_device(&data->wddev); | 468 | watchdog_unregister_device(&data->wddev); |
497 | kref_put(&data->kref, watchdog_release_resources); | 469 | kfree(data); |
498 | /* Don't touch data after this it may have been free-ed! */ | ||
499 | } | 470 | } |
500 | EXPORT_SYMBOL(sch56xx_watchdog_unregister); | 471 | EXPORT_SYMBOL(sch56xx_watchdog_unregister); |
501 | 472 | ||
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1c427beffadd..4f0e7be0da34 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -46,6 +46,13 @@ config WATCHDOG_NOWAYOUT | |||
46 | get killed. If you say Y here, the watchdog cannot be stopped once | 46 | get killed. If you say Y here, the watchdog cannot be stopped once |
47 | it has been started. | 47 | it has been started. |
48 | 48 | ||
49 | config WATCHDOG_SYSFS | ||
50 | bool "Read different watchdog information through sysfs" | ||
51 | default n | ||
52 | help | ||
53 | Say Y here if you want to enable watchdog device status read through | ||
54 | sysfs attributes. | ||
55 | |||
49 | # | 56 | # |
50 | # General Watchdog drivers | 57 | # General Watchdog drivers |
51 | # | 58 | # |
@@ -135,6 +142,16 @@ config MENF21BMC_WATCHDOG | |||
135 | This driver can also be built as a module. If so the module | 142 | This driver can also be built as a module. If so the module |
136 | will be called menf21bmc_wdt. | 143 | will be called menf21bmc_wdt. |
137 | 144 | ||
145 | config TANGOX_WATCHDOG | ||
146 | tristate "Sigma Designs SMP86xx/SMP87xx watchdog" | ||
147 | select WATCHDOG_CORE | ||
148 | depends on ARCH_TANGOX || COMPILE_TEST | ||
149 | help | ||
150 | Support for the watchdog in Sigma Designs SMP86xx (tango3) | ||
151 | and SMP87xx (tango4) family chips. | ||
152 | |||
153 | This driver can be built as a module. The module name is tangox_wdt. | ||
154 | |||
138 | config WM831X_WATCHDOG | 155 | config WM831X_WATCHDOG |
139 | tristate "WM831x watchdog" | 156 | tristate "WM831x watchdog" |
140 | depends on MFD_WM831X | 157 | depends on MFD_WM831X |
@@ -161,6 +178,17 @@ config XILINX_WATCHDOG | |||
161 | To compile this driver as a module, choose M here: the | 178 | To compile this driver as a module, choose M here: the |
162 | module will be called of_xilinx_wdt. | 179 | module will be called of_xilinx_wdt. |
163 | 180 | ||
181 | config ZIIRAVE_WATCHDOG | ||
182 | tristate "Zodiac RAVE Watchdog Timer" | ||
183 | depends on I2C | ||
184 | select WATCHDOG_CORE | ||
185 | help | ||
186 | Watchdog driver for the Zodiac Aerospace RAVE Switch Watchdog | ||
187 | Processor. | ||
188 | |||
189 | To compile this driver as a module, choose M here: the | ||
190 | module will be called ziirave_wdt. | ||
191 | |||
164 | # ALPHA Architecture | 192 | # ALPHA Architecture |
165 | 193 | ||
166 | # ARM Architecture | 194 | # ARM Architecture |
@@ -173,6 +201,16 @@ config ARM_SP805_WATCHDOG | |||
173 | ARM Primecell SP805 Watchdog timer. This will reboot your system when | 201 | ARM Primecell SP805 Watchdog timer. This will reboot your system when |
174 | the timeout is reached. | 202 | the timeout is reached. |
175 | 203 | ||
204 | config ASM9260_WATCHDOG | ||
205 | tristate "Alphascale ASM9260 watchdog" | ||
206 | depends on MACH_ASM9260 | ||
207 | depends on OF | ||
208 | select WATCHDOG_CORE | ||
209 | select RESET_CONTROLLER | ||
210 | help | ||
211 | Watchdog timer embedded into Alphascale asm9260 chips. This will reboot your | ||
212 | system when the timeout is reached. | ||
213 | |||
176 | config AT91RM9200_WATCHDOG | 214 | config AT91RM9200_WATCHDOG |
177 | tristate "AT91RM9200 watchdog" | 215 | tristate "AT91RM9200 watchdog" |
178 | depends on SOC_AT91RM9200 && MFD_SYSCON | 216 | depends on SOC_AT91RM9200 && MFD_SYSCON |
@@ -426,6 +464,16 @@ config NUC900_WATCHDOG | |||
426 | To compile this driver as a module, choose M here: the | 464 | To compile this driver as a module, choose M here: the |
427 | module will be called nuc900_wdt. | 465 | module will be called nuc900_wdt. |
428 | 466 | ||
467 | config TS4800_WATCHDOG | ||
468 | tristate "TS-4800 Watchdog" | ||
469 | depends on HAS_IOMEM && OF | ||
470 | select WATCHDOG_CORE | ||
471 | select MFD_SYSCON | ||
472 | help | ||
473 | Technologic Systems TS-4800 has watchdog timer implemented in | ||
474 | an external FPGA. Say Y here if you want to support for the | ||
475 | watchdog timer on TS-4800 board. | ||
476 | |||
429 | config TS72XX_WATCHDOG | 477 | config TS72XX_WATCHDOG |
430 | tristate "TS-72XX SBC Watchdog" | 478 | tristate "TS-72XX SBC Watchdog" |
431 | depends on MACH_TS72XX | 479 | depends on MACH_TS72XX |
@@ -578,6 +626,16 @@ config LPC18XX_WATCHDOG | |||
578 | To compile this driver as a module, choose M here: the | 626 | To compile this driver as a module, choose M here: the |
579 | module will be called lpc18xx_wdt. | 627 | module will be called lpc18xx_wdt. |
580 | 628 | ||
629 | config ATLAS7_WATCHDOG | ||
630 | tristate "CSRatlas7 watchdog" | ||
631 | depends on ARCH_ATLAS7 | ||
632 | help | ||
633 | Say Y here to include Watchdog timer support for the watchdog | ||
634 | existing on the CSRatlas7 series platforms. | ||
635 | |||
636 | To compile this driver as a module, choose M here: the | ||
637 | module will be called atlas7_wdt. | ||
638 | |||
581 | # AVR32 Architecture | 639 | # AVR32 Architecture |
582 | 640 | ||
583 | config AT32AP700X_WDT | 641 | config AT32AP700X_WDT |
@@ -1345,6 +1403,13 @@ config RALINK_WDT | |||
1345 | help | 1403 | help |
1346 | Hardware driver for the Ralink SoC Watchdog Timer. | 1404 | Hardware driver for the Ralink SoC Watchdog Timer. |
1347 | 1405 | ||
1406 | config MT7621_WDT | ||
1407 | tristate "Mediatek SoC watchdog" | ||
1408 | select WATCHDOG_CORE | ||
1409 | depends on SOC_MT7620 || SOC_MT7621 | ||
1410 | help | ||
1411 | Hardware driver for the Mediatek/Ralink MT7621/8 SoC Watchdog Timer. | ||
1412 | |||
1348 | # PARISC Architecture | 1413 | # PARISC Architecture |
1349 | 1414 | ||
1350 | # POWERPC Architecture | 1415 | # POWERPC Architecture |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 53d4827ddfe1..f566753256ab 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
30 | 30 | ||
31 | # ARM Architecture | 31 | # ARM Architecture |
32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o | 32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o |
33 | obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o | ||
33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 34 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 35 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
35 | obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o | 36 | obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o |
@@ -53,6 +54,7 @@ obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o | |||
53 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o | 54 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o |
54 | obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o | 55 | obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o |
55 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o | 56 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o |
57 | obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o | ||
56 | obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o | 58 | obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o |
57 | obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o | 59 | obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o |
58 | obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o | 60 | obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o |
@@ -69,6 +71,7 @@ obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o | |||
69 | obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o | 71 | obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o |
70 | obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o | 72 | obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o |
71 | obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o | 73 | obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o |
74 | obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o | ||
72 | 75 | ||
73 | # AVR32 Architecture | 76 | # AVR32 Architecture |
74 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 77 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
@@ -149,6 +152,7 @@ octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o | |||
149 | obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o | 152 | obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o |
150 | obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o | 153 | obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o |
151 | obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o | 154 | obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o |
155 | obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o | ||
152 | 156 | ||
153 | # PARISC Architecture | 157 | # PARISC Architecture |
154 | 158 | ||
@@ -187,8 +191,10 @@ obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | |||
187 | obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o | 191 | obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o |
188 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | 192 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o |
189 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 193 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
194 | obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o | ||
190 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 195 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
191 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 196 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
192 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o | 197 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o |
198 | obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o | ||
193 | obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o | 199 | obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o |
194 | obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o | 200 | obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o |
diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c new file mode 100644 index 000000000000..c9686b2fdafd --- /dev/null +++ b/drivers/watchdog/asm9260_wdt.c | |||
@@ -0,0 +1,403 @@ | |||
1 | /* | ||
2 | * Watchdog driver for Alphascale ASM9260. | ||
3 | * | ||
4 | * Copyright (c) 2014 Oleksij Rempel <linux@rempel-privat.de> | ||
5 | * | ||
6 | * Licensed under GPLv2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/clk.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/reboot.h> | ||
18 | #include <linux/reset.h> | ||
19 | #include <linux/watchdog.h> | ||
20 | |||
21 | #define CLOCK_FREQ 1000000 | ||
22 | |||
23 | /* Watchdog Mode register */ | ||
24 | #define HW_WDMOD 0x00 | ||
25 | /* Wake interrupt. Set by HW, can't be cleared. */ | ||
26 | #define BM_MOD_WDINT BIT(3) | ||
27 | /* This bit set if timeout reached. Cleared by SW. */ | ||
28 | #define BM_MOD_WDTOF BIT(2) | ||
29 | /* HW Reset on timeout */ | ||
30 | #define BM_MOD_WDRESET BIT(1) | ||
31 | /* WD enable */ | ||
32 | #define BM_MOD_WDEN BIT(0) | ||
33 | |||
34 | /* | ||
35 | * Watchdog Timer Constant register | ||
36 | * Minimal value is 0xff, the meaning of this value | ||
37 | * depends on used clock: T = WDCLK * (0xff + 1) * 4 | ||
38 | */ | ||
39 | #define HW_WDTC 0x04 | ||
40 | #define BM_WDTC_MAX(freq) (0x7fffffff / (freq)) | ||
41 | |||
42 | /* Watchdog Feed register */ | ||
43 | #define HW_WDFEED 0x08 | ||
44 | |||
45 | /* Watchdog Timer Value register */ | ||
46 | #define HW_WDTV 0x0c | ||
47 | |||
48 | #define ASM9260_WDT_DEFAULT_TIMEOUT 30 | ||
49 | |||
50 | enum asm9260_wdt_mode { | ||
51 | HW_RESET, | ||
52 | SW_RESET, | ||
53 | DEBUG, | ||
54 | }; | ||
55 | |||
56 | struct asm9260_wdt_priv { | ||
57 | struct device *dev; | ||
58 | struct watchdog_device wdd; | ||
59 | struct clk *clk; | ||
60 | struct clk *clk_ahb; | ||
61 | struct reset_control *rst; | ||
62 | struct notifier_block restart_handler; | ||
63 | |||
64 | void __iomem *iobase; | ||
65 | int irq; | ||
66 | unsigned long wdt_freq; | ||
67 | enum asm9260_wdt_mode mode; | ||
68 | }; | ||
69 | |||
70 | static int asm9260_wdt_feed(struct watchdog_device *wdd) | ||
71 | { | ||
72 | struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); | ||
73 | |||
74 | iowrite32(0xaa, priv->iobase + HW_WDFEED); | ||
75 | iowrite32(0x55, priv->iobase + HW_WDFEED); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd) | ||
81 | { | ||
82 | struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); | ||
83 | u32 counter; | ||
84 | |||
85 | counter = ioread32(priv->iobase + HW_WDTV); | ||
86 | |||
87 | return DIV_ROUND_CLOSEST(counter, priv->wdt_freq); | ||
88 | } | ||
89 | |||
90 | static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd) | ||
91 | { | ||
92 | struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); | ||
93 | u32 counter; | ||
94 | |||
95 | counter = wdd->timeout * priv->wdt_freq; | ||
96 | |||
97 | iowrite32(counter, priv->iobase + HW_WDTC); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int asm9260_wdt_enable(struct watchdog_device *wdd) | ||
103 | { | ||
104 | struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); | ||
105 | u32 mode = 0; | ||
106 | |||
107 | if (priv->mode == HW_RESET) | ||
108 | mode = BM_MOD_WDRESET; | ||
109 | |||
110 | iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD); | ||
111 | |||
112 | asm9260_wdt_updatetimeout(wdd); | ||
113 | |||
114 | asm9260_wdt_feed(wdd); | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int asm9260_wdt_disable(struct watchdog_device *wdd) | ||
120 | { | ||
121 | struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); | ||
122 | |||
123 | /* The only way to disable WD is to reset it. */ | ||
124 | reset_control_assert(priv->rst); | ||
125 | reset_control_deassert(priv->rst); | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) | ||
131 | { | ||
132 | wdd->timeout = to; | ||
133 | asm9260_wdt_updatetimeout(wdd); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv) | ||
139 | { | ||
140 | /* init WD if it was not started */ | ||
141 | |||
142 | iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD); | ||
143 | |||
144 | iowrite32(0xff, priv->iobase + HW_WDTC); | ||
145 | /* first pass correct sequence */ | ||
146 | asm9260_wdt_feed(&priv->wdd); | ||
147 | /* | ||
148 | * Then write wrong pattern to the feed to trigger reset | ||
149 | * ASAP. | ||
150 | */ | ||
151 | iowrite32(0xff, priv->iobase + HW_WDFEED); | ||
152 | |||
153 | mdelay(1000); | ||
154 | } | ||
155 | |||
156 | static irqreturn_t asm9260_wdt_irq(int irq, void *devid) | ||
157 | { | ||
158 | struct asm9260_wdt_priv *priv = devid; | ||
159 | u32 stat; | ||
160 | |||
161 | stat = ioread32(priv->iobase + HW_WDMOD); | ||
162 | if (!(stat & BM_MOD_WDINT)) | ||
163 | return IRQ_NONE; | ||
164 | |||
165 | if (priv->mode == DEBUG) { | ||
166 | dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n"); | ||
167 | } else { | ||
168 | dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n"); | ||
169 | asm9260_wdt_sys_reset(priv); | ||
170 | } | ||
171 | |||
172 | return IRQ_HANDLED; | ||
173 | } | ||
174 | |||
175 | static int asm9260_restart_handler(struct notifier_block *this, | ||
176 | unsigned long mode, void *cmd) | ||
177 | { | ||
178 | struct asm9260_wdt_priv *priv = | ||
179 | container_of(this, struct asm9260_wdt_priv, restart_handler); | ||
180 | |||
181 | asm9260_wdt_sys_reset(priv); | ||
182 | |||
183 | return NOTIFY_DONE; | ||
184 | } | ||
185 | |||
186 | static const struct watchdog_info asm9260_wdt_ident = { | ||
187 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | ||
188 | | WDIOF_MAGICCLOSE, | ||
189 | .identity = "Alphascale asm9260 Watchdog", | ||
190 | }; | ||
191 | |||
192 | static struct watchdog_ops asm9260_wdt_ops = { | ||
193 | .owner = THIS_MODULE, | ||
194 | .start = asm9260_wdt_enable, | ||
195 | .stop = asm9260_wdt_disable, | ||
196 | .get_timeleft = asm9260_wdt_gettimeleft, | ||
197 | .ping = asm9260_wdt_feed, | ||
198 | .set_timeout = asm9260_wdt_settimeout, | ||
199 | }; | ||
200 | |||
201 | static int asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv) | ||
202 | { | ||
203 | int err; | ||
204 | unsigned long clk; | ||
205 | |||
206 | priv->clk = devm_clk_get(priv->dev, "mod"); | ||
207 | if (IS_ERR(priv->clk)) { | ||
208 | dev_err(priv->dev, "Failed to get \"mod\" clk\n"); | ||
209 | return PTR_ERR(priv->clk); | ||
210 | } | ||
211 | |||
212 | /* configure AHB clock */ | ||
213 | priv->clk_ahb = devm_clk_get(priv->dev, "ahb"); | ||
214 | if (IS_ERR(priv->clk_ahb)) { | ||
215 | dev_err(priv->dev, "Failed to get \"ahb\" clk\n"); | ||
216 | return PTR_ERR(priv->clk_ahb); | ||
217 | } | ||
218 | |||
219 | err = clk_prepare_enable(priv->clk_ahb); | ||
220 | if (err) { | ||
221 | dev_err(priv->dev, "Failed to enable ahb_clk!\n"); | ||
222 | return err; | ||
223 | } | ||
224 | |||
225 | err = clk_set_rate(priv->clk, CLOCK_FREQ); | ||
226 | if (err) { | ||
227 | clk_disable_unprepare(priv->clk_ahb); | ||
228 | dev_err(priv->dev, "Failed to set rate!\n"); | ||
229 | return err; | ||
230 | } | ||
231 | |||
232 | err = clk_prepare_enable(priv->clk); | ||
233 | if (err) { | ||
234 | clk_disable_unprepare(priv->clk_ahb); | ||
235 | dev_err(priv->dev, "Failed to enable clk!\n"); | ||
236 | return err; | ||
237 | } | ||
238 | |||
239 | /* wdt has internal divider */ | ||
240 | clk = clk_get_rate(priv->clk); | ||
241 | if (!clk) { | ||
242 | clk_disable_unprepare(priv->clk); | ||
243 | clk_disable_unprepare(priv->clk_ahb); | ||
244 | dev_err(priv->dev, "Failed, clk is 0!\n"); | ||
245 | return -EINVAL; | ||
246 | } | ||
247 | |||
248 | priv->wdt_freq = clk / 2; | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static void asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv) | ||
254 | { | ||
255 | const char *tmp; | ||
256 | int ret; | ||
257 | |||
258 | /* default mode */ | ||
259 | priv->mode = HW_RESET; | ||
260 | |||
261 | ret = of_property_read_string(priv->dev->of_node, | ||
262 | "alphascale,mode", &tmp); | ||
263 | if (ret < 0) | ||
264 | return; | ||
265 | |||
266 | if (!strcmp(tmp, "hw")) | ||
267 | priv->mode = HW_RESET; | ||
268 | else if (!strcmp(tmp, "sw")) | ||
269 | priv->mode = SW_RESET; | ||
270 | else if (!strcmp(tmp, "debug")) | ||
271 | priv->mode = DEBUG; | ||
272 | else | ||
273 | dev_warn(priv->dev, "unknown reset-type: %s. Using default \"hw\" mode.", | ||
274 | tmp); | ||
275 | } | ||
276 | |||
277 | static int asm9260_wdt_probe(struct platform_device *pdev) | ||
278 | { | ||
279 | struct asm9260_wdt_priv *priv; | ||
280 | struct watchdog_device *wdd; | ||
281 | struct resource *res; | ||
282 | int ret; | ||
283 | const char * const mode_name[] = { "hw", "sw", "debug", }; | ||
284 | |||
285 | priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv), | ||
286 | GFP_KERNEL); | ||
287 | if (!priv) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | priv->dev = &pdev->dev; | ||
291 | |||
292 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
293 | priv->iobase = devm_ioremap_resource(&pdev->dev, res); | ||
294 | if (IS_ERR(priv->iobase)) | ||
295 | return PTR_ERR(priv->iobase); | ||
296 | |||
297 | ret = asm9260_wdt_get_dt_clks(priv); | ||
298 | if (ret) | ||
299 | return ret; | ||
300 | |||
301 | priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst"); | ||
302 | if (IS_ERR(priv->rst)) | ||
303 | return PTR_ERR(priv->rst); | ||
304 | |||
305 | wdd = &priv->wdd; | ||
306 | wdd->info = &asm9260_wdt_ident; | ||
307 | wdd->ops = &asm9260_wdt_ops; | ||
308 | wdd->min_timeout = 1; | ||
309 | wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq); | ||
310 | wdd->parent = &pdev->dev; | ||
311 | |||
312 | watchdog_set_drvdata(wdd, priv); | ||
313 | |||
314 | /* | ||
315 | * If 'timeout-sec' unspecified in devicetree, assume a 30 second | ||
316 | * default, unless the max timeout is less than 30 seconds, then use | ||
317 | * the max instead. | ||
318 | */ | ||
319 | wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT; | ||
320 | watchdog_init_timeout(wdd, 0, &pdev->dev); | ||
321 | |||
322 | asm9260_wdt_get_dt_mode(priv); | ||
323 | |||
324 | if (priv->mode != HW_RESET) | ||
325 | priv->irq = platform_get_irq(pdev, 0); | ||
326 | |||
327 | if (priv->irq > 0) { | ||
328 | /* | ||
329 | * Not all supported platforms specify an interrupt for the | ||
330 | * watchdog, so let's make it optional. | ||
331 | */ | ||
332 | ret = devm_request_irq(&pdev->dev, priv->irq, | ||
333 | asm9260_wdt_irq, 0, pdev->name, priv); | ||
334 | if (ret < 0) | ||
335 | dev_warn(&pdev->dev, "failed to request IRQ\n"); | ||
336 | } | ||
337 | |||
338 | ret = watchdog_register_device(wdd); | ||
339 | if (ret) | ||
340 | goto clk_off; | ||
341 | |||
342 | platform_set_drvdata(pdev, priv); | ||
343 | |||
344 | priv->restart_handler.notifier_call = asm9260_restart_handler; | ||
345 | priv->restart_handler.priority = 128; | ||
346 | ret = register_restart_handler(&priv->restart_handler); | ||
347 | if (ret) | ||
348 | dev_warn(&pdev->dev, "cannot register restart handler\n"); | ||
349 | |||
350 | dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n", | ||
351 | wdd->timeout, mode_name[priv->mode]); | ||
352 | return 0; | ||
353 | |||
354 | clk_off: | ||
355 | clk_disable_unprepare(priv->clk); | ||
356 | clk_disable_unprepare(priv->clk_ahb); | ||
357 | return ret; | ||
358 | } | ||
359 | |||
360 | static void asm9260_wdt_shutdown(struct platform_device *pdev) | ||
361 | { | ||
362 | struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev); | ||
363 | |||
364 | asm9260_wdt_disable(&priv->wdd); | ||
365 | } | ||
366 | |||
367 | static int asm9260_wdt_remove(struct platform_device *pdev) | ||
368 | { | ||
369 | struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev); | ||
370 | |||
371 | asm9260_wdt_disable(&priv->wdd); | ||
372 | |||
373 | unregister_restart_handler(&priv->restart_handler); | ||
374 | |||
375 | watchdog_unregister_device(&priv->wdd); | ||
376 | |||
377 | clk_disable_unprepare(priv->clk); | ||
378 | clk_disable_unprepare(priv->clk_ahb); | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static const struct of_device_id asm9260_wdt_of_match[] = { | ||
384 | { .compatible = "alphascale,asm9260-wdt"}, | ||
385 | {}, | ||
386 | }; | ||
387 | MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match); | ||
388 | |||
389 | static struct platform_driver asm9260_wdt_driver = { | ||
390 | .driver = { | ||
391 | .name = "asm9260-wdt", | ||
392 | .owner = THIS_MODULE, | ||
393 | .of_match_table = asm9260_wdt_of_match, | ||
394 | }, | ||
395 | .probe = asm9260_wdt_probe, | ||
396 | .remove = asm9260_wdt_remove, | ||
397 | .shutdown = asm9260_wdt_shutdown, | ||
398 | }; | ||
399 | module_platform_driver(asm9260_wdt_driver); | ||
400 | |||
401 | MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver"); | ||
402 | MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>"); | ||
403 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/atlas7_wdt.c b/drivers/watchdog/atlas7_wdt.c new file mode 100644 index 000000000000..df6d9242a319 --- /dev/null +++ b/drivers/watchdog/atlas7_wdt.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * Watchdog driver for CSR Atlas7 | ||
3 | * | ||
4 | * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company. | ||
5 | * | ||
6 | * Licensed under GPLv2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/clk.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/moduleparam.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/watchdog.h> | ||
16 | |||
17 | #define ATLAS7_TIMER_WDT_INDEX 5 | ||
18 | #define ATLAS7_WDT_DEFAULT_TIMEOUT 20 | ||
19 | |||
20 | #define ATLAS7_WDT_CNT_CTRL (0 + 4 * ATLAS7_TIMER_WDT_INDEX) | ||
21 | #define ATLAS7_WDT_CNT_MATCH (0x18 + 4 * ATLAS7_TIMER_WDT_INDEX) | ||
22 | #define ATLAS7_WDT_CNT (0x48 + 4 * ATLAS7_TIMER_WDT_INDEX) | ||
23 | #define ATLAS7_WDT_CNT_EN (BIT(0) | BIT(1)) | ||
24 | #define ATLAS7_WDT_EN 0x64 | ||
25 | |||
26 | static unsigned int timeout = ATLAS7_WDT_DEFAULT_TIMEOUT; | ||
27 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
28 | |||
29 | module_param(timeout, uint, 0); | ||
30 | module_param(nowayout, bool, 0); | ||
31 | |||
32 | MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); | ||
33 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
34 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
35 | |||
36 | struct atlas7_wdog { | ||
37 | struct device *dev; | ||
38 | void __iomem *base; | ||
39 | unsigned long tick_rate; | ||
40 | struct clk *clk; | ||
41 | }; | ||
42 | |||
43 | static unsigned int atlas7_wdt_gettimeleft(struct watchdog_device *wdd) | ||
44 | { | ||
45 | struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); | ||
46 | u32 counter, match, delta; | ||
47 | |||
48 | counter = readl(wdt->base + ATLAS7_WDT_CNT); | ||
49 | match = readl(wdt->base + ATLAS7_WDT_CNT_MATCH); | ||
50 | delta = match - counter; | ||
51 | |||
52 | return delta / wdt->tick_rate; | ||
53 | } | ||
54 | |||
55 | static int atlas7_wdt_ping(struct watchdog_device *wdd) | ||
56 | { | ||
57 | struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); | ||
58 | u32 counter, match, delta; | ||
59 | |||
60 | counter = readl(wdt->base + ATLAS7_WDT_CNT); | ||
61 | delta = wdd->timeout * wdt->tick_rate; | ||
62 | match = counter + delta; | ||
63 | |||
64 | writel(match, wdt->base + ATLAS7_WDT_CNT_MATCH); | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int atlas7_wdt_enable(struct watchdog_device *wdd) | ||
70 | { | ||
71 | struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); | ||
72 | |||
73 | atlas7_wdt_ping(wdd); | ||
74 | |||
75 | writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) | ATLAS7_WDT_CNT_EN, | ||
76 | wdt->base + ATLAS7_WDT_CNT_CTRL); | ||
77 | writel(1, wdt->base + ATLAS7_WDT_EN); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int atlas7_wdt_disable(struct watchdog_device *wdd) | ||
83 | { | ||
84 | struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); | ||
85 | |||
86 | writel(0, wdt->base + ATLAS7_WDT_EN); | ||
87 | writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) & ~ATLAS7_WDT_CNT_EN, | ||
88 | wdt->base + ATLAS7_WDT_CNT_CTRL); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int atlas7_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) | ||
94 | { | ||
95 | wdd->timeout = to; | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) | ||
101 | |||
102 | static const struct watchdog_info atlas7_wdt_ident = { | ||
103 | .options = OPTIONS, | ||
104 | .firmware_version = 0, | ||
105 | .identity = "atlas7 Watchdog", | ||
106 | }; | ||
107 | |||
108 | static struct watchdog_ops atlas7_wdt_ops = { | ||
109 | .owner = THIS_MODULE, | ||
110 | .start = atlas7_wdt_enable, | ||
111 | .stop = atlas7_wdt_disable, | ||
112 | .get_timeleft = atlas7_wdt_gettimeleft, | ||
113 | .ping = atlas7_wdt_ping, | ||
114 | .set_timeout = atlas7_wdt_settimeout, | ||
115 | }; | ||
116 | |||
117 | static struct watchdog_device atlas7_wdd = { | ||
118 | .info = &atlas7_wdt_ident, | ||
119 | .ops = &atlas7_wdt_ops, | ||
120 | .timeout = ATLAS7_WDT_DEFAULT_TIMEOUT, | ||
121 | }; | ||
122 | |||
123 | static const struct of_device_id atlas7_wdt_ids[] = { | ||
124 | { .compatible = "sirf,atlas7-tick"}, | ||
125 | {} | ||
126 | }; | ||
127 | |||
128 | static int atlas7_wdt_probe(struct platform_device *pdev) | ||
129 | { | ||
130 | struct device_node *np = pdev->dev.of_node; | ||
131 | struct atlas7_wdog *wdt; | ||
132 | struct resource *res; | ||
133 | struct clk *clk; | ||
134 | int ret; | ||
135 | |||
136 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
137 | if (!wdt) | ||
138 | return -ENOMEM; | ||
139 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
140 | wdt->base = devm_ioremap_resource(&pdev->dev, res); | ||
141 | if (IS_ERR(wdt->base)) | ||
142 | return PTR_ERR(wdt->base); | ||
143 | |||
144 | clk = of_clk_get(np, 0); | ||
145 | if (IS_ERR(clk)) | ||
146 | return PTR_ERR(clk); | ||
147 | ret = clk_prepare_enable(clk); | ||
148 | if (ret) { | ||
149 | dev_err(&pdev->dev, "clk enable failed\n"); | ||
150 | goto err; | ||
151 | } | ||
152 | |||
153 | /* disable watchdog hardware */ | ||
154 | writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL); | ||
155 | |||
156 | wdt->tick_rate = clk_get_rate(clk); | ||
157 | wdt->clk = clk; | ||
158 | atlas7_wdd.min_timeout = 1; | ||
159 | atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate; | ||
160 | |||
161 | watchdog_init_timeout(&atlas7_wdd, 0, &pdev->dev); | ||
162 | watchdog_set_nowayout(&atlas7_wdd, nowayout); | ||
163 | |||
164 | watchdog_set_drvdata(&atlas7_wdd, wdt); | ||
165 | platform_set_drvdata(pdev, &atlas7_wdd); | ||
166 | |||
167 | ret = watchdog_register_device(&atlas7_wdd); | ||
168 | if (ret) | ||
169 | goto err1; | ||
170 | |||
171 | return 0; | ||
172 | |||
173 | err1: | ||
174 | clk_disable_unprepare(clk); | ||
175 | err: | ||
176 | clk_put(clk); | ||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | static void atlas7_wdt_shutdown(struct platform_device *pdev) | ||
181 | { | ||
182 | struct watchdog_device *wdd = platform_get_drvdata(pdev); | ||
183 | struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); | ||
184 | |||
185 | atlas7_wdt_disable(wdd); | ||
186 | clk_disable_unprepare(wdt->clk); | ||
187 | } | ||
188 | |||
189 | static int atlas7_wdt_remove(struct platform_device *pdev) | ||
190 | { | ||
191 | struct watchdog_device *wdd = platform_get_drvdata(pdev); | ||
192 | struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); | ||
193 | |||
194 | atlas7_wdt_shutdown(pdev); | ||
195 | clk_put(wdt->clk); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static int __maybe_unused atlas7_wdt_suspend(struct device *dev) | ||
200 | { | ||
201 | /* | ||
202 | * NOTE:timer controller registers settings are saved | ||
203 | * and restored back by the timer-atlas7.c | ||
204 | */ | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int __maybe_unused atlas7_wdt_resume(struct device *dev) | ||
209 | { | ||
210 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
211 | |||
212 | /* | ||
213 | * NOTE: Since timer controller registers settings are saved | ||
214 | * and restored back by the timer-atlas7.c, so we need not | ||
215 | * update WD settings except refreshing timeout. | ||
216 | */ | ||
217 | atlas7_wdt_ping(wdd); | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static SIMPLE_DEV_PM_OPS(atlas7_wdt_pm_ops, | ||
223 | atlas7_wdt_suspend, atlas7_wdt_resume); | ||
224 | |||
225 | MODULE_DEVICE_TABLE(of, atlas7_wdt_ids); | ||
226 | |||
227 | static struct platform_driver atlas7_wdt_driver = { | ||
228 | .driver = { | ||
229 | .name = "atlas7-wdt", | ||
230 | .pm = &atlas7_wdt_pm_ops, | ||
231 | .of_match_table = atlas7_wdt_ids, | ||
232 | }, | ||
233 | .probe = atlas7_wdt_probe, | ||
234 | .remove = atlas7_wdt_remove, | ||
235 | .shutdown = atlas7_wdt_shutdown, | ||
236 | }; | ||
237 | module_platform_driver(atlas7_wdt_driver); | ||
238 | |||
239 | MODULE_DESCRIPTION("CSRatlas7 watchdog driver"); | ||
240 | MODULE_AUTHOR("Guo Zeng <Guo.Zeng@csr.com>"); | ||
241 | MODULE_LICENSE("GPL v2"); | ||
242 | MODULE_ALIAS("platform:atlas7-wdt"); | ||
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 8a5ce5b5a0b6..2e6164c4abc0 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c | |||
@@ -79,7 +79,6 @@ static int bcm2835_wdt_stop(struct watchdog_device *wdog) | |||
79 | struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); | 79 | struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); |
80 | 80 | ||
81 | writel_relaxed(PM_PASSWORD | PM_RSTC_RESET, wdt->base + PM_RSTC); | 81 | writel_relaxed(PM_PASSWORD | PM_RSTC_RESET, wdt->base + PM_RSTC); |
82 | dev_info(wdog->dev, "Watchdog timer stopped"); | ||
83 | return 0; | 82 | return 0; |
84 | } | 83 | } |
85 | 84 | ||
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index 4064a43f1360..df1c2a4b0165 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c | |||
@@ -20,7 +20,6 @@ | |||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/moduleparam.h> | 21 | #include <linux/moduleparam.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/reboot.h> | ||
24 | #include <linux/types.h> | 23 | #include <linux/types.h> |
25 | #include <linux/watchdog.h> | 24 | #include <linux/watchdog.h> |
26 | #include <linux/timer.h> | 25 | #include <linux/timer.h> |
@@ -88,12 +87,22 @@ static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd, | |||
88 | return 0; | 87 | return 0; |
89 | } | 88 | } |
90 | 89 | ||
90 | static int bcm47xx_wdt_restart(struct watchdog_device *wdd) | ||
91 | { | ||
92 | struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); | ||
93 | |||
94 | wdt->timer_set(wdt, 1); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
91 | static struct watchdog_ops bcm47xx_wdt_hard_ops = { | 99 | static struct watchdog_ops bcm47xx_wdt_hard_ops = { |
92 | .owner = THIS_MODULE, | 100 | .owner = THIS_MODULE, |
93 | .start = bcm47xx_wdt_hard_start, | 101 | .start = bcm47xx_wdt_hard_start, |
94 | .stop = bcm47xx_wdt_hard_stop, | 102 | .stop = bcm47xx_wdt_hard_stop, |
95 | .ping = bcm47xx_wdt_hard_keepalive, | 103 | .ping = bcm47xx_wdt_hard_keepalive, |
96 | .set_timeout = bcm47xx_wdt_hard_set_timeout, | 104 | .set_timeout = bcm47xx_wdt_hard_set_timeout, |
105 | .restart = bcm47xx_wdt_restart, | ||
97 | }; | 106 | }; |
98 | 107 | ||
99 | static void bcm47xx_wdt_soft_timer_tick(unsigned long data) | 108 | static void bcm47xx_wdt_soft_timer_tick(unsigned long data) |
@@ -158,34 +167,13 @@ static const struct watchdog_info bcm47xx_wdt_info = { | |||
158 | WDIOF_MAGICCLOSE, | 167 | WDIOF_MAGICCLOSE, |
159 | }; | 168 | }; |
160 | 169 | ||
161 | static int bcm47xx_wdt_notify_sys(struct notifier_block *this, | ||
162 | unsigned long code, void *unused) | ||
163 | { | ||
164 | struct bcm47xx_wdt *wdt; | ||
165 | |||
166 | wdt = container_of(this, struct bcm47xx_wdt, notifier); | ||
167 | if (code == SYS_DOWN || code == SYS_HALT) | ||
168 | wdt->wdd.ops->stop(&wdt->wdd); | ||
169 | return NOTIFY_DONE; | ||
170 | } | ||
171 | |||
172 | static int bcm47xx_wdt_restart(struct notifier_block *this, unsigned long mode, | ||
173 | void *cmd) | ||
174 | { | ||
175 | struct bcm47xx_wdt *wdt; | ||
176 | |||
177 | wdt = container_of(this, struct bcm47xx_wdt, restart_handler); | ||
178 | wdt->timer_set(wdt, 1); | ||
179 | |||
180 | return NOTIFY_DONE; | ||
181 | } | ||
182 | |||
183 | static struct watchdog_ops bcm47xx_wdt_soft_ops = { | 170 | static struct watchdog_ops bcm47xx_wdt_soft_ops = { |
184 | .owner = THIS_MODULE, | 171 | .owner = THIS_MODULE, |
185 | .start = bcm47xx_wdt_soft_start, | 172 | .start = bcm47xx_wdt_soft_start, |
186 | .stop = bcm47xx_wdt_soft_stop, | 173 | .stop = bcm47xx_wdt_soft_stop, |
187 | .ping = bcm47xx_wdt_soft_keepalive, | 174 | .ping = bcm47xx_wdt_soft_keepalive, |
188 | .set_timeout = bcm47xx_wdt_soft_set_timeout, | 175 | .set_timeout = bcm47xx_wdt_soft_set_timeout, |
176 | .restart = bcm47xx_wdt_restart, | ||
189 | }; | 177 | }; |
190 | 178 | ||
191 | static int bcm47xx_wdt_probe(struct platform_device *pdev) | 179 | static int bcm47xx_wdt_probe(struct platform_device *pdev) |
@@ -214,32 +202,18 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev) | |||
214 | if (ret) | 202 | if (ret) |
215 | goto err_timer; | 203 | goto err_timer; |
216 | watchdog_set_nowayout(&wdt->wdd, nowayout); | 204 | watchdog_set_nowayout(&wdt->wdd, nowayout); |
217 | 205 | watchdog_set_restart_priority(&wdt->wdd, 64); | |
218 | wdt->notifier.notifier_call = &bcm47xx_wdt_notify_sys; | 206 | watchdog_stop_on_reboot(&wdt->wdd); |
219 | |||
220 | ret = register_reboot_notifier(&wdt->notifier); | ||
221 | if (ret) | ||
222 | goto err_timer; | ||
223 | |||
224 | wdt->restart_handler.notifier_call = &bcm47xx_wdt_restart; | ||
225 | wdt->restart_handler.priority = 64; | ||
226 | ret = register_restart_handler(&wdt->restart_handler); | ||
227 | if (ret) | ||
228 | goto err_notifier; | ||
229 | 207 | ||
230 | ret = watchdog_register_device(&wdt->wdd); | 208 | ret = watchdog_register_device(&wdt->wdd); |
231 | if (ret) | 209 | if (ret) |
232 | goto err_handler; | 210 | goto err_timer; |
233 | 211 | ||
234 | dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n", | 212 | dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n", |
235 | timeout, nowayout ? ", nowayout" : "", | 213 | timeout, nowayout ? ", nowayout" : "", |
236 | soft ? ", Software Timer" : ""); | 214 | soft ? ", Software Timer" : ""); |
237 | return 0; | 215 | return 0; |
238 | 216 | ||
239 | err_handler: | ||
240 | unregister_restart_handler(&wdt->restart_handler); | ||
241 | err_notifier: | ||
242 | unregister_reboot_notifier(&wdt->notifier); | ||
243 | err_timer: | 217 | err_timer: |
244 | if (soft) | 218 | if (soft) |
245 | del_timer_sync(&wdt->soft_timer); | 219 | del_timer_sync(&wdt->soft_timer); |
@@ -255,7 +229,6 @@ static int bcm47xx_wdt_remove(struct platform_device *pdev) | |||
255 | return -ENXIO; | 229 | return -ENXIO; |
256 | 230 | ||
257 | watchdog_unregister_device(&wdt->wdd); | 231 | watchdog_unregister_device(&wdt->wdd); |
258 | unregister_reboot_notifier(&wdt->notifier); | ||
259 | 232 | ||
260 | return 0; | 233 | return 0; |
261 | } | 234 | } |
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index bcfd2a22208f..4dda9024e229 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c | |||
@@ -18,7 +18,6 @@ | |||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/of.h> | 19 | #include <linux/of.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/reboot.h> | ||
22 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
23 | 22 | ||
24 | #define CDNS_WDT_DEFAULT_TIMEOUT 10 | 23 | #define CDNS_WDT_DEFAULT_TIMEOUT 10 |
@@ -72,7 +71,6 @@ MODULE_PARM_DESC(nowayout, | |||
72 | * @ctrl_clksel: counter clock prescaler selection | 71 | * @ctrl_clksel: counter clock prescaler selection |
73 | * @io_lock: spinlock for IO register access | 72 | * @io_lock: spinlock for IO register access |
74 | * @cdns_wdt_device: watchdog device structure | 73 | * @cdns_wdt_device: watchdog device structure |
75 | * @cdns_wdt_notifier: notifier structure | ||
76 | * | 74 | * |
77 | * Structure containing parameters specific to cadence watchdog. | 75 | * Structure containing parameters specific to cadence watchdog. |
78 | */ | 76 | */ |
@@ -84,7 +82,6 @@ struct cdns_wdt { | |||
84 | u32 ctrl_clksel; | 82 | u32 ctrl_clksel; |
85 | spinlock_t io_lock; | 83 | spinlock_t io_lock; |
86 | struct watchdog_device cdns_wdt_device; | 84 | struct watchdog_device cdns_wdt_device; |
87 | struct notifier_block cdns_wdt_notifier; | ||
88 | }; | 85 | }; |
89 | 86 | ||
90 | /* Write access to Registers */ | 87 | /* Write access to Registers */ |
@@ -280,29 +277,6 @@ static struct watchdog_ops cdns_wdt_ops = { | |||
280 | .set_timeout = cdns_wdt_settimeout, | 277 | .set_timeout = cdns_wdt_settimeout, |
281 | }; | 278 | }; |
282 | 279 | ||
283 | /** | ||
284 | * cdns_wdt_notify_sys - Notifier for reboot or shutdown. | ||
285 | * | ||
286 | * @this: handle to notifier block | ||
287 | * @code: turn off indicator | ||
288 | * @unused: unused | ||
289 | * Return: NOTIFY_DONE | ||
290 | * | ||
291 | * This notifier is invoked whenever the system reboot or shutdown occur | ||
292 | * because we need to disable the WDT before system goes down as WDT might | ||
293 | * reset on the next boot. | ||
294 | */ | ||
295 | static int cdns_wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
296 | void *unused) | ||
297 | { | ||
298 | struct cdns_wdt *wdt = container_of(this, struct cdns_wdt, | ||
299 | cdns_wdt_notifier); | ||
300 | if (code == SYS_DOWN || code == SYS_HALT) | ||
301 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
302 | |||
303 | return NOTIFY_DONE; | ||
304 | } | ||
305 | |||
306 | /************************Platform Operations*****************************/ | 280 | /************************Platform Operations*****************************/ |
307 | /** | 281 | /** |
308 | * cdns_wdt_probe - Probe call for the device. | 282 | * cdns_wdt_probe - Probe call for the device. |
@@ -360,6 +334,7 @@ static int cdns_wdt_probe(struct platform_device *pdev) | |||
360 | } | 334 | } |
361 | 335 | ||
362 | watchdog_set_nowayout(cdns_wdt_device, nowayout); | 336 | watchdog_set_nowayout(cdns_wdt_device, nowayout); |
337 | watchdog_stop_on_reboot(cdns_wdt_device); | ||
363 | watchdog_set_drvdata(cdns_wdt_device, wdt); | 338 | watchdog_set_drvdata(cdns_wdt_device, wdt); |
364 | 339 | ||
365 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | 340 | wdt->clk = devm_clk_get(&pdev->dev, NULL); |
@@ -386,14 +361,6 @@ static int cdns_wdt_probe(struct platform_device *pdev) | |||
386 | 361 | ||
387 | spin_lock_init(&wdt->io_lock); | 362 | spin_lock_init(&wdt->io_lock); |
388 | 363 | ||
389 | wdt->cdns_wdt_notifier.notifier_call = &cdns_wdt_notify_sys; | ||
390 | ret = register_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
391 | if (ret != 0) { | ||
392 | dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", | ||
393 | ret); | ||
394 | goto err_clk_disable; | ||
395 | } | ||
396 | |||
397 | ret = watchdog_register_device(cdns_wdt_device); | 364 | ret = watchdog_register_device(cdns_wdt_device); |
398 | if (ret) { | 365 | if (ret) { |
399 | dev_err(&pdev->dev, "Failed to register wdt device\n"); | 366 | dev_err(&pdev->dev, "Failed to register wdt device\n"); |
@@ -427,7 +394,6 @@ static int cdns_wdt_remove(struct platform_device *pdev) | |||
427 | 394 | ||
428 | cdns_wdt_stop(&wdt->cdns_wdt_device); | 395 | cdns_wdt_stop(&wdt->cdns_wdt_device); |
429 | watchdog_unregister_device(&wdt->cdns_wdt_device); | 396 | watchdog_unregister_device(&wdt->cdns_wdt_device); |
430 | unregister_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
431 | clk_disable_unprepare(wdt->clk); | 397 | clk_disable_unprepare(wdt->clk); |
432 | 398 | ||
433 | return 0; | 399 | return 0; |
@@ -455,8 +421,7 @@ static void cdns_wdt_shutdown(struct platform_device *pdev) | |||
455 | */ | 421 | */ |
456 | static int __maybe_unused cdns_wdt_suspend(struct device *dev) | 422 | static int __maybe_unused cdns_wdt_suspend(struct device *dev) |
457 | { | 423 | { |
458 | struct platform_device *pdev = container_of(dev, | 424 | struct platform_device *pdev = to_platform_device(dev); |
459 | struct platform_device, dev); | ||
460 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | 425 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); |
461 | 426 | ||
462 | cdns_wdt_stop(&wdt->cdns_wdt_device); | 427 | cdns_wdt_stop(&wdt->cdns_wdt_device); |
@@ -474,8 +439,7 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev) | |||
474 | static int __maybe_unused cdns_wdt_resume(struct device *dev) | 439 | static int __maybe_unused cdns_wdt_resume(struct device *dev) |
475 | { | 440 | { |
476 | int ret; | 441 | int ret; |
477 | struct platform_device *pdev = container_of(dev, | 442 | struct platform_device *pdev = to_platform_device(dev); |
478 | struct platform_device, dev); | ||
479 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | 443 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); |
480 | 444 | ||
481 | ret = clk_prepare_enable(wdt->clk); | 445 | ret = clk_prepare_enable(wdt->clk); |
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index 67e67977bd29..2fc19a32a320 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c | |||
@@ -31,7 +31,6 @@ | |||
31 | struct da9052_wdt_data { | 31 | struct da9052_wdt_data { |
32 | struct watchdog_device wdt; | 32 | struct watchdog_device wdt; |
33 | struct da9052 *da9052; | 33 | struct da9052 *da9052; |
34 | struct kref kref; | ||
35 | unsigned long jpast; | 34 | unsigned long jpast; |
36 | }; | 35 | }; |
37 | 36 | ||
@@ -51,10 +50,6 @@ static const struct { | |||
51 | }; | 50 | }; |
52 | 51 | ||
53 | 52 | ||
54 | static void da9052_wdt_release_resources(struct kref *r) | ||
55 | { | ||
56 | } | ||
57 | |||
58 | static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, | 53 | static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, |
59 | unsigned int timeout) | 54 | unsigned int timeout) |
60 | { | 55 | { |
@@ -104,20 +99,6 @@ static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, | |||
104 | return 0; | 99 | return 0; |
105 | } | 100 | } |
106 | 101 | ||
107 | static void da9052_wdt_ref(struct watchdog_device *wdt_dev) | ||
108 | { | ||
109 | struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
110 | |||
111 | kref_get(&driver_data->kref); | ||
112 | } | ||
113 | |||
114 | static void da9052_wdt_unref(struct watchdog_device *wdt_dev) | ||
115 | { | ||
116 | struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
117 | |||
118 | kref_put(&driver_data->kref, da9052_wdt_release_resources); | ||
119 | } | ||
120 | |||
121 | static int da9052_wdt_start(struct watchdog_device *wdt_dev) | 102 | static int da9052_wdt_start(struct watchdog_device *wdt_dev) |
122 | { | 103 | { |
123 | return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); | 104 | return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); |
@@ -170,8 +151,6 @@ static const struct watchdog_ops da9052_wdt_ops = { | |||
170 | .stop = da9052_wdt_stop, | 151 | .stop = da9052_wdt_stop, |
171 | .ping = da9052_wdt_ping, | 152 | .ping = da9052_wdt_ping, |
172 | .set_timeout = da9052_wdt_set_timeout, | 153 | .set_timeout = da9052_wdt_set_timeout, |
173 | .ref = da9052_wdt_ref, | ||
174 | .unref = da9052_wdt_unref, | ||
175 | }; | 154 | }; |
176 | 155 | ||
177 | 156 | ||
@@ -198,8 +177,6 @@ static int da9052_wdt_probe(struct platform_device *pdev) | |||
198 | da9052_wdt->parent = &pdev->dev; | 177 | da9052_wdt->parent = &pdev->dev; |
199 | watchdog_set_drvdata(da9052_wdt, driver_data); | 178 | watchdog_set_drvdata(da9052_wdt, driver_data); |
200 | 179 | ||
201 | kref_init(&driver_data->kref); | ||
202 | |||
203 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, | 180 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, |
204 | DA9052_CONTROLD_TWDSCALE, 0); | 181 | DA9052_CONTROLD_TWDSCALE, 0); |
205 | if (ret < 0) { | 182 | if (ret < 0) { |
@@ -225,7 +202,6 @@ static int da9052_wdt_remove(struct platform_device *pdev) | |||
225 | struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); | 202 | struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); |
226 | 203 | ||
227 | watchdog_unregister_device(&driver_data->wdt); | 204 | watchdog_unregister_device(&driver_data->wdt); |
228 | kref_put(&driver_data->kref, da9052_wdt_release_resources); | ||
229 | 205 | ||
230 | return 0; | 206 | return 0; |
231 | } | 207 | } |
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c index 04d1430d93d2..8377c43f3f20 100644 --- a/drivers/watchdog/da9055_wdt.c +++ b/drivers/watchdog/da9055_wdt.c | |||
@@ -35,7 +35,6 @@ MODULE_PARM_DESC(nowayout, | |||
35 | struct da9055_wdt_data { | 35 | struct da9055_wdt_data { |
36 | struct watchdog_device wdt; | 36 | struct watchdog_device wdt; |
37 | struct da9055 *da9055; | 37 | struct da9055 *da9055; |
38 | struct kref kref; | ||
39 | }; | 38 | }; |
40 | 39 | ||
41 | static const struct { | 40 | static const struct { |
@@ -99,24 +98,6 @@ static int da9055_wdt_ping(struct watchdog_device *wdt_dev) | |||
99 | DA9055_WATCHDOG_MASK, 1); | 98 | DA9055_WATCHDOG_MASK, 1); |
100 | } | 99 | } |
101 | 100 | ||
102 | static void da9055_wdt_release_resources(struct kref *r) | ||
103 | { | ||
104 | } | ||
105 | |||
106 | static void da9055_wdt_ref(struct watchdog_device *wdt_dev) | ||
107 | { | ||
108 | struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
109 | |||
110 | kref_get(&driver_data->kref); | ||
111 | } | ||
112 | |||
113 | static void da9055_wdt_unref(struct watchdog_device *wdt_dev) | ||
114 | { | ||
115 | struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
116 | |||
117 | kref_put(&driver_data->kref, da9055_wdt_release_resources); | ||
118 | } | ||
119 | |||
120 | static int da9055_wdt_start(struct watchdog_device *wdt_dev) | 101 | static int da9055_wdt_start(struct watchdog_device *wdt_dev) |
121 | { | 102 | { |
122 | return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout); | 103 | return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout); |
@@ -138,8 +119,6 @@ static const struct watchdog_ops da9055_wdt_ops = { | |||
138 | .stop = da9055_wdt_stop, | 119 | .stop = da9055_wdt_stop, |
139 | .ping = da9055_wdt_ping, | 120 | .ping = da9055_wdt_ping, |
140 | .set_timeout = da9055_wdt_set_timeout, | 121 | .set_timeout = da9055_wdt_set_timeout, |
141 | .ref = da9055_wdt_ref, | ||
142 | .unref = da9055_wdt_unref, | ||
143 | }; | 122 | }; |
144 | 123 | ||
145 | static int da9055_wdt_probe(struct platform_device *pdev) | 124 | static int da9055_wdt_probe(struct platform_device *pdev) |
@@ -165,8 +144,6 @@ static int da9055_wdt_probe(struct platform_device *pdev) | |||
165 | watchdog_set_nowayout(da9055_wdt, nowayout); | 144 | watchdog_set_nowayout(da9055_wdt, nowayout); |
166 | watchdog_set_drvdata(da9055_wdt, driver_data); | 145 | watchdog_set_drvdata(da9055_wdt, driver_data); |
167 | 146 | ||
168 | kref_init(&driver_data->kref); | ||
169 | |||
170 | ret = da9055_wdt_stop(da9055_wdt); | 147 | ret = da9055_wdt_stop(da9055_wdt); |
171 | if (ret < 0) { | 148 | if (ret < 0) { |
172 | dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); | 149 | dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); |
@@ -189,7 +166,6 @@ static int da9055_wdt_remove(struct platform_device *pdev) | |||
189 | struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); | 166 | struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); |
190 | 167 | ||
191 | watchdog_unregister_device(&driver_data->wdt); | 168 | watchdog_unregister_device(&driver_data->wdt); |
192 | kref_put(&driver_data->kref, da9055_wdt_release_resources); | ||
193 | 169 | ||
194 | return 0; | 170 | return 0; |
195 | } | 171 | } |
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 6bf130bd863d..11e887572649 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c | |||
@@ -20,7 +20,6 @@ | |||
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/mfd/da9063/registers.h> | 21 | #include <linux/mfd/da9063/registers.h> |
22 | #include <linux/mfd/da9063/core.h> | 22 | #include <linux/mfd/da9063/core.h> |
23 | #include <linux/reboot.h> | ||
24 | #include <linux/regmap.h> | 23 | #include <linux/regmap.h> |
25 | 24 | ||
26 | /* | 25 | /* |
@@ -39,7 +38,6 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; | |||
39 | struct da9063_watchdog { | 38 | struct da9063_watchdog { |
40 | struct da9063 *da9063; | 39 | struct da9063 *da9063; |
41 | struct watchdog_device wdtdev; | 40 | struct watchdog_device wdtdev; |
42 | struct notifier_block restart_handler; | ||
43 | }; | 41 | }; |
44 | 42 | ||
45 | static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) | 43 | static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) |
@@ -121,12 +119,9 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd, | |||
121 | return ret; | 119 | return ret; |
122 | } | 120 | } |
123 | 121 | ||
124 | static int da9063_wdt_restart_handler(struct notifier_block *this, | 122 | static int da9063_wdt_restart(struct watchdog_device *wdd) |
125 | unsigned long mode, void *cmd) | ||
126 | { | 123 | { |
127 | struct da9063_watchdog *wdt = container_of(this, | 124 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); |
128 | struct da9063_watchdog, | ||
129 | restart_handler); | ||
130 | int ret; | 125 | int ret; |
131 | 126 | ||
132 | ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, | 127 | ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, |
@@ -135,7 +130,7 @@ static int da9063_wdt_restart_handler(struct notifier_block *this, | |||
135 | dev_alert(wdt->da9063->dev, "Failed to shutdown (err = %d)\n", | 130 | dev_alert(wdt->da9063->dev, "Failed to shutdown (err = %d)\n", |
136 | ret); | 131 | ret); |
137 | 132 | ||
138 | return NOTIFY_DONE; | 133 | return ret; |
139 | } | 134 | } |
140 | 135 | ||
141 | static const struct watchdog_info da9063_watchdog_info = { | 136 | static const struct watchdog_info da9063_watchdog_info = { |
@@ -149,6 +144,7 @@ static const struct watchdog_ops da9063_watchdog_ops = { | |||
149 | .stop = da9063_wdt_stop, | 144 | .stop = da9063_wdt_stop, |
150 | .ping = da9063_wdt_ping, | 145 | .ping = da9063_wdt_ping, |
151 | .set_timeout = da9063_wdt_set_timeout, | 146 | .set_timeout = da9063_wdt_set_timeout, |
147 | .restart = da9063_wdt_restart, | ||
152 | }; | 148 | }; |
153 | 149 | ||
154 | static int da9063_wdt_probe(struct platform_device *pdev) | 150 | static int da9063_wdt_probe(struct platform_device *pdev) |
@@ -179,6 +175,8 @@ static int da9063_wdt_probe(struct platform_device *pdev) | |||
179 | 175 | ||
180 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; | 176 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; |
181 | 177 | ||
178 | watchdog_set_restart_priority(&wdt->wdtdev, 128); | ||
179 | |||
182 | watchdog_set_drvdata(&wdt->wdtdev, wdt); | 180 | watchdog_set_drvdata(&wdt->wdtdev, wdt); |
183 | dev_set_drvdata(&pdev->dev, wdt); | 181 | dev_set_drvdata(&pdev->dev, wdt); |
184 | 182 | ||
@@ -186,13 +184,6 @@ static int da9063_wdt_probe(struct platform_device *pdev) | |||
186 | if (ret) | 184 | if (ret) |
187 | return ret; | 185 | return ret; |
188 | 186 | ||
189 | wdt->restart_handler.notifier_call = da9063_wdt_restart_handler; | ||
190 | wdt->restart_handler.priority = 128; | ||
191 | ret = register_restart_handler(&wdt->restart_handler); | ||
192 | if (ret) | ||
193 | dev_err(wdt->da9063->dev, | ||
194 | "Failed to register restart handler (err = %d)\n", ret); | ||
195 | |||
196 | return 0; | 187 | return 0; |
197 | } | 188 | } |
198 | 189 | ||
@@ -200,8 +191,6 @@ static int da9063_wdt_remove(struct platform_device *pdev) | |||
200 | { | 191 | { |
201 | struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); | 192 | struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); |
202 | 193 | ||
203 | unregister_restart_handler(&wdt->restart_handler); | ||
204 | |||
205 | watchdog_unregister_device(&wdt->wdtdev); | 194 | watchdog_unregister_device(&wdt->wdtdev); |
206 | 195 | ||
207 | return 0; | 196 | return 0; |
diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 3db9d0e0673d..861d3d3133f8 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c | |||
@@ -106,6 +106,10 @@ static int __diag288_lpar(unsigned int func, unsigned int timeout, | |||
106 | return __diag288(func, timeout, action, 0); | 106 | return __diag288(func, timeout, action, 0); |
107 | } | 107 | } |
108 | 108 | ||
109 | static unsigned long wdt_status; | ||
110 | |||
111 | #define DIAG_WDOG_BUSY 0 | ||
112 | |||
109 | static int wdt_start(struct watchdog_device *dev) | 113 | static int wdt_start(struct watchdog_device *dev) |
110 | { | 114 | { |
111 | char *ebc_cmd; | 115 | char *ebc_cmd; |
@@ -113,12 +117,17 @@ static int wdt_start(struct watchdog_device *dev) | |||
113 | int ret; | 117 | int ret; |
114 | unsigned int func; | 118 | unsigned int func; |
115 | 119 | ||
120 | if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status)) | ||
121 | return -EBUSY; | ||
122 | |||
116 | ret = -ENODEV; | 123 | ret = -ENODEV; |
117 | 124 | ||
118 | if (MACHINE_IS_VM) { | 125 | if (MACHINE_IS_VM) { |
119 | ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); | 126 | ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); |
120 | if (!ebc_cmd) | 127 | if (!ebc_cmd) { |
128 | clear_bit(DIAG_WDOG_BUSY, &wdt_status); | ||
121 | return -ENOMEM; | 129 | return -ENOMEM; |
130 | } | ||
122 | len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); | 131 | len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); |
123 | ASCEBC(ebc_cmd, MAX_CMDLEN); | 132 | ASCEBC(ebc_cmd, MAX_CMDLEN); |
124 | EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); | 133 | EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); |
@@ -135,6 +144,7 @@ static int wdt_start(struct watchdog_device *dev) | |||
135 | 144 | ||
136 | if (ret) { | 145 | if (ret) { |
137 | pr_err("The watchdog cannot be activated\n"); | 146 | pr_err("The watchdog cannot be activated\n"); |
147 | clear_bit(DIAG_WDOG_BUSY, &wdt_status); | ||
138 | return ret; | 148 | return ret; |
139 | } | 149 | } |
140 | return 0; | 150 | return 0; |
@@ -146,6 +156,9 @@ static int wdt_stop(struct watchdog_device *dev) | |||
146 | 156 | ||
147 | diag_stat_inc(DIAG_STAT_X288); | 157 | diag_stat_inc(DIAG_STAT_X288); |
148 | ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); | 158 | ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); |
159 | |||
160 | clear_bit(DIAG_WDOG_BUSY, &wdt_status); | ||
161 | |||
149 | return ret; | 162 | return ret; |
150 | } | 163 | } |
151 | 164 | ||
@@ -220,17 +233,10 @@ static struct watchdog_device wdt_dev = { | |||
220 | * It makes no sense to go into suspend while the watchdog is running. | 233 | * It makes no sense to go into suspend while the watchdog is running. |
221 | * Depending on the memory size, the watchdog might trigger, while we | 234 | * Depending on the memory size, the watchdog might trigger, while we |
222 | * are still saving the memory. | 235 | * are still saving the memory. |
223 | * We reuse the open flag to ensure that suspend and watchdog open are | ||
224 | * exclusive operations | ||
225 | */ | 236 | */ |
226 | static int wdt_suspend(void) | 237 | static int wdt_suspend(void) |
227 | { | 238 | { |
228 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdt_dev.status)) { | 239 | if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status)) { |
229 | pr_err("Linux cannot be suspended while the watchdog is in use\n"); | ||
230 | return notifier_from_errno(-EBUSY); | ||
231 | } | ||
232 | if (test_bit(WDOG_ACTIVE, &wdt_dev.status)) { | ||
233 | clear_bit(WDOG_DEV_OPEN, &wdt_dev.status); | ||
234 | pr_err("Linux cannot be suspended while the watchdog is in use\n"); | 240 | pr_err("Linux cannot be suspended while the watchdog is in use\n"); |
235 | return notifier_from_errno(-EBUSY); | 241 | return notifier_from_errno(-EBUSY); |
236 | } | 242 | } |
@@ -239,7 +245,7 @@ static int wdt_suspend(void) | |||
239 | 245 | ||
240 | static int wdt_resume(void) | 246 | static int wdt_resume(void) |
241 | { | 247 | { |
242 | clear_bit(WDOG_DEV_OPEN, &wdt_dev.status); | 248 | clear_bit(DIAG_WDOG_BUSY, &wdt_status); |
243 | return NOTIFY_DONE; | 249 | return NOTIFY_DONE; |
244 | } | 250 | } |
245 | 251 | ||
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c index 50abe1bf62a5..1ccb0b239348 100644 --- a/drivers/watchdog/digicolor_wdt.c +++ b/drivers/watchdog/digicolor_wdt.c | |||
@@ -15,7 +15,6 @@ | |||
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/clk.h> | 16 | #include <linux/clk.h> |
17 | #include <linux/watchdog.h> | 17 | #include <linux/watchdog.h> |
18 | #include <linux/reboot.h> | ||
19 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
20 | #include <linux/of_address.h> | 19 | #include <linux/of_address.h> |
21 | 20 | ||
@@ -28,7 +27,6 @@ | |||
28 | struct dc_wdt { | 27 | struct dc_wdt { |
29 | void __iomem *base; | 28 | void __iomem *base; |
30 | struct clk *clk; | 29 | struct clk *clk; |
31 | struct notifier_block restart_handler; | ||
32 | spinlock_t lock; | 30 | spinlock_t lock; |
33 | }; | 31 | }; |
34 | 32 | ||
@@ -50,16 +48,15 @@ static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) | |||
50 | spin_unlock_irqrestore(&wdt->lock, flags); | 48 | spin_unlock_irqrestore(&wdt->lock, flags); |
51 | } | 49 | } |
52 | 50 | ||
53 | static int dc_restart_handler(struct notifier_block *this, unsigned long mode, | 51 | static int dc_wdt_restart(struct watchdog_device *wdog) |
54 | void *cmd) | ||
55 | { | 52 | { |
56 | struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler); | 53 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); |
57 | 54 | ||
58 | dc_wdt_set(wdt, 1); | 55 | dc_wdt_set(wdt, 1); |
59 | /* wait for reset to assert... */ | 56 | /* wait for reset to assert... */ |
60 | mdelay(500); | 57 | mdelay(500); |
61 | 58 | ||
62 | return NOTIFY_DONE; | 59 | return 0; |
63 | } | 60 | } |
64 | 61 | ||
65 | static int dc_wdt_start(struct watchdog_device *wdog) | 62 | static int dc_wdt_start(struct watchdog_device *wdog) |
@@ -104,6 +101,7 @@ static struct watchdog_ops dc_wdt_ops = { | |||
104 | .stop = dc_wdt_stop, | 101 | .stop = dc_wdt_stop, |
105 | .set_timeout = dc_wdt_set_timeout, | 102 | .set_timeout = dc_wdt_set_timeout, |
106 | .get_timeleft = dc_wdt_get_timeleft, | 103 | .get_timeleft = dc_wdt_get_timeleft, |
104 | .restart = dc_wdt_restart, | ||
107 | }; | 105 | }; |
108 | 106 | ||
109 | static struct watchdog_info dc_wdt_info = { | 107 | static struct watchdog_info dc_wdt_info = { |
@@ -148,6 +146,7 @@ static int dc_wdt_probe(struct platform_device *pdev) | |||
148 | spin_lock_init(&wdt->lock); | 146 | spin_lock_init(&wdt->lock); |
149 | 147 | ||
150 | watchdog_set_drvdata(&dc_wdt_wdd, wdt); | 148 | watchdog_set_drvdata(&dc_wdt_wdd, wdt); |
149 | watchdog_set_restart_priority(&dc_wdt_wdd, 128); | ||
151 | watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); | 150 | watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); |
152 | ret = watchdog_register_device(&dc_wdt_wdd); | 151 | ret = watchdog_register_device(&dc_wdt_wdd); |
153 | if (ret) { | 152 | if (ret) { |
@@ -155,12 +154,6 @@ static int dc_wdt_probe(struct platform_device *pdev) | |||
155 | goto err_iounmap; | 154 | goto err_iounmap; |
156 | } | 155 | } |
157 | 156 | ||
158 | wdt->restart_handler.notifier_call = dc_restart_handler; | ||
159 | wdt->restart_handler.priority = 128; | ||
160 | ret = register_restart_handler(&wdt->restart_handler); | ||
161 | if (ret) | ||
162 | dev_warn(&pdev->dev, "cannot register restart handler\n"); | ||
163 | |||
164 | return 0; | 157 | return 0; |
165 | 158 | ||
166 | err_iounmap: | 159 | err_iounmap: |
@@ -172,7 +165,6 @@ static int dc_wdt_remove(struct platform_device *pdev) | |||
172 | { | 165 | { |
173 | struct dc_wdt *wdt = platform_get_drvdata(pdev); | 166 | struct dc_wdt *wdt = platform_get_drvdata(pdev); |
174 | 167 | ||
175 | unregister_restart_handler(&wdt->restart_handler); | ||
176 | watchdog_unregister_device(&dc_wdt_wdd); | 168 | watchdog_unregister_device(&dc_wdt_wdd); |
177 | iounmap(wdt->base); | 169 | iounmap(wdt->base); |
178 | 170 | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 6ea0634345e9..8fefa4ad46d4 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c | |||
@@ -81,7 +81,7 @@ static inline int dw_wdt_top_in_seconds(unsigned top) | |||
81 | * There are 16 possible timeout values in 0..15 where the number of | 81 | * There are 16 possible timeout values in 0..15 where the number of |
82 | * cycles is 2 ^ (16 + i) and the watchdog counts down. | 82 | * cycles is 2 ^ (16 + i) and the watchdog counts down. |
83 | */ | 83 | */ |
84 | return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk); | 84 | return (1U << (16 + top)) / clk_get_rate(dw_wdt.clk); |
85 | } | 85 | } |
86 | 86 | ||
87 | static int dw_wdt_get_top(void) | 87 | static int dw_wdt_get_top(void) |
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 90d59d3f38a3..ba066e4a707b 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c | |||
@@ -12,10 +12,8 @@ | |||
12 | #include <linux/err.h> | 12 | #include <linux/err.h> |
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/notifier.h> | ||
16 | #include <linux/of_gpio.h> | 15 | #include <linux/of_gpio.h> |
17 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
18 | #include <linux/reboot.h> | ||
19 | #include <linux/watchdog.h> | 17 | #include <linux/watchdog.h> |
20 | 18 | ||
21 | #define SOFT_TIMEOUT_MIN 1 | 19 | #define SOFT_TIMEOUT_MIN 1 |
@@ -36,7 +34,6 @@ struct gpio_wdt_priv { | |||
36 | unsigned int hw_algo; | 34 | unsigned int hw_algo; |
37 | unsigned int hw_margin; | 35 | unsigned int hw_margin; |
38 | unsigned long last_jiffies; | 36 | unsigned long last_jiffies; |
39 | struct notifier_block notifier; | ||
40 | struct timer_list timer; | 37 | struct timer_list timer; |
41 | struct watchdog_device wdd; | 38 | struct watchdog_device wdd; |
42 | }; | 39 | }; |
@@ -57,7 +54,8 @@ static void gpio_wdt_hwping(unsigned long data) | |||
57 | 54 | ||
58 | if (priv->armed && time_after(jiffies, priv->last_jiffies + | 55 | if (priv->armed && time_after(jiffies, priv->last_jiffies + |
59 | msecs_to_jiffies(wdd->timeout * 1000))) { | 56 | msecs_to_jiffies(wdd->timeout * 1000))) { |
60 | dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); | 57 | dev_crit(wdd->parent, |
58 | "Timer expired. System will reboot soon!\n"); | ||
61 | return; | 59 | return; |
62 | } | 60 | } |
63 | 61 | ||
@@ -126,26 +124,6 @@ static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) | |||
126 | return gpio_wdt_ping(wdd); | 124 | return gpio_wdt_ping(wdd); |
127 | } | 125 | } |
128 | 126 | ||
129 | static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code, | ||
130 | void *unused) | ||
131 | { | ||
132 | struct gpio_wdt_priv *priv = container_of(nb, struct gpio_wdt_priv, | ||
133 | notifier); | ||
134 | |||
135 | mod_timer(&priv->timer, 0); | ||
136 | |||
137 | switch (code) { | ||
138 | case SYS_HALT: | ||
139 | case SYS_POWER_OFF: | ||
140 | gpio_wdt_disable(priv); | ||
141 | break; | ||
142 | default: | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | return NOTIFY_DONE; | ||
147 | } | ||
148 | |||
149 | static const struct watchdog_info gpio_wdt_ident = { | 127 | static const struct watchdog_info gpio_wdt_ident = { |
150 | .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | | 128 | .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | |
151 | WDIOF_SETTIMEOUT, | 129 | WDIOF_SETTIMEOUT, |
@@ -224,23 +202,16 @@ static int gpio_wdt_probe(struct platform_device *pdev) | |||
224 | 202 | ||
225 | setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd); | 203 | setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd); |
226 | 204 | ||
205 | watchdog_stop_on_reboot(&priv->wdd); | ||
206 | |||
227 | ret = watchdog_register_device(&priv->wdd); | 207 | ret = watchdog_register_device(&priv->wdd); |
228 | if (ret) | 208 | if (ret) |
229 | return ret; | 209 | return ret; |
230 | 210 | ||
231 | priv->notifier.notifier_call = gpio_wdt_notify_sys; | ||
232 | ret = register_reboot_notifier(&priv->notifier); | ||
233 | if (ret) | ||
234 | goto error_unregister; | ||
235 | |||
236 | if (priv->always_running) | 211 | if (priv->always_running) |
237 | gpio_wdt_start_impl(priv); | 212 | gpio_wdt_start_impl(priv); |
238 | 213 | ||
239 | return 0; | 214 | return 0; |
240 | |||
241 | error_unregister: | ||
242 | watchdog_unregister_device(&priv->wdd); | ||
243 | return ret; | ||
244 | } | 215 | } |
245 | 216 | ||
246 | static int gpio_wdt_remove(struct platform_device *pdev) | 217 | static int gpio_wdt_remove(struct platform_device *pdev) |
@@ -248,7 +219,6 @@ static int gpio_wdt_remove(struct platform_device *pdev) | |||
248 | struct gpio_wdt_priv *priv = platform_get_drvdata(pdev); | 219 | struct gpio_wdt_priv *priv = platform_get_drvdata(pdev); |
249 | 220 | ||
250 | del_timer_sync(&priv->timer); | 221 | del_timer_sync(&priv->timer); |
251 | unregister_reboot_notifier(&priv->notifier); | ||
252 | watchdog_unregister_device(&priv->wdd); | 222 | watchdog_unregister_device(&priv->wdd); |
253 | 223 | ||
254 | return 0; | 224 | return 0; |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 286369d4f0f5..92443c319e59 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -1,11 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * HP WatchDog Driver | 2 | * HPE WatchDog Driver |
3 | * based on | 3 | * based on |
4 | * | 4 | * |
5 | * SoftDog 0.05: A Software Watchdog Device | 5 | * SoftDog 0.05: A Software Watchdog Device |
6 | * | 6 | * |
7 | * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. | 7 | * (c) Copyright 2015 Hewlett Packard Enterprise Development LP |
8 | * Thomas Mingarelli <thomas.mingarelli@hp.com> | 8 | * Thomas Mingarelli <thomas.mingarelli@hpe.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or | 10 | * This program is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU General Public License | 11 | * modify it under the terms of the GNU General Public License |
@@ -580,7 +580,7 @@ static const struct watchdog_info ident = { | |||
580 | .options = WDIOF_SETTIMEOUT | | 580 | .options = WDIOF_SETTIMEOUT | |
581 | WDIOF_KEEPALIVEPING | | 581 | WDIOF_KEEPALIVEPING | |
582 | WDIOF_MAGICCLOSE, | 582 | WDIOF_MAGICCLOSE, |
583 | .identity = "HP iLO2+ HW Watchdog Timer", | 583 | .identity = "HPE iLO2+ HW Watchdog Timer", |
584 | }; | 584 | }; |
585 | 585 | ||
586 | static long hpwdt_ioctl(struct file *file, unsigned int cmd, | 586 | static long hpwdt_ioctl(struct file *file, unsigned int cmd, |
@@ -758,7 +758,7 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) | |||
758 | goto error2; | 758 | goto error2; |
759 | 759 | ||
760 | dev_info(&dev->dev, | 760 | dev_info(&dev->dev, |
761 | "HP Watchdog Timer Driver: NMI decoding initialized" | 761 | "HPE Watchdog Timer Driver: NMI decoding initialized" |
762 | ", allow kernel dump: %s (default = 1/ON)\n", | 762 | ", allow kernel dump: %s (default = 1/ON)\n", |
763 | (allow_kdump == 0) ? "OFF" : "ON"); | 763 | (allow_kdump == 0) ? "OFF" : "ON"); |
764 | return 0; | 764 | return 0; |
@@ -863,7 +863,7 @@ static int hpwdt_init_one(struct pci_dev *dev, | |||
863 | goto error_misc_register; | 863 | goto error_misc_register; |
864 | } | 864 | } |
865 | 865 | ||
866 | dev_info(&dev->dev, "HP Watchdog Timer Driver: %s" | 866 | dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s" |
867 | ", timer margin: %d seconds (nowayout=%d).\n", | 867 | ", timer margin: %d seconds (nowayout=%d).\n", |
868 | HPWDT_VERSION, soft_margin, nowayout); | 868 | HPWDT_VERSION, soft_margin, nowayout); |
869 | return 0; | 869 | return 0; |
diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 15ab07230960..3679f2e1922f 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c | |||
@@ -45,7 +45,6 @@ | |||
45 | #include <linux/log2.h> | 45 | #include <linux/log2.h> |
46 | #include <linux/module.h> | 46 | #include <linux/module.h> |
47 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
48 | #include <linux/reboot.h> | ||
49 | #include <linux/slab.h> | 48 | #include <linux/slab.h> |
50 | #include <linux/watchdog.h> | 49 | #include <linux/watchdog.h> |
51 | 50 | ||
@@ -87,7 +86,6 @@ struct pdc_wdt_dev { | |||
87 | struct clk *wdt_clk; | 86 | struct clk *wdt_clk; |
88 | struct clk *sys_clk; | 87 | struct clk *sys_clk; |
89 | void __iomem *base; | 88 | void __iomem *base; |
90 | struct notifier_block restart_handler; | ||
91 | }; | 89 | }; |
92 | 90 | ||
93 | static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) | 91 | static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) |
@@ -152,6 +150,16 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev) | |||
152 | return 0; | 150 | return 0; |
153 | } | 151 | } |
154 | 152 | ||
153 | static int pdc_wdt_restart(struct watchdog_device *wdt_dev) | ||
154 | { | ||
155 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); | ||
156 | |||
157 | /* Assert SOFT_RESET */ | ||
158 | writel(0x1, wdt->base + PDC_WDT_SOFT_RESET); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
155 | static struct watchdog_info pdc_wdt_info = { | 163 | static struct watchdog_info pdc_wdt_info = { |
156 | .identity = "IMG PDC Watchdog", | 164 | .identity = "IMG PDC Watchdog", |
157 | .options = WDIOF_SETTIMEOUT | | 165 | .options = WDIOF_SETTIMEOUT | |
@@ -165,20 +173,9 @@ static const struct watchdog_ops pdc_wdt_ops = { | |||
165 | .stop = pdc_wdt_stop, | 173 | .stop = pdc_wdt_stop, |
166 | .ping = pdc_wdt_keepalive, | 174 | .ping = pdc_wdt_keepalive, |
167 | .set_timeout = pdc_wdt_set_timeout, | 175 | .set_timeout = pdc_wdt_set_timeout, |
176 | .restart = pdc_wdt_restart, | ||
168 | }; | 177 | }; |
169 | 178 | ||
170 | static int pdc_wdt_restart(struct notifier_block *this, unsigned long mode, | ||
171 | void *cmd) | ||
172 | { | ||
173 | struct pdc_wdt_dev *wdt = container_of(this, struct pdc_wdt_dev, | ||
174 | restart_handler); | ||
175 | |||
176 | /* Assert SOFT_RESET */ | ||
177 | writel(0x1, wdt->base + PDC_WDT_SOFT_RESET); | ||
178 | |||
179 | return NOTIFY_OK; | ||
180 | } | ||
181 | |||
182 | static int pdc_wdt_probe(struct platform_device *pdev) | 179 | static int pdc_wdt_probe(struct platform_device *pdev) |
183 | { | 180 | { |
184 | u64 div; | 181 | u64 div; |
@@ -282,6 +279,7 @@ static int pdc_wdt_probe(struct platform_device *pdev) | |||
282 | } | 279 | } |
283 | 280 | ||
284 | watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout); | 281 | watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout); |
282 | watchdog_set_restart_priority(&pdc_wdt->wdt_dev, 128); | ||
285 | 283 | ||
286 | platform_set_drvdata(pdev, pdc_wdt); | 284 | platform_set_drvdata(pdev, pdc_wdt); |
287 | 285 | ||
@@ -289,13 +287,6 @@ static int pdc_wdt_probe(struct platform_device *pdev) | |||
289 | if (ret) | 287 | if (ret) |
290 | goto disable_wdt_clk; | 288 | goto disable_wdt_clk; |
291 | 289 | ||
292 | pdc_wdt->restart_handler.notifier_call = pdc_wdt_restart; | ||
293 | pdc_wdt->restart_handler.priority = 128; | ||
294 | ret = register_restart_handler(&pdc_wdt->restart_handler); | ||
295 | if (ret) | ||
296 | dev_warn(&pdev->dev, "failed to register restart handler: %d\n", | ||
297 | ret); | ||
298 | |||
299 | return 0; | 290 | return 0; |
300 | 291 | ||
301 | disable_wdt_clk: | 292 | disable_wdt_clk: |
@@ -316,7 +307,6 @@ static int pdc_wdt_remove(struct platform_device *pdev) | |||
316 | { | 307 | { |
317 | struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); | 308 | struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); |
318 | 309 | ||
319 | unregister_restart_handler(&pdc_wdt->restart_handler); | ||
320 | pdc_wdt_stop(&pdc_wdt->wdt_dev); | 310 | pdc_wdt_stop(&pdc_wdt->wdt_dev); |
321 | watchdog_unregister_device(&pdc_wdt->wdt_dev); | 311 | watchdog_unregister_device(&pdc_wdt->wdt_dev); |
322 | clk_disable_unprepare(pdc_wdt->wdt_clk); | 312 | clk_disable_unprepare(pdc_wdt->wdt_clk); |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 29ef719a6a3c..e47966aa2db0 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -29,10 +29,8 @@ | |||
29 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/moduleparam.h> | 31 | #include <linux/moduleparam.h> |
32 | #include <linux/notifier.h> | ||
33 | #include <linux/of_address.h> | 32 | #include <linux/of_address.h> |
34 | #include <linux/platform_device.h> | 33 | #include <linux/platform_device.h> |
35 | #include <linux/reboot.h> | ||
36 | #include <linux/regmap.h> | 34 | #include <linux/regmap.h> |
37 | #include <linux/timer.h> | 35 | #include <linux/timer.h> |
38 | #include <linux/watchdog.h> | 36 | #include <linux/watchdog.h> |
@@ -64,7 +62,6 @@ struct imx2_wdt_device { | |||
64 | struct regmap *regmap; | 62 | struct regmap *regmap; |
65 | struct timer_list timer; /* Pings the watchdog when closed */ | 63 | struct timer_list timer; /* Pings the watchdog when closed */ |
66 | struct watchdog_device wdog; | 64 | struct watchdog_device wdog; |
67 | struct notifier_block restart_handler; | ||
68 | }; | 65 | }; |
69 | 66 | ||
70 | static bool nowayout = WATCHDOG_NOWAYOUT; | 67 | static bool nowayout = WATCHDOG_NOWAYOUT; |
@@ -83,13 +80,11 @@ static const struct watchdog_info imx2_wdt_info = { | |||
83 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | 80 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, |
84 | }; | 81 | }; |
85 | 82 | ||
86 | static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, | 83 | static int imx2_wdt_restart(struct watchdog_device *wdog) |
87 | void *cmd) | ||
88 | { | 84 | { |
85 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | ||
89 | unsigned int wcr_enable = IMX2_WDT_WCR_WDE; | 86 | unsigned int wcr_enable = IMX2_WDT_WCR_WDE; |
90 | struct imx2_wdt_device *wdev = container_of(this, | 87 | |
91 | struct imx2_wdt_device, | ||
92 | restart_handler); | ||
93 | /* Assert SRS signal */ | 88 | /* Assert SRS signal */ |
94 | regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable); | 89 | regmap_write(wdev->regmap, IMX2_WDT_WCR, wcr_enable); |
95 | /* | 90 | /* |
@@ -105,7 +100,7 @@ static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, | |||
105 | /* wait for reset to assert... */ | 100 | /* wait for reset to assert... */ |
106 | mdelay(500); | 101 | mdelay(500); |
107 | 102 | ||
108 | return NOTIFY_DONE; | 103 | return 0; |
109 | } | 104 | } |
110 | 105 | ||
111 | static inline void imx2_wdt_setup(struct watchdog_device *wdog) | 106 | static inline void imx2_wdt_setup(struct watchdog_device *wdog) |
@@ -213,6 +208,7 @@ static const struct watchdog_ops imx2_wdt_ops = { | |||
213 | .stop = imx2_wdt_stop, | 208 | .stop = imx2_wdt_stop, |
214 | .ping = imx2_wdt_ping, | 209 | .ping = imx2_wdt_ping, |
215 | .set_timeout = imx2_wdt_set_timeout, | 210 | .set_timeout = imx2_wdt_set_timeout, |
211 | .restart = imx2_wdt_restart, | ||
216 | }; | 212 | }; |
217 | 213 | ||
218 | static const struct regmap_config imx2_wdt_regmap_config = { | 214 | static const struct regmap_config imx2_wdt_regmap_config = { |
@@ -275,6 +271,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
275 | platform_set_drvdata(pdev, wdog); | 271 | platform_set_drvdata(pdev, wdog); |
276 | watchdog_set_drvdata(wdog, wdev); | 272 | watchdog_set_drvdata(wdog, wdev); |
277 | watchdog_set_nowayout(wdog, nowayout); | 273 | watchdog_set_nowayout(wdog, nowayout); |
274 | watchdog_set_restart_priority(wdog, 128); | ||
278 | watchdog_init_timeout(wdog, timeout, &pdev->dev); | 275 | watchdog_init_timeout(wdog, timeout, &pdev->dev); |
279 | 276 | ||
280 | setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog); | 277 | setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog); |
@@ -294,12 +291,6 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
294 | goto disable_clk; | 291 | goto disable_clk; |
295 | } | 292 | } |
296 | 293 | ||
297 | wdev->restart_handler.notifier_call = imx2_restart_handler; | ||
298 | wdev->restart_handler.priority = 128; | ||
299 | ret = register_restart_handler(&wdev->restart_handler); | ||
300 | if (ret) | ||
301 | dev_err(&pdev->dev, "cannot register restart handler\n"); | ||
302 | |||
303 | dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", | 294 | dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", |
304 | wdog->timeout, nowayout); | 295 | wdog->timeout, nowayout); |
305 | 296 | ||
@@ -315,8 +306,6 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev) | |||
315 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 306 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
316 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 307 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
317 | 308 | ||
318 | unregister_restart_handler(&wdev->restart_handler); | ||
319 | |||
320 | watchdog_unregister_device(wdog); | 309 | watchdog_unregister_device(wdog); |
321 | 310 | ||
322 | if (imx2_wdt_is_running(wdev)) { | 311 | if (imx2_wdt_is_running(wdev)) { |
diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index ab7b8b185d99..6914c83aa6d9 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c | |||
@@ -18,7 +18,6 @@ | |||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/of.h> | 19 | #include <linux/of.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/reboot.h> | ||
22 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
23 | 22 | ||
24 | /* Registers */ | 23 | /* Registers */ |
@@ -59,7 +58,6 @@ struct lpc18xx_wdt_dev { | |||
59 | unsigned long clk_rate; | 58 | unsigned long clk_rate; |
60 | void __iomem *base; | 59 | void __iomem *base; |
61 | struct timer_list timer; | 60 | struct timer_list timer; |
62 | struct notifier_block restart_handler; | ||
63 | spinlock_t lock; | 61 | spinlock_t lock; |
64 | }; | 62 | }; |
65 | 63 | ||
@@ -155,27 +153,9 @@ static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev) | |||
155 | return 0; | 153 | return 0; |
156 | } | 154 | } |
157 | 155 | ||
158 | static struct watchdog_info lpc18xx_wdt_info = { | 156 | static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev) |
159 | .identity = "NXP LPC18xx Watchdog", | ||
160 | .options = WDIOF_SETTIMEOUT | | ||
161 | WDIOF_KEEPALIVEPING | | ||
162 | WDIOF_MAGICCLOSE, | ||
163 | }; | ||
164 | |||
165 | static const struct watchdog_ops lpc18xx_wdt_ops = { | ||
166 | .owner = THIS_MODULE, | ||
167 | .start = lpc18xx_wdt_start, | ||
168 | .stop = lpc18xx_wdt_stop, | ||
169 | .ping = lpc18xx_wdt_feed, | ||
170 | .set_timeout = lpc18xx_wdt_set_timeout, | ||
171 | .get_timeleft = lpc18xx_wdt_get_timeleft, | ||
172 | }; | ||
173 | |||
174 | static int lpc18xx_wdt_restart(struct notifier_block *this, unsigned long mode, | ||
175 | void *cmd) | ||
176 | { | 157 | { |
177 | struct lpc18xx_wdt_dev *lpc18xx_wdt = container_of(this, | 158 | struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); |
178 | struct lpc18xx_wdt_dev, restart_handler); | ||
179 | unsigned long flags; | 159 | unsigned long flags; |
180 | int val; | 160 | int val; |
181 | 161 | ||
@@ -197,9 +177,26 @@ static int lpc18xx_wdt_restart(struct notifier_block *this, unsigned long mode, | |||
197 | 177 | ||
198 | spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags); | 178 | spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags); |
199 | 179 | ||
200 | return NOTIFY_OK; | 180 | return 0; |
201 | } | 181 | } |
202 | 182 | ||
183 | static struct watchdog_info lpc18xx_wdt_info = { | ||
184 | .identity = "NXP LPC18xx Watchdog", | ||
185 | .options = WDIOF_SETTIMEOUT | | ||
186 | WDIOF_KEEPALIVEPING | | ||
187 | WDIOF_MAGICCLOSE, | ||
188 | }; | ||
189 | |||
190 | static const struct watchdog_ops lpc18xx_wdt_ops = { | ||
191 | .owner = THIS_MODULE, | ||
192 | .start = lpc18xx_wdt_start, | ||
193 | .stop = lpc18xx_wdt_stop, | ||
194 | .ping = lpc18xx_wdt_feed, | ||
195 | .set_timeout = lpc18xx_wdt_set_timeout, | ||
196 | .get_timeleft = lpc18xx_wdt_get_timeleft, | ||
197 | .restart = lpc18xx_wdt_restart, | ||
198 | }; | ||
199 | |||
203 | static int lpc18xx_wdt_probe(struct platform_device *pdev) | 200 | static int lpc18xx_wdt_probe(struct platform_device *pdev) |
204 | { | 201 | { |
205 | struct lpc18xx_wdt_dev *lpc18xx_wdt; | 202 | struct lpc18xx_wdt_dev *lpc18xx_wdt; |
@@ -273,6 +270,7 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev) | |||
273 | (unsigned long)&lpc18xx_wdt->wdt_dev); | 270 | (unsigned long)&lpc18xx_wdt->wdt_dev); |
274 | 271 | ||
275 | watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout); | 272 | watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout); |
273 | watchdog_set_restart_priority(&lpc18xx_wdt->wdt_dev, 128); | ||
276 | 274 | ||
277 | platform_set_drvdata(pdev, lpc18xx_wdt); | 275 | platform_set_drvdata(pdev, lpc18xx_wdt); |
278 | 276 | ||
@@ -280,12 +278,6 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev) | |||
280 | if (ret) | 278 | if (ret) |
281 | goto disable_wdt_clk; | 279 | goto disable_wdt_clk; |
282 | 280 | ||
283 | lpc18xx_wdt->restart_handler.notifier_call = lpc18xx_wdt_restart; | ||
284 | lpc18xx_wdt->restart_handler.priority = 128; | ||
285 | ret = register_restart_handler(&lpc18xx_wdt->restart_handler); | ||
286 | if (ret) | ||
287 | dev_warn(dev, "failed to register restart handler: %d\n", ret); | ||
288 | |||
289 | return 0; | 281 | return 0; |
290 | 282 | ||
291 | disable_wdt_clk: | 283 | disable_wdt_clk: |
@@ -306,8 +298,6 @@ static int lpc18xx_wdt_remove(struct platform_device *pdev) | |||
306 | { | 298 | { |
307 | struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); | 299 | struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); |
308 | 300 | ||
309 | unregister_restart_handler(&lpc18xx_wdt->restart_handler); | ||
310 | |||
311 | dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n"); | 301 | dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n"); |
312 | del_timer(&lpc18xx_wdt->timer); | 302 | del_timer(&lpc18xx_wdt->timer); |
313 | 303 | ||
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index 098fa9c34d6d..af6a7c489f08 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c | |||
@@ -100,12 +100,12 @@ static int a21_wdt_set_timeout(struct watchdog_device *wdt, | |||
100 | struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); | 100 | struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); |
101 | 101 | ||
102 | if (timeout != 1 && timeout != 30) { | 102 | if (timeout != 1 && timeout != 30) { |
103 | dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n"); | 103 | dev_err(wdt->parent, "Only 1 and 30 allowed as timeout\n"); |
104 | return -EINVAL; | 104 | return -EINVAL; |
105 | } | 105 | } |
106 | 106 | ||
107 | if (timeout == 30 && wdt->timeout == 1) { | 107 | if (timeout == 30 && wdt->timeout == 1) { |
108 | dev_err(wdt->dev, | 108 | dev_err(wdt->parent, |
109 | "Transition from fast to slow mode not allowed\n"); | 109 | "Transition from fast to slow mode not allowed\n"); |
110 | return -EINVAL; | 110 | return -EINVAL; |
111 | } | 111 | } |
diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index 1f4155ee3404..aea5d2f44ad7 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c | |||
@@ -17,51 +17,64 @@ | |||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/moduleparam.h> | 19 | #include <linux/moduleparam.h> |
20 | #include <linux/notifier.h> | ||
21 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_device.h> | ||
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/reboot.h> | ||
24 | #include <linux/types.h> | 23 | #include <linux/types.h> |
25 | #include <linux/watchdog.h> | 24 | #include <linux/watchdog.h> |
26 | 25 | ||
27 | #define DRV_NAME "meson_wdt" | 26 | #define DRV_NAME "meson_wdt" |
28 | 27 | ||
29 | #define MESON_WDT_TC 0x00 | 28 | #define MESON_WDT_TC 0x00 |
30 | #define MESON_WDT_TC_EN BIT(22) | ||
31 | #define MESON_WDT_TC_TM_MASK 0x3fffff | ||
32 | #define MESON_WDT_DC_RESET (3 << 24) | 29 | #define MESON_WDT_DC_RESET (3 << 24) |
33 | 30 | ||
34 | #define MESON_WDT_RESET 0x04 | 31 | #define MESON_WDT_RESET 0x04 |
35 | 32 | ||
36 | #define MESON_WDT_TIMEOUT 30 | 33 | #define MESON_WDT_TIMEOUT 30 |
37 | #define MESON_WDT_MIN_TIMEOUT 1 | 34 | #define MESON_WDT_MIN_TIMEOUT 1 |
38 | #define MESON_WDT_MAX_TIMEOUT (MESON_WDT_TC_TM_MASK / 100000) | ||
39 | 35 | ||
40 | #define MESON_SEC_TO_TC(s) ((s) * 100000) | 36 | #define MESON_SEC_TO_TC(s, c) ((s) * (c)) |
41 | 37 | ||
42 | static bool nowayout = WATCHDOG_NOWAYOUT; | 38 | static bool nowayout = WATCHDOG_NOWAYOUT; |
43 | static unsigned int timeout = MESON_WDT_TIMEOUT; | 39 | static unsigned int timeout = MESON_WDT_TIMEOUT; |
44 | 40 | ||
41 | struct meson_wdt_data { | ||
42 | unsigned int enable; | ||
43 | unsigned int terminal_count_mask; | ||
44 | unsigned int count_unit; | ||
45 | }; | ||
46 | |||
47 | static struct meson_wdt_data meson6_wdt_data = { | ||
48 | .enable = BIT(22), | ||
49 | .terminal_count_mask = 0x3fffff, | ||
50 | .count_unit = 100000, /* 10 us */ | ||
51 | }; | ||
52 | |||
53 | static struct meson_wdt_data meson8b_wdt_data = { | ||
54 | .enable = BIT(19), | ||
55 | .terminal_count_mask = 0xffff, | ||
56 | .count_unit = 7812, /* 128 us */ | ||
57 | }; | ||
58 | |||
45 | struct meson_wdt_dev { | 59 | struct meson_wdt_dev { |
46 | struct watchdog_device wdt_dev; | 60 | struct watchdog_device wdt_dev; |
47 | void __iomem *wdt_base; | 61 | void __iomem *wdt_base; |
48 | struct notifier_block restart_handler; | 62 | const struct meson_wdt_data *data; |
49 | }; | 63 | }; |
50 | 64 | ||
51 | static int meson_restart_handle(struct notifier_block *this, unsigned long mode, | 65 | static int meson_wdt_restart(struct watchdog_device *wdt_dev) |
52 | void *cmd) | ||
53 | { | 66 | { |
54 | u32 tc_reboot = MESON_WDT_DC_RESET | MESON_WDT_TC_EN; | 67 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); |
55 | struct meson_wdt_dev *meson_wdt = container_of(this, | 68 | u32 tc_reboot = MESON_WDT_DC_RESET; |
56 | struct meson_wdt_dev, | 69 | |
57 | restart_handler); | 70 | tc_reboot |= meson_wdt->data->enable; |
58 | 71 | ||
59 | while (1) { | 72 | while (1) { |
60 | writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC); | 73 | writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC); |
61 | mdelay(5); | 74 | mdelay(5); |
62 | } | 75 | } |
63 | 76 | ||
64 | return NOTIFY_DONE; | 77 | return 0; |
65 | } | 78 | } |
66 | 79 | ||
67 | static int meson_wdt_ping(struct watchdog_device *wdt_dev) | 80 | static int meson_wdt_ping(struct watchdog_device *wdt_dev) |
@@ -80,8 +93,8 @@ static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev, | |||
80 | u32 reg; | 93 | u32 reg; |
81 | 94 | ||
82 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | 95 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); |
83 | reg &= ~MESON_WDT_TC_TM_MASK; | 96 | reg &= ~meson_wdt->data->terminal_count_mask; |
84 | reg |= MESON_SEC_TO_TC(timeout); | 97 | reg |= MESON_SEC_TO_TC(timeout, meson_wdt->data->count_unit); |
85 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | 98 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); |
86 | } | 99 | } |
87 | 100 | ||
@@ -102,7 +115,7 @@ static int meson_wdt_stop(struct watchdog_device *wdt_dev) | |||
102 | u32 reg; | 115 | u32 reg; |
103 | 116 | ||
104 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | 117 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); |
105 | reg &= ~MESON_WDT_TC_EN; | 118 | reg &= ~meson_wdt->data->enable; |
106 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | 119 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); |
107 | 120 | ||
108 | return 0; | 121 | return 0; |
@@ -117,7 +130,7 @@ static int meson_wdt_start(struct watchdog_device *wdt_dev) | |||
117 | meson_wdt_ping(wdt_dev); | 130 | meson_wdt_ping(wdt_dev); |
118 | 131 | ||
119 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | 132 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); |
120 | reg |= MESON_WDT_TC_EN; | 133 | reg |= meson_wdt->data->enable; |
121 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | 134 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); |
122 | 135 | ||
123 | return 0; | 136 | return 0; |
@@ -136,12 +149,21 @@ static const struct watchdog_ops meson_wdt_ops = { | |||
136 | .stop = meson_wdt_stop, | 149 | .stop = meson_wdt_stop, |
137 | .ping = meson_wdt_ping, | 150 | .ping = meson_wdt_ping, |
138 | .set_timeout = meson_wdt_set_timeout, | 151 | .set_timeout = meson_wdt_set_timeout, |
152 | .restart = meson_wdt_restart, | ||
153 | }; | ||
154 | |||
155 | static const struct of_device_id meson_wdt_dt_ids[] = { | ||
156 | { .compatible = "amlogic,meson6-wdt", .data = &meson6_wdt_data }, | ||
157 | { .compatible = "amlogic,meson8b-wdt", .data = &meson8b_wdt_data }, | ||
158 | { /* sentinel */ } | ||
139 | }; | 159 | }; |
160 | MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids); | ||
140 | 161 | ||
141 | static int meson_wdt_probe(struct platform_device *pdev) | 162 | static int meson_wdt_probe(struct platform_device *pdev) |
142 | { | 163 | { |
143 | struct resource *res; | 164 | struct resource *res; |
144 | struct meson_wdt_dev *meson_wdt; | 165 | struct meson_wdt_dev *meson_wdt; |
166 | const struct of_device_id *of_id; | ||
145 | int err; | 167 | int err; |
146 | 168 | ||
147 | meson_wdt = devm_kzalloc(&pdev->dev, sizeof(*meson_wdt), GFP_KERNEL); | 169 | meson_wdt = devm_kzalloc(&pdev->dev, sizeof(*meson_wdt), GFP_KERNEL); |
@@ -153,17 +175,28 @@ static int meson_wdt_probe(struct platform_device *pdev) | |||
153 | if (IS_ERR(meson_wdt->wdt_base)) | 175 | if (IS_ERR(meson_wdt->wdt_base)) |
154 | return PTR_ERR(meson_wdt->wdt_base); | 176 | return PTR_ERR(meson_wdt->wdt_base); |
155 | 177 | ||
178 | of_id = of_match_device(meson_wdt_dt_ids, &pdev->dev); | ||
179 | if (!of_id) { | ||
180 | dev_err(&pdev->dev, "Unable to initialize WDT data\n"); | ||
181 | return -ENODEV; | ||
182 | } | ||
183 | meson_wdt->data = of_id->data; | ||
184 | |||
156 | meson_wdt->wdt_dev.parent = &pdev->dev; | 185 | meson_wdt->wdt_dev.parent = &pdev->dev; |
157 | meson_wdt->wdt_dev.info = &meson_wdt_info; | 186 | meson_wdt->wdt_dev.info = &meson_wdt_info; |
158 | meson_wdt->wdt_dev.ops = &meson_wdt_ops; | 187 | meson_wdt->wdt_dev.ops = &meson_wdt_ops; |
159 | meson_wdt->wdt_dev.timeout = MESON_WDT_TIMEOUT; | 188 | meson_wdt->wdt_dev.max_timeout = |
160 | meson_wdt->wdt_dev.max_timeout = MESON_WDT_MAX_TIMEOUT; | 189 | meson_wdt->data->terminal_count_mask / meson_wdt->data->count_unit; |
161 | meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT; | 190 | meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT; |
191 | meson_wdt->wdt_dev.timeout = min_t(unsigned int, | ||
192 | MESON_WDT_TIMEOUT, | ||
193 | meson_wdt->wdt_dev.max_timeout); | ||
162 | 194 | ||
163 | watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt); | 195 | watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt); |
164 | 196 | ||
165 | watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, &pdev->dev); | 197 | watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, &pdev->dev); |
166 | watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout); | 198 | watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout); |
199 | watchdog_set_restart_priority(&meson_wdt->wdt_dev, 128); | ||
167 | 200 | ||
168 | meson_wdt_stop(&meson_wdt->wdt_dev); | 201 | meson_wdt_stop(&meson_wdt->wdt_dev); |
169 | 202 | ||
@@ -173,13 +206,6 @@ static int meson_wdt_probe(struct platform_device *pdev) | |||
173 | 206 | ||
174 | platform_set_drvdata(pdev, meson_wdt); | 207 | platform_set_drvdata(pdev, meson_wdt); |
175 | 208 | ||
176 | meson_wdt->restart_handler.notifier_call = meson_restart_handle; | ||
177 | meson_wdt->restart_handler.priority = 128; | ||
178 | err = register_restart_handler(&meson_wdt->restart_handler); | ||
179 | if (err) | ||
180 | dev_err(&pdev->dev, | ||
181 | "cannot register restart handler (err=%d)\n", err); | ||
182 | |||
183 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", | 209 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", |
184 | meson_wdt->wdt_dev.timeout, nowayout); | 210 | meson_wdt->wdt_dev.timeout, nowayout); |
185 | 211 | ||
@@ -190,8 +216,6 @@ static int meson_wdt_remove(struct platform_device *pdev) | |||
190 | { | 216 | { |
191 | struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); | 217 | struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); |
192 | 218 | ||
193 | unregister_restart_handler(&meson_wdt->restart_handler); | ||
194 | |||
195 | watchdog_unregister_device(&meson_wdt->wdt_dev); | 219 | watchdog_unregister_device(&meson_wdt->wdt_dev); |
196 | 220 | ||
197 | return 0; | 221 | return 0; |
@@ -204,12 +228,6 @@ static void meson_wdt_shutdown(struct platform_device *pdev) | |||
204 | meson_wdt_stop(&meson_wdt->wdt_dev); | 228 | meson_wdt_stop(&meson_wdt->wdt_dev); |
205 | } | 229 | } |
206 | 230 | ||
207 | static const struct of_device_id meson_wdt_dt_ids[] = { | ||
208 | { .compatible = "amlogic,meson6-wdt" }, | ||
209 | { /* sentinel */ } | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids); | ||
212 | |||
213 | static struct platform_driver meson_wdt_driver = { | 231 | static struct platform_driver meson_wdt_driver = { |
214 | .probe = meson_wdt_probe, | 232 | .probe = meson_wdt_probe, |
215 | .remove = meson_wdt_remove, | 233 | .remove = meson_wdt_remove, |
diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c index 60b0605bd7e6..885c81bc4210 100644 --- a/drivers/watchdog/moxart_wdt.c +++ b/drivers/watchdog/moxart_wdt.c | |||
@@ -15,9 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/err.h> | 16 | #include <linux/err.h> |
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/notifier.h> | ||
19 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
20 | #include <linux/reboot.h> | ||
21 | #include <linux/watchdog.h> | 19 | #include <linux/watchdog.h> |
22 | #include <linux/moduleparam.h> | 20 | #include <linux/moduleparam.h> |
23 | 21 | ||
@@ -29,22 +27,19 @@ struct moxart_wdt_dev { | |||
29 | struct watchdog_device dev; | 27 | struct watchdog_device dev; |
30 | void __iomem *base; | 28 | void __iomem *base; |
31 | unsigned int clock_frequency; | 29 | unsigned int clock_frequency; |
32 | struct notifier_block restart_handler; | ||
33 | }; | 30 | }; |
34 | 31 | ||
35 | static int heartbeat; | 32 | static int heartbeat; |
36 | 33 | ||
37 | static int moxart_restart_handle(struct notifier_block *this, | 34 | static int moxart_wdt_restart(struct watchdog_device *wdt_dev) |
38 | unsigned long mode, void *cmd) | ||
39 | { | 35 | { |
40 | struct moxart_wdt_dev *moxart_wdt = container_of(this, | 36 | struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev); |
41 | struct moxart_wdt_dev, | 37 | |
42 | restart_handler); | ||
43 | writel(1, moxart_wdt->base + REG_COUNT); | 38 | writel(1, moxart_wdt->base + REG_COUNT); |
44 | writel(0x5ab9, moxart_wdt->base + REG_MODE); | 39 | writel(0x5ab9, moxart_wdt->base + REG_MODE); |
45 | writel(0x03, moxart_wdt->base + REG_ENABLE); | 40 | writel(0x03, moxart_wdt->base + REG_ENABLE); |
46 | 41 | ||
47 | return NOTIFY_DONE; | 42 | return 0; |
48 | } | 43 | } |
49 | 44 | ||
50 | static int moxart_wdt_stop(struct watchdog_device *wdt_dev) | 45 | static int moxart_wdt_stop(struct watchdog_device *wdt_dev) |
@@ -87,6 +82,7 @@ static const struct watchdog_ops moxart_wdt_ops = { | |||
87 | .start = moxart_wdt_start, | 82 | .start = moxart_wdt_start, |
88 | .stop = moxart_wdt_stop, | 83 | .stop = moxart_wdt_stop, |
89 | .set_timeout = moxart_wdt_set_timeout, | 84 | .set_timeout = moxart_wdt_set_timeout, |
85 | .restart = moxart_wdt_restart, | ||
90 | }; | 86 | }; |
91 | 87 | ||
92 | static int moxart_wdt_probe(struct platform_device *pdev) | 88 | static int moxart_wdt_probe(struct platform_device *pdev) |
@@ -134,6 +130,7 @@ static int moxart_wdt_probe(struct platform_device *pdev) | |||
134 | 130 | ||
135 | watchdog_init_timeout(&moxart_wdt->dev, heartbeat, dev); | 131 | watchdog_init_timeout(&moxart_wdt->dev, heartbeat, dev); |
136 | watchdog_set_nowayout(&moxart_wdt->dev, nowayout); | 132 | watchdog_set_nowayout(&moxart_wdt->dev, nowayout); |
133 | watchdog_set_restart_priority(&moxart_wdt->dev, 128); | ||
137 | 134 | ||
138 | watchdog_set_drvdata(&moxart_wdt->dev, moxart_wdt); | 135 | watchdog_set_drvdata(&moxart_wdt->dev, moxart_wdt); |
139 | 136 | ||
@@ -141,13 +138,6 @@ static int moxart_wdt_probe(struct platform_device *pdev) | |||
141 | if (err) | 138 | if (err) |
142 | return err; | 139 | return err; |
143 | 140 | ||
144 | moxart_wdt->restart_handler.notifier_call = moxart_restart_handle; | ||
145 | moxart_wdt->restart_handler.priority = 128; | ||
146 | err = register_restart_handler(&moxart_wdt->restart_handler); | ||
147 | if (err) | ||
148 | dev_err(dev, "cannot register restart notifier (err=%d)\n", | ||
149 | err); | ||
150 | |||
151 | dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n", | 141 | dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n", |
152 | moxart_wdt->dev.timeout, nowayout); | 142 | moxart_wdt->dev.timeout, nowayout); |
153 | 143 | ||
@@ -158,7 +148,6 @@ static int moxart_wdt_remove(struct platform_device *pdev) | |||
158 | { | 148 | { |
159 | struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev); | 149 | struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev); |
160 | 150 | ||
161 | unregister_restart_handler(&moxart_wdt->restart_handler); | ||
162 | moxart_wdt_stop(&moxart_wdt->dev); | 151 | moxart_wdt_stop(&moxart_wdt->dev); |
163 | 152 | ||
164 | return 0; | 153 | return 0; |
diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c new file mode 100644 index 000000000000..4a2290f900a8 --- /dev/null +++ b/drivers/watchdog/mt7621_wdt.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * Ralink MT7621/MT7628 built-in hardware watchdog timer | ||
3 | * | ||
4 | * Copyright (C) 2014 John Crispin <blogic@openwrt.org> | ||
5 | * | ||
6 | * This driver was based on: drivers/watchdog/rt2880_wdt.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License version 2 as published | ||
10 | * by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/reset.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/watchdog.h> | ||
18 | #include <linux/moduleparam.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include <asm/mach-ralink/ralink_regs.h> | ||
22 | |||
23 | #define SYSC_RSTSTAT 0x38 | ||
24 | #define WDT_RST_CAUSE BIT(1) | ||
25 | |||
26 | #define RALINK_WDT_TIMEOUT 30 | ||
27 | |||
28 | #define TIMER_REG_TMRSTAT 0x00 | ||
29 | #define TIMER_REG_TMR1LOAD 0x24 | ||
30 | #define TIMER_REG_TMR1CTL 0x20 | ||
31 | |||
32 | #define TMR1CTL_ENABLE BIT(7) | ||
33 | #define TMR1CTL_RESTART BIT(9) | ||
34 | #define TMR1CTL_PRESCALE_SHIFT 16 | ||
35 | |||
36 | static void __iomem *mt7621_wdt_base; | ||
37 | static struct reset_control *mt7621_wdt_reset; | ||
38 | |||
39 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
40 | module_param(nowayout, bool, 0); | ||
41 | MODULE_PARM_DESC(nowayout, | ||
42 | "Watchdog cannot be stopped once started (default=" | ||
43 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
44 | |||
45 | static inline void rt_wdt_w32(unsigned reg, u32 val) | ||
46 | { | ||
47 | iowrite32(val, mt7621_wdt_base + reg); | ||
48 | } | ||
49 | |||
50 | static inline u32 rt_wdt_r32(unsigned reg) | ||
51 | { | ||
52 | return ioread32(mt7621_wdt_base + reg); | ||
53 | } | ||
54 | |||
55 | static int mt7621_wdt_ping(struct watchdog_device *w) | ||
56 | { | ||
57 | rt_wdt_w32(TIMER_REG_TMRSTAT, TMR1CTL_RESTART); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int mt7621_wdt_set_timeout(struct watchdog_device *w, unsigned int t) | ||
63 | { | ||
64 | w->timeout = t; | ||
65 | rt_wdt_w32(TIMER_REG_TMR1LOAD, t * 1000); | ||
66 | mt7621_wdt_ping(w); | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int mt7621_wdt_start(struct watchdog_device *w) | ||
72 | { | ||
73 | u32 t; | ||
74 | |||
75 | /* set the prescaler to 1ms == 1000us */ | ||
76 | rt_wdt_w32(TIMER_REG_TMR1CTL, 1000 << TMR1CTL_PRESCALE_SHIFT); | ||
77 | |||
78 | mt7621_wdt_set_timeout(w, w->timeout); | ||
79 | |||
80 | t = rt_wdt_r32(TIMER_REG_TMR1CTL); | ||
81 | t |= TMR1CTL_ENABLE; | ||
82 | rt_wdt_w32(TIMER_REG_TMR1CTL, t); | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int mt7621_wdt_stop(struct watchdog_device *w) | ||
88 | { | ||
89 | u32 t; | ||
90 | |||
91 | mt7621_wdt_ping(w); | ||
92 | |||
93 | t = rt_wdt_r32(TIMER_REG_TMR1CTL); | ||
94 | t &= ~TMR1CTL_ENABLE; | ||
95 | rt_wdt_w32(TIMER_REG_TMR1CTL, t); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int mt7621_wdt_bootcause(void) | ||
101 | { | ||
102 | if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) | ||
103 | return WDIOF_CARDRESET; | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static struct watchdog_info mt7621_wdt_info = { | ||
109 | .identity = "Mediatek Watchdog", | ||
110 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
111 | }; | ||
112 | |||
113 | static struct watchdog_ops mt7621_wdt_ops = { | ||
114 | .owner = THIS_MODULE, | ||
115 | .start = mt7621_wdt_start, | ||
116 | .stop = mt7621_wdt_stop, | ||
117 | .ping = mt7621_wdt_ping, | ||
118 | .set_timeout = mt7621_wdt_set_timeout, | ||
119 | }; | ||
120 | |||
121 | static struct watchdog_device mt7621_wdt_dev = { | ||
122 | .info = &mt7621_wdt_info, | ||
123 | .ops = &mt7621_wdt_ops, | ||
124 | .min_timeout = 1, | ||
125 | .max_timeout = 0xfffful / 1000, | ||
126 | }; | ||
127 | |||
128 | static int mt7621_wdt_probe(struct platform_device *pdev) | ||
129 | { | ||
130 | struct resource *res; | ||
131 | int ret; | ||
132 | |||
133 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
134 | mt7621_wdt_base = devm_ioremap_resource(&pdev->dev, res); | ||
135 | if (IS_ERR(mt7621_wdt_base)) | ||
136 | return PTR_ERR(mt7621_wdt_base); | ||
137 | |||
138 | mt7621_wdt_reset = devm_reset_control_get(&pdev->dev, NULL); | ||
139 | if (!IS_ERR(mt7621_wdt_reset)) | ||
140 | reset_control_deassert(mt7621_wdt_reset); | ||
141 | |||
142 | mt7621_wdt_dev.dev = &pdev->dev; | ||
143 | mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause(); | ||
144 | |||
145 | watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, | ||
146 | &pdev->dev); | ||
147 | watchdog_set_nowayout(&mt7621_wdt_dev, nowayout); | ||
148 | |||
149 | ret = watchdog_register_device(&mt7621_wdt_dev); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int mt7621_wdt_remove(struct platform_device *pdev) | ||
155 | { | ||
156 | watchdog_unregister_device(&mt7621_wdt_dev); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static void mt7621_wdt_shutdown(struct platform_device *pdev) | ||
162 | { | ||
163 | mt7621_wdt_stop(&mt7621_wdt_dev); | ||
164 | } | ||
165 | |||
166 | static const struct of_device_id mt7621_wdt_match[] = { | ||
167 | { .compatible = "mediatek,mt7621-wdt" }, | ||
168 | {}, | ||
169 | }; | ||
170 | MODULE_DEVICE_TABLE(of, mt7621_wdt_match); | ||
171 | |||
172 | static struct platform_driver mt7621_wdt_driver = { | ||
173 | .probe = mt7621_wdt_probe, | ||
174 | .remove = mt7621_wdt_remove, | ||
175 | .shutdown = mt7621_wdt_shutdown, | ||
176 | .driver = { | ||
177 | .name = KBUILD_MODNAME, | ||
178 | .of_match_table = mt7621_wdt_match, | ||
179 | }, | ||
180 | }; | ||
181 | |||
182 | module_platform_driver(mt7621_wdt_driver); | ||
183 | |||
184 | MODULE_DESCRIPTION("MediaTek MT762x hardware watchdog driver"); | ||
185 | MODULE_AUTHOR("John Crispin <blogic@openwrt.org"); | ||
186 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index b751f43d76ed..b78776c05554 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c | |||
@@ -28,8 +28,6 @@ | |||
28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <linux/types.h> | 29 | #include <linux/types.h> |
30 | #include <linux/watchdog.h> | 30 | #include <linux/watchdog.h> |
31 | #include <linux/notifier.h> | ||
32 | #include <linux/reboot.h> | ||
33 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
34 | 32 | ||
35 | #define WDT_MAX_TIMEOUT 31 | 33 | #define WDT_MAX_TIMEOUT 31 |
@@ -64,16 +62,13 @@ static unsigned int timeout = WDT_MAX_TIMEOUT; | |||
64 | struct mtk_wdt_dev { | 62 | struct mtk_wdt_dev { |
65 | struct watchdog_device wdt_dev; | 63 | struct watchdog_device wdt_dev; |
66 | void __iomem *wdt_base; | 64 | void __iomem *wdt_base; |
67 | struct notifier_block restart_handler; | ||
68 | }; | 65 | }; |
69 | 66 | ||
70 | static int mtk_reset_handler(struct notifier_block *this, unsigned long mode, | 67 | static int mtk_wdt_restart(struct watchdog_device *wdt_dev) |
71 | void *cmd) | ||
72 | { | 68 | { |
73 | struct mtk_wdt_dev *mtk_wdt; | 69 | struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); |
74 | void __iomem *wdt_base; | 70 | void __iomem *wdt_base; |
75 | 71 | ||
76 | mtk_wdt = container_of(this, struct mtk_wdt_dev, restart_handler); | ||
77 | wdt_base = mtk_wdt->wdt_base; | 72 | wdt_base = mtk_wdt->wdt_base; |
78 | 73 | ||
79 | while (1) { | 74 | while (1) { |
@@ -81,7 +76,7 @@ static int mtk_reset_handler(struct notifier_block *this, unsigned long mode, | |||
81 | mdelay(5); | 76 | mdelay(5); |
82 | } | 77 | } |
83 | 78 | ||
84 | return NOTIFY_DONE; | 79 | return 0; |
85 | } | 80 | } |
86 | 81 | ||
87 | static int mtk_wdt_ping(struct watchdog_device *wdt_dev) | 82 | static int mtk_wdt_ping(struct watchdog_device *wdt_dev) |
@@ -161,6 +156,7 @@ static const struct watchdog_ops mtk_wdt_ops = { | |||
161 | .stop = mtk_wdt_stop, | 156 | .stop = mtk_wdt_stop, |
162 | .ping = mtk_wdt_ping, | 157 | .ping = mtk_wdt_ping, |
163 | .set_timeout = mtk_wdt_set_timeout, | 158 | .set_timeout = mtk_wdt_set_timeout, |
159 | .restart = mtk_wdt_restart, | ||
164 | }; | 160 | }; |
165 | 161 | ||
166 | static int mtk_wdt_probe(struct platform_device *pdev) | 162 | static int mtk_wdt_probe(struct platform_device *pdev) |
@@ -189,6 +185,7 @@ static int mtk_wdt_probe(struct platform_device *pdev) | |||
189 | 185 | ||
190 | watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, &pdev->dev); | 186 | watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, &pdev->dev); |
191 | watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout); | 187 | watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout); |
188 | watchdog_set_restart_priority(&mtk_wdt->wdt_dev, 128); | ||
192 | 189 | ||
193 | watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); | 190 | watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); |
194 | 191 | ||
@@ -198,13 +195,6 @@ static int mtk_wdt_probe(struct platform_device *pdev) | |||
198 | if (unlikely(err)) | 195 | if (unlikely(err)) |
199 | return err; | 196 | return err; |
200 | 197 | ||
201 | mtk_wdt->restart_handler.notifier_call = mtk_reset_handler; | ||
202 | mtk_wdt->restart_handler.priority = 128; | ||
203 | err = register_restart_handler(&mtk_wdt->restart_handler); | ||
204 | if (err) | ||
205 | dev_warn(&pdev->dev, | ||
206 | "cannot register restart handler (err=%d)\n", err); | ||
207 | |||
208 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", | 198 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", |
209 | mtk_wdt->wdt_dev.timeout, nowayout); | 199 | mtk_wdt->wdt_dev.timeout, nowayout); |
210 | 200 | ||
@@ -223,8 +213,6 @@ static int mtk_wdt_remove(struct platform_device *pdev) | |||
223 | { | 213 | { |
224 | struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); | 214 | struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); |
225 | 215 | ||
226 | unregister_restart_handler(&mtk_wdt->restart_handler); | ||
227 | |||
228 | watchdog_unregister_device(&mtk_wdt->wdt_dev); | 216 | watchdog_unregister_device(&mtk_wdt->wdt_dev); |
229 | 217 | ||
230 | return 0; | 218 | return 0; |
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 6f17c935a6cf..1b02bfa81b29 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
@@ -271,7 +271,8 @@ static int omap_wdt_probe(struct platform_device *pdev) | |||
271 | wdev->wdog.bootstatus = WDIOF_CARDRESET; | 271 | wdev->wdog.bootstatus = WDIOF_CARDRESET; |
272 | } | 272 | } |
273 | 273 | ||
274 | omap_wdt_disable(wdev); | 274 | if (!early_enable) |
275 | omap_wdt_disable(wdev); | ||
275 | 276 | ||
276 | ret = watchdog_register_device(&wdev->wdog); | 277 | ret = watchdog_register_device(&wdev->wdog); |
277 | if (ret) { | 278 | if (ret) { |
@@ -283,11 +284,11 @@ static int omap_wdt_probe(struct platform_device *pdev) | |||
283 | readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, | 284 | readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, |
284 | wdev->wdog.timeout); | 285 | wdev->wdog.timeout); |
285 | 286 | ||
286 | pm_runtime_put_sync(wdev->dev); | ||
287 | |||
288 | if (early_enable) | 287 | if (early_enable) |
289 | omap_wdt_start(&wdev->wdog); | 288 | omap_wdt_start(&wdev->wdog); |
290 | 289 | ||
290 | pm_runtime_put(wdev->dev); | ||
291 | |||
291 | return 0; | 292 | return 0; |
292 | } | 293 | } |
293 | 294 | ||
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 773dcfaee7b2..424f9a952fee 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c | |||
@@ -17,7 +17,6 @@ | |||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/of.h> | 18 | #include <linux/of.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/reboot.h> | ||
21 | #include <linux/watchdog.h> | 20 | #include <linux/watchdog.h> |
22 | 21 | ||
23 | #define WDT_RST 0x38 | 22 | #define WDT_RST 0x38 |
@@ -28,7 +27,6 @@ struct qcom_wdt { | |||
28 | struct watchdog_device wdd; | 27 | struct watchdog_device wdd; |
29 | struct clk *clk; | 28 | struct clk *clk; |
30 | unsigned long rate; | 29 | unsigned long rate; |
31 | struct notifier_block restart_nb; | ||
32 | void __iomem *base; | 30 | void __iomem *base; |
33 | }; | 31 | }; |
34 | 32 | ||
@@ -72,25 +70,9 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd, | |||
72 | return qcom_wdt_start(wdd); | 70 | return qcom_wdt_start(wdd); |
73 | } | 71 | } |
74 | 72 | ||
75 | static const struct watchdog_ops qcom_wdt_ops = { | 73 | static int qcom_wdt_restart(struct watchdog_device *wdd) |
76 | .start = qcom_wdt_start, | ||
77 | .stop = qcom_wdt_stop, | ||
78 | .ping = qcom_wdt_ping, | ||
79 | .set_timeout = qcom_wdt_set_timeout, | ||
80 | .owner = THIS_MODULE, | ||
81 | }; | ||
82 | |||
83 | static const struct watchdog_info qcom_wdt_info = { | ||
84 | .options = WDIOF_KEEPALIVEPING | ||
85 | | WDIOF_MAGICCLOSE | ||
86 | | WDIOF_SETTIMEOUT, | ||
87 | .identity = KBUILD_MODNAME, | ||
88 | }; | ||
89 | |||
90 | static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, | ||
91 | void *data) | ||
92 | { | 74 | { |
93 | struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb); | 75 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); |
94 | u32 timeout; | 76 | u32 timeout; |
95 | 77 | ||
96 | /* | 78 | /* |
@@ -110,9 +92,25 @@ static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, | |||
110 | wmb(); | 92 | wmb(); |
111 | 93 | ||
112 | msleep(150); | 94 | msleep(150); |
113 | return NOTIFY_DONE; | 95 | return 0; |
114 | } | 96 | } |
115 | 97 | ||
98 | static const struct watchdog_ops qcom_wdt_ops = { | ||
99 | .start = qcom_wdt_start, | ||
100 | .stop = qcom_wdt_stop, | ||
101 | .ping = qcom_wdt_ping, | ||
102 | .set_timeout = qcom_wdt_set_timeout, | ||
103 | .restart = qcom_wdt_restart, | ||
104 | .owner = THIS_MODULE, | ||
105 | }; | ||
106 | |||
107 | static const struct watchdog_info qcom_wdt_info = { | ||
108 | .options = WDIOF_KEEPALIVEPING | ||
109 | | WDIOF_MAGICCLOSE | ||
110 | | WDIOF_SETTIMEOUT, | ||
111 | .identity = KBUILD_MODNAME, | ||
112 | }; | ||
113 | |||
116 | static int qcom_wdt_probe(struct platform_device *pdev) | 114 | static int qcom_wdt_probe(struct platform_device *pdev) |
117 | { | 115 | { |
118 | struct qcom_wdt *wdt; | 116 | struct qcom_wdt *wdt; |
@@ -166,7 +164,6 @@ static int qcom_wdt_probe(struct platform_device *pdev) | |||
166 | goto err_clk_unprepare; | 164 | goto err_clk_unprepare; |
167 | } | 165 | } |
168 | 166 | ||
169 | wdt->wdd.dev = &pdev->dev; | ||
170 | wdt->wdd.info = &qcom_wdt_info; | 167 | wdt->wdd.info = &qcom_wdt_info; |
171 | wdt->wdd.ops = &qcom_wdt_ops; | 168 | wdt->wdd.ops = &qcom_wdt_ops; |
172 | wdt->wdd.min_timeout = 1; | 169 | wdt->wdd.min_timeout = 1; |
@@ -187,14 +184,6 @@ static int qcom_wdt_probe(struct platform_device *pdev) | |||
187 | goto err_clk_unprepare; | 184 | goto err_clk_unprepare; |
188 | } | 185 | } |
189 | 186 | ||
190 | /* | ||
191 | * WDT restart notifier has priority 0 (use as a last resort) | ||
192 | */ | ||
193 | wdt->restart_nb.notifier_call = qcom_wdt_restart; | ||
194 | ret = register_restart_handler(&wdt->restart_nb); | ||
195 | if (ret) | ||
196 | dev_err(&pdev->dev, "failed to setup restart handler\n"); | ||
197 | |||
198 | platform_set_drvdata(pdev, wdt); | 187 | platform_set_drvdata(pdev, wdt); |
199 | return 0; | 188 | return 0; |
200 | 189 | ||
@@ -207,7 +196,6 @@ static int qcom_wdt_remove(struct platform_device *pdev) | |||
207 | { | 196 | { |
208 | struct qcom_wdt *wdt = platform_get_drvdata(pdev); | 197 | struct qcom_wdt *wdt = platform_get_drvdata(pdev); |
209 | 198 | ||
210 | unregister_restart_handler(&wdt->restart_nb); | ||
211 | watchdog_unregister_device(&wdt->wdd); | 199 | watchdog_unregister_device(&wdt->wdd); |
212 | clk_disable_unprepare(wdt->clk); | 200 | clk_disable_unprepare(wdt->clk); |
213 | return 0; | 201 | return 0; |
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index d781000c7825..0093450441fe 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -41,7 +41,6 @@ | |||
41 | #include <linux/of.h> | 41 | #include <linux/of.h> |
42 | #include <linux/mfd/syscon.h> | 42 | #include <linux/mfd/syscon.h> |
43 | #include <linux/regmap.h> | 43 | #include <linux/regmap.h> |
44 | #include <linux/reboot.h> | ||
45 | #include <linux/delay.h> | 44 | #include <linux/delay.h> |
46 | 45 | ||
47 | #define S3C2410_WTCON 0x00 | 46 | #define S3C2410_WTCON 0x00 |
@@ -130,7 +129,6 @@ struct s3c2410_wdt { | |||
130 | unsigned long wtdat_save; | 129 | unsigned long wtdat_save; |
131 | struct watchdog_device wdt_device; | 130 | struct watchdog_device wdt_device; |
132 | struct notifier_block freq_transition; | 131 | struct notifier_block freq_transition; |
133 | struct notifier_block restart_handler; | ||
134 | struct s3c2410_wdt_variant *drv_data; | 132 | struct s3c2410_wdt_variant *drv_data; |
135 | struct regmap *pmureg; | 133 | struct regmap *pmureg; |
136 | }; | 134 | }; |
@@ -351,6 +349,29 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou | |||
351 | return 0; | 349 | return 0; |
352 | } | 350 | } |
353 | 351 | ||
352 | static int s3c2410wdt_restart(struct watchdog_device *wdd) | ||
353 | { | ||
354 | struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); | ||
355 | void __iomem *wdt_base = wdt->reg_base; | ||
356 | |||
357 | /* disable watchdog, to be safe */ | ||
358 | writel(0, wdt_base + S3C2410_WTCON); | ||
359 | |||
360 | /* put initial values into count and data */ | ||
361 | writel(0x80, wdt_base + S3C2410_WTCNT); | ||
362 | writel(0x80, wdt_base + S3C2410_WTDAT); | ||
363 | |||
364 | /* set the watchdog to go and reset... */ | ||
365 | writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 | | ||
366 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20), | ||
367 | wdt_base + S3C2410_WTCON); | ||
368 | |||
369 | /* wait for reset to assert... */ | ||
370 | mdelay(500); | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
354 | #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) | 375 | #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) |
355 | 376 | ||
356 | static const struct watchdog_info s3c2410_wdt_ident = { | 377 | static const struct watchdog_info s3c2410_wdt_ident = { |
@@ -365,6 +386,7 @@ static struct watchdog_ops s3c2410wdt_ops = { | |||
365 | .stop = s3c2410wdt_stop, | 386 | .stop = s3c2410wdt_stop, |
366 | .ping = s3c2410wdt_keepalive, | 387 | .ping = s3c2410wdt_keepalive, |
367 | .set_timeout = s3c2410wdt_set_heartbeat, | 388 | .set_timeout = s3c2410wdt_set_heartbeat, |
389 | .restart = s3c2410wdt_restart, | ||
368 | }; | 390 | }; |
369 | 391 | ||
370 | static struct watchdog_device s3c2410_wdd = { | 392 | static struct watchdog_device s3c2410_wdd = { |
@@ -452,31 +474,6 @@ static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) | |||
452 | } | 474 | } |
453 | #endif | 475 | #endif |
454 | 476 | ||
455 | static int s3c2410wdt_restart(struct notifier_block *this, | ||
456 | unsigned long mode, void *cmd) | ||
457 | { | ||
458 | struct s3c2410_wdt *wdt = container_of(this, struct s3c2410_wdt, | ||
459 | restart_handler); | ||
460 | void __iomem *wdt_base = wdt->reg_base; | ||
461 | |||
462 | /* disable watchdog, to be safe */ | ||
463 | writel(0, wdt_base + S3C2410_WTCON); | ||
464 | |||
465 | /* put initial values into count and data */ | ||
466 | writel(0x80, wdt_base + S3C2410_WTCNT); | ||
467 | writel(0x80, wdt_base + S3C2410_WTDAT); | ||
468 | |||
469 | /* set the watchdog to go and reset... */ | ||
470 | writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 | | ||
471 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20), | ||
472 | wdt_base + S3C2410_WTCON); | ||
473 | |||
474 | /* wait for reset to assert... */ | ||
475 | mdelay(500); | ||
476 | |||
477 | return NOTIFY_DONE; | ||
478 | } | ||
479 | |||
480 | static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) | 477 | static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) |
481 | { | 478 | { |
482 | unsigned int rst_stat; | 479 | unsigned int rst_stat; |
@@ -605,6 +602,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
605 | } | 602 | } |
606 | 603 | ||
607 | watchdog_set_nowayout(&wdt->wdt_device, nowayout); | 604 | watchdog_set_nowayout(&wdt->wdt_device, nowayout); |
605 | watchdog_set_restart_priority(&wdt->wdt_device, 128); | ||
608 | 606 | ||
609 | wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); | 607 | wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); |
610 | wdt->wdt_device.parent = &pdev->dev; | 608 | wdt->wdt_device.parent = &pdev->dev; |
@@ -632,12 +630,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
632 | 630 | ||
633 | platform_set_drvdata(pdev, wdt); | 631 | platform_set_drvdata(pdev, wdt); |
634 | 632 | ||
635 | wdt->restart_handler.notifier_call = s3c2410wdt_restart; | ||
636 | wdt->restart_handler.priority = 128; | ||
637 | ret = register_restart_handler(&wdt->restart_handler); | ||
638 | if (ret) | ||
639 | pr_err("cannot register restart handler, %d\n", ret); | ||
640 | |||
641 | /* print out a statement of readiness */ | 633 | /* print out a statement of readiness */ |
642 | 634 | ||
643 | wtcon = readl(wdt->reg_base + S3C2410_WTCON); | 635 | wtcon = readl(wdt->reg_base + S3C2410_WTCON); |
@@ -667,8 +659,6 @@ static int s3c2410wdt_remove(struct platform_device *dev) | |||
667 | int ret; | 659 | int ret; |
668 | struct s3c2410_wdt *wdt = platform_get_drvdata(dev); | 660 | struct s3c2410_wdt *wdt = platform_get_drvdata(dev); |
669 | 661 | ||
670 | unregister_restart_handler(&wdt->restart_handler); | ||
671 | |||
672 | ret = s3c2410wdt_mask_and_disable_reset(wdt, true); | 662 | ret = s3c2410wdt_mask_and_disable_reset(wdt, true); |
673 | if (ret < 0) | 663 | if (ret < 0) |
674 | return ret; | 664 | return ret; |
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 0dc5e323d59d..99a06f9e3930 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c | |||
@@ -43,7 +43,6 @@ | |||
43 | #include <linux/types.h> | 43 | #include <linux/types.h> |
44 | #include <linux/timer.h> | 44 | #include <linux/timer.h> |
45 | #include <linux/watchdog.h> | 45 | #include <linux/watchdog.h> |
46 | #include <linux/notifier.h> | ||
47 | #include <linux/reboot.h> | 46 | #include <linux/reboot.h> |
48 | #include <linux/init.h> | 47 | #include <linux/init.h> |
49 | #include <linux/jiffies.h> | 48 | #include <linux/jiffies.h> |
@@ -87,6 +86,7 @@ static struct timer_list watchdog_ticktock = | |||
87 | 86 | ||
88 | static void watchdog_fire(unsigned long data) | 87 | static void watchdog_fire(unsigned long data) |
89 | { | 88 | { |
89 | module_put(THIS_MODULE); | ||
90 | if (soft_noboot) | 90 | if (soft_noboot) |
91 | pr_crit("Triggered - Reboot ignored\n"); | 91 | pr_crit("Triggered - Reboot ignored\n"); |
92 | else if (soft_panic) { | 92 | else if (soft_panic) { |
@@ -105,13 +105,16 @@ static void watchdog_fire(unsigned long data) | |||
105 | 105 | ||
106 | static int softdog_ping(struct watchdog_device *w) | 106 | static int softdog_ping(struct watchdog_device *w) |
107 | { | 107 | { |
108 | mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ)); | 108 | if (!mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ))) |
109 | __module_get(THIS_MODULE); | ||
109 | return 0; | 110 | return 0; |
110 | } | 111 | } |
111 | 112 | ||
112 | static int softdog_stop(struct watchdog_device *w) | 113 | static int softdog_stop(struct watchdog_device *w) |
113 | { | 114 | { |
114 | del_timer(&watchdog_ticktock); | 115 | if (del_timer(&watchdog_ticktock)) |
116 | module_put(THIS_MODULE); | ||
117 | |||
115 | return 0; | 118 | return 0; |
116 | } | 119 | } |
117 | 120 | ||
@@ -122,26 +125,9 @@ static int softdog_set_timeout(struct watchdog_device *w, unsigned int t) | |||
122 | } | 125 | } |
123 | 126 | ||
124 | /* | 127 | /* |
125 | * Notifier for system down | ||
126 | */ | ||
127 | |||
128 | static int softdog_notify_sys(struct notifier_block *this, unsigned long code, | ||
129 | void *unused) | ||
130 | { | ||
131 | if (code == SYS_DOWN || code == SYS_HALT) | ||
132 | /* Turn the WDT off */ | ||
133 | softdog_stop(NULL); | ||
134 | return NOTIFY_DONE; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * Kernel Interfaces | 128 | * Kernel Interfaces |
139 | */ | 129 | */ |
140 | 130 | ||
141 | static struct notifier_block softdog_notifier = { | ||
142 | .notifier_call = softdog_notify_sys, | ||
143 | }; | ||
144 | |||
145 | static struct watchdog_info softdog_info = { | 131 | static struct watchdog_info softdog_info = { |
146 | .identity = "Software Watchdog", | 132 | .identity = "Software Watchdog", |
147 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | 133 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
@@ -175,18 +161,11 @@ static int __init watchdog_init(void) | |||
175 | softdog_dev.timeout = soft_margin; | 161 | softdog_dev.timeout = soft_margin; |
176 | 162 | ||
177 | watchdog_set_nowayout(&softdog_dev, nowayout); | 163 | watchdog_set_nowayout(&softdog_dev, nowayout); |
178 | 164 | watchdog_stop_on_reboot(&softdog_dev); | |
179 | ret = register_reboot_notifier(&softdog_notifier); | ||
180 | if (ret) { | ||
181 | pr_err("cannot register reboot notifier (err=%d)\n", ret); | ||
182 | return ret; | ||
183 | } | ||
184 | 165 | ||
185 | ret = watchdog_register_device(&softdog_dev); | 166 | ret = watchdog_register_device(&softdog_dev); |
186 | if (ret) { | 167 | if (ret) |
187 | unregister_reboot_notifier(&softdog_notifier); | ||
188 | return ret; | 168 | return ret; |
189 | } | ||
190 | 169 | ||
191 | pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", | 170 | pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", |
192 | soft_noboot, soft_margin, soft_panic, nowayout); | 171 | soft_noboot, soft_margin, soft_panic, nowayout); |
@@ -197,7 +176,6 @@ static int __init watchdog_init(void) | |||
197 | static void __exit watchdog_exit(void) | 176 | static void __exit watchdog_exit(void) |
198 | { | 177 | { |
199 | watchdog_unregister_device(&softdog_dev); | 178 | watchdog_unregister_device(&softdog_dev); |
200 | unregister_reboot_notifier(&softdog_notifier); | ||
201 | } | 179 | } |
202 | 180 | ||
203 | module_init(watchdog_init); | 181 | module_init(watchdog_init); |
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index eb8044ef0ea0..6467b91f2245 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c | |||
@@ -306,6 +306,10 @@ static struct miscdevice sp5100_tco_miscdev = { | |||
306 | static const struct pci_device_id sp5100_tco_pci_tbl[] = { | 306 | static const struct pci_device_id sp5100_tco_pci_tbl[] = { |
307 | { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, | 307 | { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, |
308 | PCI_ANY_ID, }, | 308 | PCI_ANY_ID, }, |
309 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, | ||
310 | PCI_ANY_ID, }, | ||
311 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, | ||
312 | PCI_ANY_ID, }, | ||
309 | { 0, }, /* End of list */ | 313 | { 0, }, /* End of list */ |
310 | }; | 314 | }; |
311 | MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); | 315 | MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); |
@@ -331,21 +335,24 @@ static unsigned char sp5100_tco_setupdevice(void) | |||
331 | if (!sp5100_tco_pci) | 335 | if (!sp5100_tco_pci) |
332 | return 0; | 336 | return 0; |
333 | 337 | ||
334 | pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision); | 338 | pr_info("PCI Vendor ID: 0x%x, Device ID: 0x%x, Revision ID: 0x%x\n", |
339 | sp5100_tco_pci->vendor, sp5100_tco_pci->device, | ||
340 | sp5100_tco_pci->revision); | ||
335 | 341 | ||
336 | /* | 342 | /* |
337 | * Determine type of southbridge chipset. | 343 | * Determine type of southbridge chipset. |
338 | */ | 344 | */ |
339 | if (sp5100_tco_pci->revision >= 0x40) { | 345 | if (sp5100_tco_pci->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && |
340 | dev_name = SB800_DEVNAME; | 346 | sp5100_tco_pci->revision < 0x40) { |
341 | index_reg = SB800_IO_PM_INDEX_REG; | ||
342 | data_reg = SB800_IO_PM_DATA_REG; | ||
343 | base_addr = SB800_PM_WATCHDOG_BASE; | ||
344 | } else { | ||
345 | dev_name = SP5100_DEVNAME; | 347 | dev_name = SP5100_DEVNAME; |
346 | index_reg = SP5100_IO_PM_INDEX_REG; | 348 | index_reg = SP5100_IO_PM_INDEX_REG; |
347 | data_reg = SP5100_IO_PM_DATA_REG; | 349 | data_reg = SP5100_IO_PM_DATA_REG; |
348 | base_addr = SP5100_PM_WATCHDOG_BASE; | 350 | base_addr = SP5100_PM_WATCHDOG_BASE; |
351 | } else { | ||
352 | dev_name = SB800_DEVNAME; | ||
353 | index_reg = SB800_IO_PM_INDEX_REG; | ||
354 | data_reg = SB800_IO_PM_DATA_REG; | ||
355 | base_addr = SB800_PM_WATCHDOG_BASE; | ||
349 | } | 356 | } |
350 | 357 | ||
351 | /* Request the IO ports used by this driver */ | 358 | /* Request the IO ports used by this driver */ |
@@ -381,7 +388,12 @@ static unsigned char sp5100_tco_setupdevice(void) | |||
381 | * Secondly, Find the watchdog timer MMIO address | 388 | * Secondly, Find the watchdog timer MMIO address |
382 | * from SBResource_MMIO register. | 389 | * from SBResource_MMIO register. |
383 | */ | 390 | */ |
384 | if (sp5100_tco_pci->revision >= 0x40) { | 391 | if (sp5100_tco_pci->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && |
392 | sp5100_tco_pci->revision < 0x40) { | ||
393 | /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ | ||
394 | pci_read_config_dword(sp5100_tco_pci, | ||
395 | SP5100_SB_RESOURCE_MMIO_BASE, &val); | ||
396 | } else { | ||
385 | /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ | 397 | /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ |
386 | outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG); | 398 | outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG); |
387 | val = inb(SB800_IO_PM_DATA_REG); | 399 | val = inb(SB800_IO_PM_DATA_REG); |
@@ -391,10 +403,6 @@ static unsigned char sp5100_tco_setupdevice(void) | |||
391 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); | 403 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); |
392 | outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG); | 404 | outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG); |
393 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); | 405 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); |
394 | } else { | ||
395 | /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ | ||
396 | pci_read_config_dword(sp5100_tco_pci, | ||
397 | SP5100_SB_RESOURCE_MMIO_BASE, &val); | ||
398 | } | 406 | } |
399 | 407 | ||
400 | /* The SBResource_MMIO is enabled and mapped memory space? */ | 408 | /* The SBResource_MMIO is enabled and mapped memory space? */ |
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index 3ee6128a540e..d8b11eb269ad 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c | |||
@@ -14,6 +14,8 @@ | |||
14 | #include <linux/watchdog.h> | 14 | #include <linux/watchdog.h> |
15 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
16 | #include <linux/stmp3xxx_rtc_wdt.h> | 16 | #include <linux/stmp3xxx_rtc_wdt.h> |
17 | #include <linux/notifier.h> | ||
18 | #include <linux/reboot.h> | ||
17 | 19 | ||
18 | #define WDOG_TICK_RATE 1000 /* 1 kHz clock */ | 20 | #define WDOG_TICK_RATE 1000 /* 1 kHz clock */ |
19 | #define STMP3XXX_DEFAULT_TIMEOUT 19 | 21 | #define STMP3XXX_DEFAULT_TIMEOUT 19 |
@@ -69,6 +71,25 @@ static struct watchdog_device stmp3xxx_wdd = { | |||
69 | .status = WATCHDOG_NOWAYOUT_INIT_STATUS, | 71 | .status = WATCHDOG_NOWAYOUT_INIT_STATUS, |
70 | }; | 72 | }; |
71 | 73 | ||
74 | static int wdt_notify_sys(struct notifier_block *nb, unsigned long code, | ||
75 | void *unused) | ||
76 | { | ||
77 | switch (code) { | ||
78 | case SYS_DOWN: /* keep enabled, system might crash while going down */ | ||
79 | break; | ||
80 | case SYS_HALT: /* allow the system to actually halt */ | ||
81 | case SYS_POWER_OFF: | ||
82 | wdt_stop(&stmp3xxx_wdd); | ||
83 | break; | ||
84 | } | ||
85 | |||
86 | return NOTIFY_DONE; | ||
87 | } | ||
88 | |||
89 | static struct notifier_block wdt_notifier = { | ||
90 | .notifier_call = wdt_notify_sys, | ||
91 | }; | ||
92 | |||
72 | static int stmp3xxx_wdt_probe(struct platform_device *pdev) | 93 | static int stmp3xxx_wdt_probe(struct platform_device *pdev) |
73 | { | 94 | { |
74 | int ret; | 95 | int ret; |
@@ -84,6 +105,9 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev) | |||
84 | return ret; | 105 | return ret; |
85 | } | 106 | } |
86 | 107 | ||
108 | if (register_reboot_notifier(&wdt_notifier)) | ||
109 | dev_warn(&pdev->dev, "cannot register reboot notifier\n"); | ||
110 | |||
87 | dev_info(&pdev->dev, "initialized watchdog with heartbeat %ds\n", | 111 | dev_info(&pdev->dev, "initialized watchdog with heartbeat %ds\n", |
88 | stmp3xxx_wdd.timeout); | 112 | stmp3xxx_wdd.timeout); |
89 | return 0; | 113 | return 0; |
@@ -91,6 +115,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev) | |||
91 | 115 | ||
92 | static int stmp3xxx_wdt_remove(struct platform_device *pdev) | 116 | static int stmp3xxx_wdt_remove(struct platform_device *pdev) |
93 | { | 117 | { |
118 | unregister_reboot_notifier(&wdt_notifier); | ||
94 | watchdog_unregister_device(&stmp3xxx_wdd); | 119 | watchdog_unregister_device(&stmp3xxx_wdd); |
95 | return 0; | 120 | return 0; |
96 | } | 121 | } |
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 47bd8a14d01f..e027deb54740 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c | |||
@@ -21,11 +21,9 @@ | |||
21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/moduleparam.h> | 23 | #include <linux/moduleparam.h> |
24 | #include <linux/notifier.h> | ||
25 | #include <linux/of.h> | 24 | #include <linux/of.h> |
26 | #include <linux/of_device.h> | 25 | #include <linux/of_device.h> |
27 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
28 | #include <linux/reboot.h> | ||
29 | #include <linux/types.h> | 27 | #include <linux/types.h> |
30 | #include <linux/watchdog.h> | 28 | #include <linux/watchdog.h> |
31 | 29 | ||
@@ -60,7 +58,6 @@ struct sunxi_wdt_dev { | |||
60 | struct watchdog_device wdt_dev; | 58 | struct watchdog_device wdt_dev; |
61 | void __iomem *wdt_base; | 59 | void __iomem *wdt_base; |
62 | const struct sunxi_wdt_reg *wdt_regs; | 60 | const struct sunxi_wdt_reg *wdt_regs; |
63 | struct notifier_block restart_handler; | ||
64 | }; | 61 | }; |
65 | 62 | ||
66 | /* | 63 | /* |
@@ -86,12 +83,9 @@ static const int wdt_timeout_map[] = { | |||
86 | }; | 83 | }; |
87 | 84 | ||
88 | 85 | ||
89 | static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, | 86 | static int sunxi_wdt_restart(struct watchdog_device *wdt_dev) |
90 | void *cmd) | ||
91 | { | 87 | { |
92 | struct sunxi_wdt_dev *sunxi_wdt = container_of(this, | 88 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
93 | struct sunxi_wdt_dev, | ||
94 | restart_handler); | ||
95 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 89 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
96 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | 90 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; |
97 | u32 val; | 91 | u32 val; |
@@ -120,7 +114,7 @@ static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, | |||
120 | val |= WDT_MODE_EN; | 114 | val |= WDT_MODE_EN; |
121 | writel(val, wdt_base + regs->wdt_mode); | 115 | writel(val, wdt_base + regs->wdt_mode); |
122 | } | 116 | } |
123 | return NOTIFY_DONE; | 117 | return 0; |
124 | } | 118 | } |
125 | 119 | ||
126 | static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) | 120 | static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) |
@@ -208,6 +202,7 @@ static const struct watchdog_ops sunxi_wdt_ops = { | |||
208 | .stop = sunxi_wdt_stop, | 202 | .stop = sunxi_wdt_stop, |
209 | .ping = sunxi_wdt_ping, | 203 | .ping = sunxi_wdt_ping, |
210 | .set_timeout = sunxi_wdt_set_timeout, | 204 | .set_timeout = sunxi_wdt_set_timeout, |
205 | .restart = sunxi_wdt_restart, | ||
211 | }; | 206 | }; |
212 | 207 | ||
213 | static const struct sunxi_wdt_reg sun4i_wdt_reg = { | 208 | static const struct sunxi_wdt_reg sun4i_wdt_reg = { |
@@ -268,6 +263,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev) | |||
268 | 263 | ||
269 | watchdog_init_timeout(&sunxi_wdt->wdt_dev, timeout, &pdev->dev); | 264 | watchdog_init_timeout(&sunxi_wdt->wdt_dev, timeout, &pdev->dev); |
270 | watchdog_set_nowayout(&sunxi_wdt->wdt_dev, nowayout); | 265 | watchdog_set_nowayout(&sunxi_wdt->wdt_dev, nowayout); |
266 | watchdog_set_restart_priority(&sunxi_wdt->wdt_dev, 128); | ||
271 | 267 | ||
272 | watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt); | 268 | watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt); |
273 | 269 | ||
@@ -277,13 +273,6 @@ static int sunxi_wdt_probe(struct platform_device *pdev) | |||
277 | if (unlikely(err)) | 273 | if (unlikely(err)) |
278 | return err; | 274 | return err; |
279 | 275 | ||
280 | sunxi_wdt->restart_handler.notifier_call = sunxi_restart_handle; | ||
281 | sunxi_wdt->restart_handler.priority = 128; | ||
282 | err = register_restart_handler(&sunxi_wdt->restart_handler); | ||
283 | if (err) | ||
284 | dev_err(&pdev->dev, | ||
285 | "cannot register restart handler (err=%d)\n", err); | ||
286 | |||
287 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", | 276 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", |
288 | sunxi_wdt->wdt_dev.timeout, nowayout); | 277 | sunxi_wdt->wdt_dev.timeout, nowayout); |
289 | 278 | ||
@@ -294,8 +283,6 @@ static int sunxi_wdt_remove(struct platform_device *pdev) | |||
294 | { | 283 | { |
295 | struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); | 284 | struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); |
296 | 285 | ||
297 | unregister_restart_handler(&sunxi_wdt->restart_handler); | ||
298 | |||
299 | watchdog_unregister_device(&sunxi_wdt->wdt_dev); | 286 | watchdog_unregister_device(&sunxi_wdt->wdt_dev); |
300 | watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); | 287 | watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); |
301 | 288 | ||
diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c new file mode 100644 index 000000000000..709c1ed6fd79 --- /dev/null +++ b/drivers/watchdog/tangox_wdt.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Mans Rullgard <mans@mansr.com> | ||
3 | * SMP86xx/SMP87xx Watchdog driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/bitops.h> | ||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/notifier.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/reboot.h> | ||
21 | #include <linux/watchdog.h> | ||
22 | |||
23 | #define DEFAULT_TIMEOUT 30 | ||
24 | |||
25 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
26 | module_param(nowayout, bool, 0); | ||
27 | MODULE_PARM_DESC(nowayout, | ||
28 | "Watchdog cannot be stopped once started (default=" | ||
29 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
30 | |||
31 | static unsigned int timeout; | ||
32 | module_param(timeout, int, 0); | ||
33 | MODULE_PARM_DESC(timeout, "Watchdog timeout"); | ||
34 | |||
35 | /* | ||
36 | * Counter counts down from programmed value. Reset asserts when | ||
37 | * the counter reaches 1. | ||
38 | */ | ||
39 | #define WD_COUNTER 0 | ||
40 | |||
41 | #define WD_CONFIG 4 | ||
42 | #define WD_CONFIG_XTAL_IN BIT(0) | ||
43 | #define WD_CONFIG_DISABLE BIT(31) | ||
44 | |||
45 | struct tangox_wdt_device { | ||
46 | struct watchdog_device wdt; | ||
47 | void __iomem *base; | ||
48 | unsigned long clk_rate; | ||
49 | struct clk *clk; | ||
50 | struct notifier_block restart; | ||
51 | }; | ||
52 | |||
53 | static int tangox_wdt_set_timeout(struct watchdog_device *wdt, | ||
54 | unsigned int new_timeout) | ||
55 | { | ||
56 | wdt->timeout = new_timeout; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int tangox_wdt_start(struct watchdog_device *wdt) | ||
62 | { | ||
63 | struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); | ||
64 | u32 ticks; | ||
65 | |||
66 | ticks = 1 + wdt->timeout * dev->clk_rate; | ||
67 | writel(ticks, dev->base + WD_COUNTER); | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int tangox_wdt_stop(struct watchdog_device *wdt) | ||
73 | { | ||
74 | struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); | ||
75 | |||
76 | writel(0, dev->base + WD_COUNTER); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static unsigned int tangox_wdt_get_timeleft(struct watchdog_device *wdt) | ||
82 | { | ||
83 | struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); | ||
84 | u32 count; | ||
85 | |||
86 | count = readl(dev->base + WD_COUNTER); | ||
87 | |||
88 | if (!count) | ||
89 | return 0; | ||
90 | |||
91 | return (count - 1) / dev->clk_rate; | ||
92 | } | ||
93 | |||
94 | static const struct watchdog_info tangox_wdt_info = { | ||
95 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
96 | .identity = "tangox watchdog", | ||
97 | }; | ||
98 | |||
99 | static const struct watchdog_ops tangox_wdt_ops = { | ||
100 | .start = tangox_wdt_start, | ||
101 | .stop = tangox_wdt_stop, | ||
102 | .set_timeout = tangox_wdt_set_timeout, | ||
103 | .get_timeleft = tangox_wdt_get_timeleft, | ||
104 | }; | ||
105 | |||
106 | static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action, | ||
107 | void *data) | ||
108 | { | ||
109 | struct tangox_wdt_device *dev = | ||
110 | container_of(nb, struct tangox_wdt_device, restart); | ||
111 | |||
112 | writel(1, dev->base + WD_COUNTER); | ||
113 | |||
114 | return NOTIFY_DONE; | ||
115 | } | ||
116 | |||
117 | static int tangox_wdt_probe(struct platform_device *pdev) | ||
118 | { | ||
119 | struct tangox_wdt_device *dev; | ||
120 | struct resource *res; | ||
121 | u32 config; | ||
122 | int err; | ||
123 | |||
124 | dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); | ||
125 | if (!dev) | ||
126 | return -ENOMEM; | ||
127 | |||
128 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
129 | dev->base = devm_ioremap_resource(&pdev->dev, res); | ||
130 | if (IS_ERR(dev->base)) | ||
131 | return PTR_ERR(dev->base); | ||
132 | |||
133 | dev->clk = devm_clk_get(&pdev->dev, NULL); | ||
134 | if (IS_ERR(dev->clk)) | ||
135 | return PTR_ERR(dev->clk); | ||
136 | |||
137 | err = clk_prepare_enable(dev->clk); | ||
138 | if (err) | ||
139 | return err; | ||
140 | |||
141 | dev->clk_rate = clk_get_rate(dev->clk); | ||
142 | |||
143 | dev->wdt.parent = &pdev->dev; | ||
144 | dev->wdt.info = &tangox_wdt_info; | ||
145 | dev->wdt.ops = &tangox_wdt_ops; | ||
146 | dev->wdt.timeout = DEFAULT_TIMEOUT; | ||
147 | dev->wdt.min_timeout = 1; | ||
148 | dev->wdt.max_timeout = (U32_MAX - 1) / dev->clk_rate; | ||
149 | |||
150 | watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev); | ||
151 | watchdog_set_nowayout(&dev->wdt, nowayout); | ||
152 | watchdog_set_drvdata(&dev->wdt, dev); | ||
153 | |||
154 | /* | ||
155 | * Deactivate counter if disable bit is set to avoid | ||
156 | * accidental reset. | ||
157 | */ | ||
158 | config = readl(dev->base + WD_CONFIG); | ||
159 | if (config & WD_CONFIG_DISABLE) | ||
160 | writel(0, dev->base + WD_COUNTER); | ||
161 | |||
162 | writel(WD_CONFIG_XTAL_IN, dev->base + WD_CONFIG); | ||
163 | |||
164 | /* | ||
165 | * Mark as active and restart with configured timeout if | ||
166 | * already running. | ||
167 | */ | ||
168 | if (readl(dev->base + WD_COUNTER)) { | ||
169 | set_bit(WDOG_ACTIVE, &dev->wdt.status); | ||
170 | tangox_wdt_start(&dev->wdt); | ||
171 | } | ||
172 | |||
173 | err = watchdog_register_device(&dev->wdt); | ||
174 | if (err) { | ||
175 | clk_disable_unprepare(dev->clk); | ||
176 | return err; | ||
177 | } | ||
178 | |||
179 | platform_set_drvdata(pdev, dev); | ||
180 | |||
181 | dev->restart.notifier_call = tangox_wdt_restart; | ||
182 | dev->restart.priority = 128; | ||
183 | err = register_restart_handler(&dev->restart); | ||
184 | if (err) | ||
185 | dev_warn(&pdev->dev, "failed to register restart handler\n"); | ||
186 | |||
187 | dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int tangox_wdt_remove(struct platform_device *pdev) | ||
193 | { | ||
194 | struct tangox_wdt_device *dev = platform_get_drvdata(pdev); | ||
195 | |||
196 | tangox_wdt_stop(&dev->wdt); | ||
197 | clk_disable_unprepare(dev->clk); | ||
198 | |||
199 | unregister_restart_handler(&dev->restart); | ||
200 | watchdog_unregister_device(&dev->wdt); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static const struct of_device_id tangox_wdt_dt_ids[] = { | ||
206 | { .compatible = "sigma,smp8642-wdt" }, | ||
207 | { .compatible = "sigma,smp8759-wdt" }, | ||
208 | { } | ||
209 | }; | ||
210 | MODULE_DEVICE_TABLE(of, tangox_wdt_dt_ids); | ||
211 | |||
212 | static struct platform_driver tangox_wdt_driver = { | ||
213 | .probe = tangox_wdt_probe, | ||
214 | .remove = tangox_wdt_remove, | ||
215 | .driver = { | ||
216 | .name = "tangox-wdt", | ||
217 | .of_match_table = tangox_wdt_dt_ids, | ||
218 | }, | ||
219 | }; | ||
220 | |||
221 | module_platform_driver(tangox_wdt_driver); | ||
222 | |||
223 | MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>"); | ||
224 | MODULE_DESCRIPTION("SMP86xx/SMP87xx Watchdog driver"); | ||
225 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c new file mode 100644 index 000000000000..2b8de8602b67 --- /dev/null +++ b/drivers/watchdog/ts4800_wdt.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * Watchdog driver for TS-4800 based boards | ||
3 | * | ||
4 | * Copyright (c) 2015 - Savoir-faire Linux | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mfd/syscon.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/regmap.h> | ||
17 | #include <linux/watchdog.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 | /* possible feed values */ | ||
26 | #define TS4800_WDT_FEED_2S 0x1 | ||
27 | #define TS4800_WDT_FEED_10S 0x2 | ||
28 | #define TS4800_WDT_DISABLE 0x3 | ||
29 | |||
30 | struct ts4800_wdt { | ||
31 | struct watchdog_device wdd; | ||
32 | struct regmap *regmap; | ||
33 | u32 feed_offset; | ||
34 | u32 feed_val; | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * TS-4800 supports the following timeout values: | ||
39 | * | ||
40 | * value desc | ||
41 | * --------------------- | ||
42 | * 0 feed for 338ms | ||
43 | * 1 feed for 2.706s | ||
44 | * 2 feed for 10.824s | ||
45 | * 3 disable watchdog | ||
46 | * | ||
47 | * Keep the regmap/timeout map ordered by timeout | ||
48 | */ | ||
49 | static const struct { | ||
50 | const int timeout; | ||
51 | const int regval; | ||
52 | } ts4800_wdt_map[] = { | ||
53 | { 2, TS4800_WDT_FEED_2S }, | ||
54 | { 10, TS4800_WDT_FEED_10S }, | ||
55 | }; | ||
56 | |||
57 | #define MAX_TIMEOUT_INDEX (ARRAY_SIZE(ts4800_wdt_map) - 1) | ||
58 | |||
59 | static void ts4800_write_feed(struct ts4800_wdt *wdt, u32 val) | ||
60 | { | ||
61 | regmap_write(wdt->regmap, wdt->feed_offset, val); | ||
62 | } | ||
63 | |||
64 | static int ts4800_wdt_start(struct watchdog_device *wdd) | ||
65 | { | ||
66 | struct ts4800_wdt *wdt = watchdog_get_drvdata(wdd); | ||
67 | |||
68 | ts4800_write_feed(wdt, wdt->feed_val); | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int ts4800_wdt_stop(struct watchdog_device *wdd) | ||
73 | { | ||
74 | struct ts4800_wdt *wdt = watchdog_get_drvdata(wdd); | ||
75 | |||
76 | ts4800_write_feed(wdt, TS4800_WDT_DISABLE); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int ts4800_wdt_set_timeout(struct watchdog_device *wdd, | ||
81 | unsigned int timeout) | ||
82 | { | ||
83 | struct ts4800_wdt *wdt = watchdog_get_drvdata(wdd); | ||
84 | int i; | ||
85 | |||
86 | for (i = 0; i < MAX_TIMEOUT_INDEX; i++) { | ||
87 | if (ts4800_wdt_map[i].timeout >= timeout) | ||
88 | break; | ||
89 | } | ||
90 | |||
91 | wdd->timeout = ts4800_wdt_map[i].timeout; | ||
92 | wdt->feed_val = ts4800_wdt_map[i].regval; | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static const struct watchdog_ops ts4800_wdt_ops = { | ||
98 | .owner = THIS_MODULE, | ||
99 | .start = ts4800_wdt_start, | ||
100 | .stop = ts4800_wdt_stop, | ||
101 | .set_timeout = ts4800_wdt_set_timeout, | ||
102 | }; | ||
103 | |||
104 | static const struct watchdog_info ts4800_wdt_info = { | ||
105 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | ||
106 | .identity = "TS-4800 Watchdog", | ||
107 | }; | ||
108 | |||
109 | static int ts4800_wdt_probe(struct platform_device *pdev) | ||
110 | { | ||
111 | struct device_node *np = pdev->dev.of_node; | ||
112 | struct device_node *syscon_np; | ||
113 | struct watchdog_device *wdd; | ||
114 | struct ts4800_wdt *wdt; | ||
115 | u32 reg; | ||
116 | int ret; | ||
117 | |||
118 | syscon_np = of_parse_phandle(np, "syscon", 0); | ||
119 | if (!syscon_np) { | ||
120 | dev_err(&pdev->dev, "no syscon property\n"); | ||
121 | return -ENODEV; | ||
122 | } | ||
123 | |||
124 | ret = of_property_read_u32_index(np, "syscon", 1, ®); | ||
125 | if (ret < 0) { | ||
126 | dev_err(&pdev->dev, "no offset in syscon\n"); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | /* allocate memory for watchdog struct */ | ||
131 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
132 | if (!wdt) | ||
133 | return -ENOMEM; | ||
134 | |||
135 | /* set regmap and offset to know where to write */ | ||
136 | wdt->feed_offset = reg; | ||
137 | wdt->regmap = syscon_node_to_regmap(syscon_np); | ||
138 | if (IS_ERR(wdt->regmap)) { | ||
139 | dev_err(&pdev->dev, "cannot get parent's regmap\n"); | ||
140 | return PTR_ERR(wdt->regmap); | ||
141 | } | ||
142 | |||
143 | /* Initialize struct watchdog_device */ | ||
144 | wdd = &wdt->wdd; | ||
145 | wdd->parent = &pdev->dev; | ||
146 | wdd->info = &ts4800_wdt_info; | ||
147 | wdd->ops = &ts4800_wdt_ops; | ||
148 | wdd->min_timeout = ts4800_wdt_map[0].timeout; | ||
149 | wdd->max_timeout = ts4800_wdt_map[MAX_TIMEOUT_INDEX].timeout; | ||
150 | |||
151 | watchdog_set_drvdata(wdd, wdt); | ||
152 | watchdog_set_nowayout(wdd, nowayout); | ||
153 | watchdog_init_timeout(wdd, 0, &pdev->dev); | ||
154 | |||
155 | /* | ||
156 | * As this watchdog supports only a few values, ts4800_wdt_set_timeout | ||
157 | * must be called to initialize timeout and feed_val with valid values. | ||
158 | * Default to maximum timeout if none, or an invalid one, is provided in | ||
159 | * device tree. | ||
160 | */ | ||
161 | if (!wdd->timeout) | ||
162 | wdd->timeout = wdd->max_timeout; | ||
163 | ts4800_wdt_set_timeout(wdd, wdd->timeout); | ||
164 | |||
165 | /* | ||
166 | * The feed register is write-only, so it is not possible to determine | ||
167 | * watchdog's state. Disable it to be in a known state. | ||
168 | */ | ||
169 | ts4800_wdt_stop(wdd); | ||
170 | |||
171 | ret = watchdog_register_device(wdd); | ||
172 | if (ret) { | ||
173 | dev_err(&pdev->dev, | ||
174 | "failed to register watchdog device\n"); | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | platform_set_drvdata(pdev, wdt); | ||
179 | |||
180 | dev_info(&pdev->dev, | ||
181 | "initialized (timeout = %d sec, nowayout = %d)\n", | ||
182 | wdd->timeout, nowayout); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int ts4800_wdt_remove(struct platform_device *pdev) | ||
188 | { | ||
189 | struct ts4800_wdt *wdt = platform_get_drvdata(pdev); | ||
190 | |||
191 | watchdog_unregister_device(&wdt->wdd); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static const struct of_device_id ts4800_wdt_of_match[] = { | ||
197 | { .compatible = "technologic,ts4800-wdt", }, | ||
198 | { }, | ||
199 | }; | ||
200 | MODULE_DEVICE_TABLE(of, ts4800_wdt_of_match); | ||
201 | |||
202 | static struct platform_driver ts4800_wdt_driver = { | ||
203 | .probe = ts4800_wdt_probe, | ||
204 | .remove = ts4800_wdt_remove, | ||
205 | .driver = { | ||
206 | .name = "ts4800_wdt", | ||
207 | .of_match_table = ts4800_wdt_of_match, | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | module_platform_driver(ts4800_wdt_driver); | ||
212 | |||
213 | MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); | ||
214 | MODULE_LICENSE("GPL v2"); | ||
215 | MODULE_ALIAS("platform:ts4800_wdt"); | ||
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 5824e25eebbb..cab14bc9106c 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c | |||
@@ -36,8 +36,6 @@ | |||
36 | #include <linux/types.h> | 36 | #include <linux/types.h> |
37 | #include <linux/watchdog.h> | 37 | #include <linux/watchdog.h> |
38 | #include <linux/ioport.h> | 38 | #include <linux/ioport.h> |
39 | #include <linux/notifier.h> | ||
40 | #include <linux/reboot.h> | ||
41 | #include <linux/init.h> | 39 | #include <linux/init.h> |
42 | #include <linux/io.h> | 40 | #include <linux/io.h> |
43 | 41 | ||
@@ -288,18 +286,6 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog) | |||
288 | } | 286 | } |
289 | 287 | ||
290 | /* | 288 | /* |
291 | * Notifier for system down | ||
292 | */ | ||
293 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
294 | void *unused) | ||
295 | { | ||
296 | if (code == SYS_DOWN || code == SYS_HALT) | ||
297 | wdt_set_time(0); /* Turn the WDT off */ | ||
298 | |||
299 | return NOTIFY_DONE; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Kernel Interfaces | 289 | * Kernel Interfaces |
304 | */ | 290 | */ |
305 | 291 | ||
@@ -329,10 +315,6 @@ static struct watchdog_device wdt_dev = { | |||
329 | * turn the timebomb registers off. | 315 | * turn the timebomb registers off. |
330 | */ | 316 | */ |
331 | 317 | ||
332 | static struct notifier_block wdt_notifier = { | ||
333 | .notifier_call = wdt_notify_sys, | ||
334 | }; | ||
335 | |||
336 | static int wdt_find(int addr) | 318 | static int wdt_find(int addr) |
337 | { | 319 | { |
338 | u8 val; | 320 | u8 val; |
@@ -456,6 +438,7 @@ static int __init wdt_init(void) | |||
456 | 438 | ||
457 | watchdog_init_timeout(&wdt_dev, timeout, NULL); | 439 | watchdog_init_timeout(&wdt_dev, timeout, NULL); |
458 | watchdog_set_nowayout(&wdt_dev, nowayout); | 440 | watchdog_set_nowayout(&wdt_dev, nowayout); |
441 | watchdog_stop_on_reboot(&wdt_dev); | ||
459 | 442 | ||
460 | ret = w83627hf_init(&wdt_dev, chip); | 443 | ret = w83627hf_init(&wdt_dev, chip); |
461 | if (ret) { | 444 | if (ret) { |
@@ -463,30 +446,19 @@ static int __init wdt_init(void) | |||
463 | return ret; | 446 | return ret; |
464 | } | 447 | } |
465 | 448 | ||
466 | ret = register_reboot_notifier(&wdt_notifier); | ||
467 | if (ret != 0) { | ||
468 | pr_err("cannot register reboot notifier (err=%d)\n", ret); | ||
469 | return ret; | ||
470 | } | ||
471 | |||
472 | ret = watchdog_register_device(&wdt_dev); | 449 | ret = watchdog_register_device(&wdt_dev); |
473 | if (ret) | 450 | if (ret) |
474 | goto unreg_reboot; | 451 | return ret; |
475 | 452 | ||
476 | pr_info("initialized. timeout=%d sec (nowayout=%d)\n", | 453 | pr_info("initialized. timeout=%d sec (nowayout=%d)\n", |
477 | wdt_dev.timeout, nowayout); | 454 | wdt_dev.timeout, nowayout); |
478 | 455 | ||
479 | return ret; | 456 | return ret; |
480 | |||
481 | unreg_reboot: | ||
482 | unregister_reboot_notifier(&wdt_notifier); | ||
483 | return ret; | ||
484 | } | 457 | } |
485 | 458 | ||
486 | static void __exit wdt_exit(void) | 459 | static void __exit wdt_exit(void) |
487 | { | 460 | { |
488 | watchdog_unregister_device(&wdt_dev); | 461 | watchdog_unregister_device(&wdt_dev); |
489 | unregister_reboot_notifier(&wdt_notifier); | ||
490 | } | 462 | } |
491 | 463 | ||
492 | module_init(wdt_init); | 464 | module_init(wdt_init); |
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 873f13972cf4..e600fd93b7de 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/types.h> /* For standard types */ | 32 | #include <linux/types.h> /* For standard types */ |
33 | #include <linux/errno.h> /* For the -ENODEV/... values */ | 33 | #include <linux/errno.h> /* For the -ENODEV/... values */ |
34 | #include <linux/kernel.h> /* For printk/panic/... */ | 34 | #include <linux/kernel.h> /* For printk/panic/... */ |
35 | #include <linux/reboot.h> /* For restart handler */ | ||
35 | #include <linux/watchdog.h> /* For watchdog specific items */ | 36 | #include <linux/watchdog.h> /* For watchdog specific items */ |
36 | #include <linux/init.h> /* For __init/__exit/... */ | 37 | #include <linux/init.h> /* For __init/__exit/... */ |
37 | #include <linux/idr.h> /* For ida_* macros */ | 38 | #include <linux/idr.h> /* For ida_* macros */ |
@@ -41,7 +42,6 @@ | |||
41 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ | 42 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ |
42 | 43 | ||
43 | static DEFINE_IDA(watchdog_ida); | 44 | static DEFINE_IDA(watchdog_ida); |
44 | static struct class *watchdog_class; | ||
45 | 45 | ||
46 | /* | 46 | /* |
47 | * Deferred Registration infrastructure. | 47 | * Deferred Registration infrastructure. |
@@ -137,9 +137,63 @@ int watchdog_init_timeout(struct watchdog_device *wdd, | |||
137 | } | 137 | } |
138 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); | 138 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); |
139 | 139 | ||
140 | static int watchdog_reboot_notifier(struct notifier_block *nb, | ||
141 | unsigned long code, void *data) | ||
142 | { | ||
143 | struct watchdog_device *wdd = container_of(nb, struct watchdog_device, | ||
144 | reboot_nb); | ||
145 | |||
146 | if (code == SYS_DOWN || code == SYS_HALT) { | ||
147 | if (watchdog_active(wdd)) { | ||
148 | int ret; | ||
149 | |||
150 | ret = wdd->ops->stop(wdd); | ||
151 | if (ret) | ||
152 | return NOTIFY_BAD; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return NOTIFY_DONE; | ||
157 | } | ||
158 | |||
159 | static int watchdog_restart_notifier(struct notifier_block *nb, | ||
160 | unsigned long action, void *data) | ||
161 | { | ||
162 | struct watchdog_device *wdd = container_of(nb, struct watchdog_device, | ||
163 | restart_nb); | ||
164 | |||
165 | int ret; | ||
166 | |||
167 | ret = wdd->ops->restart(wdd); | ||
168 | if (ret) | ||
169 | return NOTIFY_BAD; | ||
170 | |||
171 | return NOTIFY_DONE; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * watchdog_set_restart_priority - Change priority of restart handler | ||
176 | * @wdd: watchdog device | ||
177 | * @priority: priority of the restart handler, should follow these guidelines: | ||
178 | * 0: use watchdog's restart function as last resort, has limited restart | ||
179 | * capabilies | ||
180 | * 128: default restart handler, use if no other handler is expected to be | ||
181 | * available and/or if restart is sufficient to restart the entire system | ||
182 | * 255: preempt all other handlers | ||
183 | * | ||
184 | * If a wdd->ops->restart function is provided when watchdog_register_device is | ||
185 | * called, it will be registered as a restart handler with the priority given | ||
186 | * here. | ||
187 | */ | ||
188 | void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority) | ||
189 | { | ||
190 | wdd->restart_nb.priority = priority; | ||
191 | } | ||
192 | EXPORT_SYMBOL_GPL(watchdog_set_restart_priority); | ||
193 | |||
140 | static int __watchdog_register_device(struct watchdog_device *wdd) | 194 | static int __watchdog_register_device(struct watchdog_device *wdd) |
141 | { | 195 | { |
142 | int ret, id = -1, devno; | 196 | int ret, id = -1; |
143 | 197 | ||
144 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) | 198 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) |
145 | return -EINVAL; | 199 | return -EINVAL; |
@@ -156,8 +210,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd) | |||
156 | * corrupted in a later stage then we expect a kernel panic! | 210 | * corrupted in a later stage then we expect a kernel panic! |
157 | */ | 211 | */ |
158 | 212 | ||
159 | mutex_init(&wdd->lock); | ||
160 | |||
161 | /* Use alias for watchdog id if possible */ | 213 | /* Use alias for watchdog id if possible */ |
162 | if (wdd->parent) { | 214 | if (wdd->parent) { |
163 | ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); | 215 | ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); |
@@ -192,14 +244,26 @@ static int __watchdog_register_device(struct watchdog_device *wdd) | |||
192 | } | 244 | } |
193 | } | 245 | } |
194 | 246 | ||
195 | devno = wdd->cdev.dev; | 247 | if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { |
196 | wdd->dev = device_create(watchdog_class, wdd->parent, devno, | 248 | wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; |
197 | NULL, "watchdog%d", wdd->id); | 249 | |
198 | if (IS_ERR(wdd->dev)) { | 250 | ret = register_reboot_notifier(&wdd->reboot_nb); |
199 | watchdog_dev_unregister(wdd); | 251 | if (ret) { |
200 | ida_simple_remove(&watchdog_ida, id); | 252 | pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", |
201 | ret = PTR_ERR(wdd->dev); | 253 | wdd->id, ret); |
202 | return ret; | 254 | watchdog_dev_unregister(wdd); |
255 | ida_simple_remove(&watchdog_ida, wdd->id); | ||
256 | return ret; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | if (wdd->ops->restart) { | ||
261 | wdd->restart_nb.notifier_call = watchdog_restart_notifier; | ||
262 | |||
263 | ret = register_restart_handler(&wdd->restart_nb); | ||
264 | if (ret) | ||
265 | pr_warn("watchog%d: Cannot register restart handler (%d)\n", | ||
266 | wdd->id, ret); | ||
203 | } | 267 | } |
204 | 268 | ||
205 | return 0; | 269 | return 0; |
@@ -232,19 +296,17 @@ EXPORT_SYMBOL_GPL(watchdog_register_device); | |||
232 | 296 | ||
233 | static void __watchdog_unregister_device(struct watchdog_device *wdd) | 297 | static void __watchdog_unregister_device(struct watchdog_device *wdd) |
234 | { | 298 | { |
235 | int ret; | ||
236 | int devno; | ||
237 | |||
238 | if (wdd == NULL) | 299 | if (wdd == NULL) |
239 | return; | 300 | return; |
240 | 301 | ||
241 | devno = wdd->cdev.dev; | 302 | if (wdd->ops->restart) |
242 | ret = watchdog_dev_unregister(wdd); | 303 | unregister_restart_handler(&wdd->restart_nb); |
243 | if (ret) | 304 | |
244 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); | 305 | if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) |
245 | device_destroy(watchdog_class, devno); | 306 | unregister_reboot_notifier(&wdd->reboot_nb); |
307 | |||
308 | watchdog_dev_unregister(wdd); | ||
246 | ida_simple_remove(&watchdog_ida, wdd->id); | 309 | ida_simple_remove(&watchdog_ida, wdd->id); |
247 | wdd->dev = NULL; | ||
248 | } | 310 | } |
249 | 311 | ||
250 | /** | 312 | /** |
@@ -287,17 +349,9 @@ static int __init watchdog_init(void) | |||
287 | { | 349 | { |
288 | int err; | 350 | int err; |
289 | 351 | ||
290 | watchdog_class = class_create(THIS_MODULE, "watchdog"); | ||
291 | if (IS_ERR(watchdog_class)) { | ||
292 | pr_err("couldn't create class\n"); | ||
293 | return PTR_ERR(watchdog_class); | ||
294 | } | ||
295 | |||
296 | err = watchdog_dev_init(); | 352 | err = watchdog_dev_init(); |
297 | if (err < 0) { | 353 | if (err < 0) |
298 | class_destroy(watchdog_class); | ||
299 | return err; | 354 | return err; |
300 | } | ||
301 | 355 | ||
302 | watchdog_deferred_registration(); | 356 | watchdog_deferred_registration(); |
303 | return 0; | 357 | return 0; |
@@ -306,7 +360,6 @@ static int __init watchdog_init(void) | |||
306 | static void __exit watchdog_exit(void) | 360 | static void __exit watchdog_exit(void) |
307 | { | 361 | { |
308 | watchdog_dev_exit(); | 362 | watchdog_dev_exit(); |
309 | class_destroy(watchdog_class); | ||
310 | ida_destroy(&watchdog_ida); | 363 | ida_destroy(&watchdog_ida); |
311 | } | 364 | } |
312 | 365 | ||
diff --git a/drivers/watchdog/watchdog_core.h b/drivers/watchdog/watchdog_core.h index 6c951418fca7..86ff962d1e15 100644 --- a/drivers/watchdog/watchdog_core.h +++ b/drivers/watchdog/watchdog_core.h | |||
@@ -32,6 +32,6 @@ | |||
32 | * Functions/procedures to be called by the core | 32 | * Functions/procedures to be called by the core |
33 | */ | 33 | */ |
34 | extern int watchdog_dev_register(struct watchdog_device *); | 34 | extern int watchdog_dev_register(struct watchdog_device *); |
35 | extern int watchdog_dev_unregister(struct watchdog_device *); | 35 | extern void watchdog_dev_unregister(struct watchdog_device *); |
36 | extern int __init watchdog_dev_init(void); | 36 | extern int __init watchdog_dev_init(void); |
37 | extern void __exit watchdog_dev_exit(void); | 37 | extern void __exit watchdog_dev_exit(void); |
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 56a649e66eb2..ba2ecce4aae6 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c | |||
@@ -32,27 +32,51 @@ | |||
32 | 32 | ||
33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
34 | 34 | ||
35 | #include <linux/module.h> /* For module stuff/... */ | 35 | #include <linux/cdev.h> /* For character device */ |
36 | #include <linux/types.h> /* For standard types (like size_t) */ | ||
37 | #include <linux/errno.h> /* For the -ENODEV/... values */ | 36 | #include <linux/errno.h> /* For the -ENODEV/... values */ |
38 | #include <linux/kernel.h> /* For printk/panic/... */ | ||
39 | #include <linux/fs.h> /* For file operations */ | 37 | #include <linux/fs.h> /* For file operations */ |
40 | #include <linux/watchdog.h> /* For watchdog specific items */ | ||
41 | #include <linux/miscdevice.h> /* For handling misc devices */ | ||
42 | #include <linux/init.h> /* For __init/__exit/... */ | 38 | #include <linux/init.h> /* For __init/__exit/... */ |
39 | #include <linux/kernel.h> /* For printk/panic/... */ | ||
40 | #include <linux/kref.h> /* For data references */ | ||
41 | #include <linux/miscdevice.h> /* For handling misc devices */ | ||
42 | #include <linux/module.h> /* For module stuff/... */ | ||
43 | #include <linux/mutex.h> /* For mutexes */ | ||
44 | #include <linux/slab.h> /* For memory functions */ | ||
45 | #include <linux/types.h> /* For standard types (like size_t) */ | ||
46 | #include <linux/watchdog.h> /* For watchdog specific items */ | ||
43 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ | 47 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ |
44 | 48 | ||
45 | #include "watchdog_core.h" | 49 | #include "watchdog_core.h" |
46 | 50 | ||
51 | /* | ||
52 | * struct watchdog_core_data - watchdog core internal data | ||
53 | * @kref: Reference count. | ||
54 | * @cdev: The watchdog's Character device. | ||
55 | * @wdd: Pointer to watchdog device. | ||
56 | * @lock: Lock for watchdog core. | ||
57 | * @status: Watchdog core internal status bits. | ||
58 | */ | ||
59 | struct watchdog_core_data { | ||
60 | struct kref kref; | ||
61 | struct cdev cdev; | ||
62 | struct watchdog_device *wdd; | ||
63 | struct mutex lock; | ||
64 | unsigned long status; /* Internal status bits */ | ||
65 | #define _WDOG_DEV_OPEN 0 /* Opened ? */ | ||
66 | #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ | ||
67 | }; | ||
68 | |||
47 | /* the dev_t structure to store the dynamically allocated watchdog devices */ | 69 | /* the dev_t structure to store the dynamically allocated watchdog devices */ |
48 | static dev_t watchdog_devt; | 70 | static dev_t watchdog_devt; |
49 | /* the watchdog device behind /dev/watchdog */ | 71 | /* Reference to watchdog device behind /dev/watchdog */ |
50 | static struct watchdog_device *old_wdd; | 72 | static struct watchdog_core_data *old_wd_data; |
51 | 73 | ||
52 | /* | 74 | /* |
53 | * watchdog_ping: ping the watchdog. | 75 | * watchdog_ping: ping the watchdog. |
54 | * @wdd: the watchdog device to ping | 76 | * @wdd: the watchdog device to ping |
55 | * | 77 | * |
78 | * The caller must hold wd_data->lock. | ||
79 | * | ||
56 | * If the watchdog has no own ping operation then it needs to be | 80 | * If the watchdog has no own ping operation then it needs to be |
57 | * restarted via the start operation. This wrapper function does | 81 | * restarted via the start operation. This wrapper function does |
58 | * exactly that. | 82 | * exactly that. |
@@ -61,25 +85,16 @@ static struct watchdog_device *old_wdd; | |||
61 | 85 | ||
62 | static int watchdog_ping(struct watchdog_device *wdd) | 86 | static int watchdog_ping(struct watchdog_device *wdd) |
63 | { | 87 | { |
64 | int err = 0; | 88 | int err; |
65 | |||
66 | mutex_lock(&wdd->lock); | ||
67 | |||
68 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | ||
69 | err = -ENODEV; | ||
70 | goto out_ping; | ||
71 | } | ||
72 | 89 | ||
73 | if (!watchdog_active(wdd)) | 90 | if (!watchdog_active(wdd)) |
74 | goto out_ping; | 91 | return 0; |
75 | 92 | ||
76 | if (wdd->ops->ping) | 93 | if (wdd->ops->ping) |
77 | err = wdd->ops->ping(wdd); /* ping the watchdog */ | 94 | err = wdd->ops->ping(wdd); /* ping the watchdog */ |
78 | else | 95 | else |
79 | err = wdd->ops->start(wdd); /* restart watchdog */ | 96 | err = wdd->ops->start(wdd); /* restart watchdog */ |
80 | 97 | ||
81 | out_ping: | ||
82 | mutex_unlock(&wdd->lock); | ||
83 | return err; | 98 | return err; |
84 | } | 99 | } |
85 | 100 | ||
@@ -87,6 +102,8 @@ out_ping: | |||
87 | * watchdog_start: wrapper to start the watchdog. | 102 | * watchdog_start: wrapper to start the watchdog. |
88 | * @wdd: the watchdog device to start | 103 | * @wdd: the watchdog device to start |
89 | * | 104 | * |
105 | * The caller must hold wd_data->lock. | ||
106 | * | ||
90 | * Start the watchdog if it is not active and mark it active. | 107 | * Start the watchdog if it is not active and mark it active. |
91 | * This function returns zero on success or a negative errno code for | 108 | * This function returns zero on success or a negative errno code for |
92 | * failure. | 109 | * failure. |
@@ -94,24 +111,15 @@ out_ping: | |||
94 | 111 | ||
95 | static int watchdog_start(struct watchdog_device *wdd) | 112 | static int watchdog_start(struct watchdog_device *wdd) |
96 | { | 113 | { |
97 | int err = 0; | 114 | int err; |
98 | |||
99 | mutex_lock(&wdd->lock); | ||
100 | |||
101 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | ||
102 | err = -ENODEV; | ||
103 | goto out_start; | ||
104 | } | ||
105 | 115 | ||
106 | if (watchdog_active(wdd)) | 116 | if (watchdog_active(wdd)) |
107 | goto out_start; | 117 | return 0; |
108 | 118 | ||
109 | err = wdd->ops->start(wdd); | 119 | err = wdd->ops->start(wdd); |
110 | if (err == 0) | 120 | if (err == 0) |
111 | set_bit(WDOG_ACTIVE, &wdd->status); | 121 | set_bit(WDOG_ACTIVE, &wdd->status); |
112 | 122 | ||
113 | out_start: | ||
114 | mutex_unlock(&wdd->lock); | ||
115 | return err; | 123 | return err; |
116 | } | 124 | } |
117 | 125 | ||
@@ -119,6 +127,8 @@ out_start: | |||
119 | * watchdog_stop: wrapper to stop the watchdog. | 127 | * watchdog_stop: wrapper to stop the watchdog. |
120 | * @wdd: the watchdog device to stop | 128 | * @wdd: the watchdog device to stop |
121 | * | 129 | * |
130 | * The caller must hold wd_data->lock. | ||
131 | * | ||
122 | * Stop the watchdog if it is still active and unmark it active. | 132 | * Stop the watchdog if it is still active and unmark it active. |
123 | * This function returns zero on success or a negative errno code for | 133 | * This function returns zero on success or a negative errno code for |
124 | * failure. | 134 | * failure. |
@@ -127,93 +137,59 @@ out_start: | |||
127 | 137 | ||
128 | static int watchdog_stop(struct watchdog_device *wdd) | 138 | static int watchdog_stop(struct watchdog_device *wdd) |
129 | { | 139 | { |
130 | int err = 0; | 140 | int err; |
131 | |||
132 | mutex_lock(&wdd->lock); | ||
133 | |||
134 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | ||
135 | err = -ENODEV; | ||
136 | goto out_stop; | ||
137 | } | ||
138 | 141 | ||
139 | if (!watchdog_active(wdd)) | 142 | if (!watchdog_active(wdd)) |
140 | goto out_stop; | 143 | return 0; |
141 | 144 | ||
142 | if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { | 145 | if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { |
143 | dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); | 146 | pr_info("watchdog%d: nowayout prevents watchdog being stopped!\n", |
144 | err = -EBUSY; | 147 | wdd->id); |
145 | goto out_stop; | 148 | return -EBUSY; |
146 | } | 149 | } |
147 | 150 | ||
148 | err = wdd->ops->stop(wdd); | 151 | err = wdd->ops->stop(wdd); |
149 | if (err == 0) | 152 | if (err == 0) |
150 | clear_bit(WDOG_ACTIVE, &wdd->status); | 153 | clear_bit(WDOG_ACTIVE, &wdd->status); |
151 | 154 | ||
152 | out_stop: | ||
153 | mutex_unlock(&wdd->lock); | ||
154 | return err; | 155 | return err; |
155 | } | 156 | } |
156 | 157 | ||
157 | /* | 158 | /* |
158 | * watchdog_get_status: wrapper to get the watchdog status | 159 | * watchdog_get_status: wrapper to get the watchdog status |
159 | * @wdd: the watchdog device to get the status from | 160 | * @wdd: the watchdog device to get the status from |
160 | * @status: the status of the watchdog device | 161 | * |
162 | * The caller must hold wd_data->lock. | ||
161 | * | 163 | * |
162 | * Get the watchdog's status flags. | 164 | * Get the watchdog's status flags. |
163 | */ | 165 | */ |
164 | 166 | ||
165 | static int watchdog_get_status(struct watchdog_device *wdd, | 167 | static unsigned int watchdog_get_status(struct watchdog_device *wdd) |
166 | unsigned int *status) | ||
167 | { | 168 | { |
168 | int err = 0; | ||
169 | |||
170 | *status = 0; | ||
171 | if (!wdd->ops->status) | 169 | if (!wdd->ops->status) |
172 | return -EOPNOTSUPP; | 170 | return 0; |
173 | |||
174 | mutex_lock(&wdd->lock); | ||
175 | |||
176 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | ||
177 | err = -ENODEV; | ||
178 | goto out_status; | ||
179 | } | ||
180 | |||
181 | *status = wdd->ops->status(wdd); | ||
182 | 171 | ||
183 | out_status: | 172 | return wdd->ops->status(wdd); |
184 | mutex_unlock(&wdd->lock); | ||
185 | return err; | ||
186 | } | 173 | } |
187 | 174 | ||
188 | /* | 175 | /* |
189 | * watchdog_set_timeout: set the watchdog timer timeout | 176 | * watchdog_set_timeout: set the watchdog timer timeout |
190 | * @wdd: the watchdog device to set the timeout for | 177 | * @wdd: the watchdog device to set the timeout for |
191 | * @timeout: timeout to set in seconds | 178 | * @timeout: timeout to set in seconds |
179 | * | ||
180 | * The caller must hold wd_data->lock. | ||
192 | */ | 181 | */ |
193 | 182 | ||
194 | static int watchdog_set_timeout(struct watchdog_device *wdd, | 183 | static int watchdog_set_timeout(struct watchdog_device *wdd, |
195 | unsigned int timeout) | 184 | unsigned int timeout) |
196 | { | 185 | { |
197 | int err; | ||
198 | |||
199 | if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) | 186 | if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) |
200 | return -EOPNOTSUPP; | 187 | return -EOPNOTSUPP; |
201 | 188 | ||
202 | if (watchdog_timeout_invalid(wdd, timeout)) | 189 | if (watchdog_timeout_invalid(wdd, timeout)) |
203 | return -EINVAL; | 190 | return -EINVAL; |
204 | 191 | ||
205 | mutex_lock(&wdd->lock); | 192 | return wdd->ops->set_timeout(wdd, timeout); |
206 | |||
207 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | ||
208 | err = -ENODEV; | ||
209 | goto out_timeout; | ||
210 | } | ||
211 | |||
212 | err = wdd->ops->set_timeout(wdd, timeout); | ||
213 | |||
214 | out_timeout: | ||
215 | mutex_unlock(&wdd->lock); | ||
216 | return err; | ||
217 | } | 193 | } |
218 | 194 | ||
219 | /* | 195 | /* |
@@ -221,59 +197,156 @@ out_timeout: | |||
221 | * @wdd: the watchdog device to get the remaining time from | 197 | * @wdd: the watchdog device to get the remaining time from |
222 | * @timeleft: the time that's left | 198 | * @timeleft: the time that's left |
223 | * | 199 | * |
200 | * The caller must hold wd_data->lock. | ||
201 | * | ||
224 | * Get the time before a watchdog will reboot (if not pinged). | 202 | * Get the time before a watchdog will reboot (if not pinged). |
225 | */ | 203 | */ |
226 | 204 | ||
227 | static int watchdog_get_timeleft(struct watchdog_device *wdd, | 205 | static int watchdog_get_timeleft(struct watchdog_device *wdd, |
228 | unsigned int *timeleft) | 206 | unsigned int *timeleft) |
229 | { | 207 | { |
230 | int err = 0; | ||
231 | |||
232 | *timeleft = 0; | 208 | *timeleft = 0; |
209 | |||
233 | if (!wdd->ops->get_timeleft) | 210 | if (!wdd->ops->get_timeleft) |
234 | return -EOPNOTSUPP; | 211 | return -EOPNOTSUPP; |
235 | 212 | ||
236 | mutex_lock(&wdd->lock); | 213 | *timeleft = wdd->ops->get_timeleft(wdd); |
237 | 214 | ||
238 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | 215 | return 0; |
239 | err = -ENODEV; | 216 | } |
240 | goto out_timeleft; | ||
241 | } | ||
242 | 217 | ||
243 | *timeleft = wdd->ops->get_timeleft(wdd); | 218 | #ifdef CONFIG_WATCHDOG_SYSFS |
219 | static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, | ||
220 | char *buf) | ||
221 | { | ||
222 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
244 | 223 | ||
245 | out_timeleft: | 224 | return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); |
246 | mutex_unlock(&wdd->lock); | 225 | } |
247 | return err; | 226 | static DEVICE_ATTR_RO(nowayout); |
227 | |||
228 | static ssize_t status_show(struct device *dev, struct device_attribute *attr, | ||
229 | char *buf) | ||
230 | { | ||
231 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
232 | struct watchdog_core_data *wd_data = wdd->wd_data; | ||
233 | unsigned int status; | ||
234 | |||
235 | mutex_lock(&wd_data->lock); | ||
236 | status = watchdog_get_status(wdd); | ||
237 | mutex_unlock(&wd_data->lock); | ||
238 | |||
239 | return sprintf(buf, "%u\n", status); | ||
240 | } | ||
241 | static DEVICE_ATTR_RO(status); | ||
242 | |||
243 | static ssize_t bootstatus_show(struct device *dev, | ||
244 | struct device_attribute *attr, char *buf) | ||
245 | { | ||
246 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
247 | |||
248 | return sprintf(buf, "%u\n", wdd->bootstatus); | ||
249 | } | ||
250 | static DEVICE_ATTR_RO(bootstatus); | ||
251 | |||
252 | static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr, | ||
253 | char *buf) | ||
254 | { | ||
255 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
256 | struct watchdog_core_data *wd_data = wdd->wd_data; | ||
257 | ssize_t status; | ||
258 | unsigned int val; | ||
259 | |||
260 | mutex_lock(&wd_data->lock); | ||
261 | status = watchdog_get_timeleft(wdd, &val); | ||
262 | mutex_unlock(&wd_data->lock); | ||
263 | if (!status) | ||
264 | status = sprintf(buf, "%u\n", val); | ||
265 | |||
266 | return status; | ||
248 | } | 267 | } |
268 | static DEVICE_ATTR_RO(timeleft); | ||
269 | |||
270 | static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, | ||
271 | char *buf) | ||
272 | { | ||
273 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
274 | |||
275 | return sprintf(buf, "%u\n", wdd->timeout); | ||
276 | } | ||
277 | static DEVICE_ATTR_RO(timeout); | ||
278 | |||
279 | static ssize_t identity_show(struct device *dev, struct device_attribute *attr, | ||
280 | char *buf) | ||
281 | { | ||
282 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
283 | |||
284 | return sprintf(buf, "%s\n", wdd->info->identity); | ||
285 | } | ||
286 | static DEVICE_ATTR_RO(identity); | ||
287 | |||
288 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, | ||
289 | char *buf) | ||
290 | { | ||
291 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
292 | |||
293 | if (watchdog_active(wdd)) | ||
294 | return sprintf(buf, "active\n"); | ||
295 | |||
296 | return sprintf(buf, "inactive\n"); | ||
297 | } | ||
298 | static DEVICE_ATTR_RO(state); | ||
299 | |||
300 | static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, | ||
301 | int n) | ||
302 | { | ||
303 | struct device *dev = container_of(kobj, struct device, kobj); | ||
304 | struct watchdog_device *wdd = dev_get_drvdata(dev); | ||
305 | umode_t mode = attr->mode; | ||
306 | |||
307 | if (attr == &dev_attr_status.attr && !wdd->ops->status) | ||
308 | mode = 0; | ||
309 | else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) | ||
310 | mode = 0; | ||
311 | |||
312 | return mode; | ||
313 | } | ||
314 | static struct attribute *wdt_attrs[] = { | ||
315 | &dev_attr_state.attr, | ||
316 | &dev_attr_identity.attr, | ||
317 | &dev_attr_timeout.attr, | ||
318 | &dev_attr_timeleft.attr, | ||
319 | &dev_attr_bootstatus.attr, | ||
320 | &dev_attr_status.attr, | ||
321 | &dev_attr_nowayout.attr, | ||
322 | NULL, | ||
323 | }; | ||
324 | |||
325 | static const struct attribute_group wdt_group = { | ||
326 | .attrs = wdt_attrs, | ||
327 | .is_visible = wdt_is_visible, | ||
328 | }; | ||
329 | __ATTRIBUTE_GROUPS(wdt); | ||
330 | #else | ||
331 | #define wdt_groups NULL | ||
332 | #endif | ||
249 | 333 | ||
250 | /* | 334 | /* |
251 | * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined | 335 | * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined |
252 | * @wdd: the watchdog device to do the ioctl on | 336 | * @wdd: the watchdog device to do the ioctl on |
253 | * @cmd: watchdog command | 337 | * @cmd: watchdog command |
254 | * @arg: argument pointer | 338 | * @arg: argument pointer |
339 | * | ||
340 | * The caller must hold wd_data->lock. | ||
255 | */ | 341 | */ |
256 | 342 | ||
257 | static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, | 343 | static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, |
258 | unsigned long arg) | 344 | unsigned long arg) |
259 | { | 345 | { |
260 | int err; | ||
261 | |||
262 | if (!wdd->ops->ioctl) | 346 | if (!wdd->ops->ioctl) |
263 | return -ENOIOCTLCMD; | 347 | return -ENOIOCTLCMD; |
264 | 348 | ||
265 | mutex_lock(&wdd->lock); | 349 | return wdd->ops->ioctl(wdd, cmd, arg); |
266 | |||
267 | if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { | ||
268 | err = -ENODEV; | ||
269 | goto out_ioctl; | ||
270 | } | ||
271 | |||
272 | err = wdd->ops->ioctl(wdd, cmd, arg); | ||
273 | |||
274 | out_ioctl: | ||
275 | mutex_unlock(&wdd->lock); | ||
276 | return err; | ||
277 | } | 350 | } |
278 | 351 | ||
279 | /* | 352 | /* |
@@ -291,10 +364,11 @@ out_ioctl: | |||
291 | static ssize_t watchdog_write(struct file *file, const char __user *data, | 364 | static ssize_t watchdog_write(struct file *file, const char __user *data, |
292 | size_t len, loff_t *ppos) | 365 | size_t len, loff_t *ppos) |
293 | { | 366 | { |
294 | struct watchdog_device *wdd = file->private_data; | 367 | struct watchdog_core_data *wd_data = file->private_data; |
368 | struct watchdog_device *wdd; | ||
369 | int err; | ||
295 | size_t i; | 370 | size_t i; |
296 | char c; | 371 | char c; |
297 | int err; | ||
298 | 372 | ||
299 | if (len == 0) | 373 | if (len == 0) |
300 | return 0; | 374 | return 0; |
@@ -303,18 +377,25 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, | |||
303 | * Note: just in case someone wrote the magic character | 377 | * Note: just in case someone wrote the magic character |
304 | * five months ago... | 378 | * five months ago... |
305 | */ | 379 | */ |
306 | clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); | 380 | clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); |
307 | 381 | ||
308 | /* scan to see whether or not we got the magic character */ | 382 | /* scan to see whether or not we got the magic character */ |
309 | for (i = 0; i != len; i++) { | 383 | for (i = 0; i != len; i++) { |
310 | if (get_user(c, data + i)) | 384 | if (get_user(c, data + i)) |
311 | return -EFAULT; | 385 | return -EFAULT; |
312 | if (c == 'V') | 386 | if (c == 'V') |
313 | set_bit(WDOG_ALLOW_RELEASE, &wdd->status); | 387 | set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); |
314 | } | 388 | } |
315 | 389 | ||
316 | /* someone wrote to us, so we send the watchdog a keepalive ping */ | 390 | /* someone wrote to us, so we send the watchdog a keepalive ping */ |
317 | err = watchdog_ping(wdd); | 391 | |
392 | err = -ENODEV; | ||
393 | mutex_lock(&wd_data->lock); | ||
394 | wdd = wd_data->wdd; | ||
395 | if (wdd) | ||
396 | err = watchdog_ping(wdd); | ||
397 | mutex_unlock(&wd_data->lock); | ||
398 | |||
318 | if (err < 0) | 399 | if (err < 0) |
319 | return err; | 400 | return err; |
320 | 401 | ||
@@ -334,71 +415,94 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, | |||
334 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | 415 | static long watchdog_ioctl(struct file *file, unsigned int cmd, |
335 | unsigned long arg) | 416 | unsigned long arg) |
336 | { | 417 | { |
337 | struct watchdog_device *wdd = file->private_data; | 418 | struct watchdog_core_data *wd_data = file->private_data; |
338 | void __user *argp = (void __user *)arg; | 419 | void __user *argp = (void __user *)arg; |
420 | struct watchdog_device *wdd; | ||
339 | int __user *p = argp; | 421 | int __user *p = argp; |
340 | unsigned int val; | 422 | unsigned int val; |
341 | int err; | 423 | int err; |
342 | 424 | ||
425 | mutex_lock(&wd_data->lock); | ||
426 | |||
427 | wdd = wd_data->wdd; | ||
428 | if (!wdd) { | ||
429 | err = -ENODEV; | ||
430 | goto out_ioctl; | ||
431 | } | ||
432 | |||
343 | err = watchdog_ioctl_op(wdd, cmd, arg); | 433 | err = watchdog_ioctl_op(wdd, cmd, arg); |
344 | if (err != -ENOIOCTLCMD) | 434 | if (err != -ENOIOCTLCMD) |
345 | return err; | 435 | goto out_ioctl; |
346 | 436 | ||
347 | switch (cmd) { | 437 | switch (cmd) { |
348 | case WDIOC_GETSUPPORT: | 438 | case WDIOC_GETSUPPORT: |
349 | return copy_to_user(argp, wdd->info, | 439 | err = copy_to_user(argp, wdd->info, |
350 | sizeof(struct watchdog_info)) ? -EFAULT : 0; | 440 | sizeof(struct watchdog_info)) ? -EFAULT : 0; |
441 | break; | ||
351 | case WDIOC_GETSTATUS: | 442 | case WDIOC_GETSTATUS: |
352 | err = watchdog_get_status(wdd, &val); | 443 | val = watchdog_get_status(wdd); |
353 | if (err == -ENODEV) | 444 | err = put_user(val, p); |
354 | return err; | 445 | break; |
355 | return put_user(val, p); | ||
356 | case WDIOC_GETBOOTSTATUS: | 446 | case WDIOC_GETBOOTSTATUS: |
357 | return put_user(wdd->bootstatus, p); | 447 | err = put_user(wdd->bootstatus, p); |
448 | break; | ||
358 | case WDIOC_SETOPTIONS: | 449 | case WDIOC_SETOPTIONS: |
359 | if (get_user(val, p)) | 450 | if (get_user(val, p)) { |
360 | return -EFAULT; | 451 | err = -EFAULT; |
452 | break; | ||
453 | } | ||
361 | if (val & WDIOS_DISABLECARD) { | 454 | if (val & WDIOS_DISABLECARD) { |
362 | err = watchdog_stop(wdd); | 455 | err = watchdog_stop(wdd); |
363 | if (err < 0) | 456 | if (err < 0) |
364 | return err; | 457 | break; |
365 | } | 458 | } |
366 | if (val & WDIOS_ENABLECARD) { | 459 | if (val & WDIOS_ENABLECARD) |
367 | err = watchdog_start(wdd); | 460 | err = watchdog_start(wdd); |
368 | if (err < 0) | 461 | break; |
369 | return err; | ||
370 | } | ||
371 | return 0; | ||
372 | case WDIOC_KEEPALIVE: | 462 | case WDIOC_KEEPALIVE: |
373 | if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) | 463 | if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) { |
374 | return -EOPNOTSUPP; | 464 | err = -EOPNOTSUPP; |
375 | return watchdog_ping(wdd); | 465 | break; |
466 | } | ||
467 | err = watchdog_ping(wdd); | ||
468 | break; | ||
376 | case WDIOC_SETTIMEOUT: | 469 | case WDIOC_SETTIMEOUT: |
377 | if (get_user(val, p)) | 470 | if (get_user(val, p)) { |
378 | return -EFAULT; | 471 | err = -EFAULT; |
472 | break; | ||
473 | } | ||
379 | err = watchdog_set_timeout(wdd, val); | 474 | err = watchdog_set_timeout(wdd, val); |
380 | if (err < 0) | 475 | if (err < 0) |
381 | return err; | 476 | break; |
382 | /* If the watchdog is active then we send a keepalive ping | 477 | /* If the watchdog is active then we send a keepalive ping |
383 | * to make sure that the watchdog keep's running (and if | 478 | * to make sure that the watchdog keep's running (and if |
384 | * possible that it takes the new timeout) */ | 479 | * possible that it takes the new timeout) */ |
385 | err = watchdog_ping(wdd); | 480 | err = watchdog_ping(wdd); |
386 | if (err < 0) | 481 | if (err < 0) |
387 | return err; | 482 | break; |
388 | /* Fall */ | 483 | /* Fall */ |
389 | case WDIOC_GETTIMEOUT: | 484 | case WDIOC_GETTIMEOUT: |
390 | /* timeout == 0 means that we don't know the timeout */ | 485 | /* timeout == 0 means that we don't know the timeout */ |
391 | if (wdd->timeout == 0) | 486 | if (wdd->timeout == 0) { |
392 | return -EOPNOTSUPP; | 487 | err = -EOPNOTSUPP; |
393 | return put_user(wdd->timeout, p); | 488 | break; |
489 | } | ||
490 | err = put_user(wdd->timeout, p); | ||
491 | break; | ||
394 | case WDIOC_GETTIMELEFT: | 492 | case WDIOC_GETTIMELEFT: |
395 | err = watchdog_get_timeleft(wdd, &val); | 493 | err = watchdog_get_timeleft(wdd, &val); |
396 | if (err) | 494 | if (err < 0) |
397 | return err; | 495 | break; |
398 | return put_user(val, p); | 496 | err = put_user(val, p); |
497 | break; | ||
399 | default: | 498 | default: |
400 | return -ENOTTY; | 499 | err = -ENOTTY; |
500 | break; | ||
401 | } | 501 | } |
502 | |||
503 | out_ioctl: | ||
504 | mutex_unlock(&wd_data->lock); | ||
505 | return err; | ||
402 | } | 506 | } |
403 | 507 | ||
404 | /* | 508 | /* |
@@ -413,45 +517,59 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
413 | 517 | ||
414 | static int watchdog_open(struct inode *inode, struct file *file) | 518 | static int watchdog_open(struct inode *inode, struct file *file) |
415 | { | 519 | { |
416 | int err = -EBUSY; | 520 | struct watchdog_core_data *wd_data; |
417 | struct watchdog_device *wdd; | 521 | struct watchdog_device *wdd; |
522 | int err; | ||
418 | 523 | ||
419 | /* Get the corresponding watchdog device */ | 524 | /* Get the corresponding watchdog device */ |
420 | if (imajor(inode) == MISC_MAJOR) | 525 | if (imajor(inode) == MISC_MAJOR) |
421 | wdd = old_wdd; | 526 | wd_data = old_wd_data; |
422 | else | 527 | else |
423 | wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); | 528 | wd_data = container_of(inode->i_cdev, struct watchdog_core_data, |
529 | cdev); | ||
424 | 530 | ||
425 | /* the watchdog is single open! */ | 531 | /* the watchdog is single open! */ |
426 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) | 532 | if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) |
427 | return -EBUSY; | 533 | return -EBUSY; |
428 | 534 | ||
535 | wdd = wd_data->wdd; | ||
536 | |||
429 | /* | 537 | /* |
430 | * If the /dev/watchdog device is open, we don't want the module | 538 | * If the /dev/watchdog device is open, we don't want the module |
431 | * to be unloaded. | 539 | * to be unloaded. |
432 | */ | 540 | */ |
433 | if (!try_module_get(wdd->ops->owner)) | 541 | if (!try_module_get(wdd->ops->owner)) { |
434 | goto out; | 542 | err = -EBUSY; |
543 | goto out_clear; | ||
544 | } | ||
435 | 545 | ||
436 | err = watchdog_start(wdd); | 546 | err = watchdog_start(wdd); |
437 | if (err < 0) | 547 | if (err < 0) |
438 | goto out_mod; | 548 | goto out_mod; |
439 | 549 | ||
440 | file->private_data = wdd; | 550 | file->private_data = wd_data; |
441 | 551 | ||
442 | if (wdd->ops->ref) | 552 | kref_get(&wd_data->kref); |
443 | wdd->ops->ref(wdd); | ||
444 | 553 | ||
445 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | 554 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
446 | return nonseekable_open(inode, file); | 555 | return nonseekable_open(inode, file); |
447 | 556 | ||
448 | out_mod: | 557 | out_mod: |
449 | module_put(wdd->ops->owner); | 558 | module_put(wd_data->wdd->ops->owner); |
450 | out: | 559 | out_clear: |
451 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | 560 | clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
452 | return err; | 561 | return err; |
453 | } | 562 | } |
454 | 563 | ||
564 | static void watchdog_core_data_release(struct kref *kref) | ||
565 | { | ||
566 | struct watchdog_core_data *wd_data; | ||
567 | |||
568 | wd_data = container_of(kref, struct watchdog_core_data, kref); | ||
569 | |||
570 | kfree(wd_data); | ||
571 | } | ||
572 | |||
455 | /* | 573 | /* |
456 | * watchdog_release: release the watchdog device. | 574 | * watchdog_release: release the watchdog device. |
457 | * @inode: inode of device | 575 | * @inode: inode of device |
@@ -464,9 +582,16 @@ out: | |||
464 | 582 | ||
465 | static int watchdog_release(struct inode *inode, struct file *file) | 583 | static int watchdog_release(struct inode *inode, struct file *file) |
466 | { | 584 | { |
467 | struct watchdog_device *wdd = file->private_data; | 585 | struct watchdog_core_data *wd_data = file->private_data; |
586 | struct watchdog_device *wdd; | ||
468 | int err = -EBUSY; | 587 | int err = -EBUSY; |
469 | 588 | ||
589 | mutex_lock(&wd_data->lock); | ||
590 | |||
591 | wdd = wd_data->wdd; | ||
592 | if (!wdd) | ||
593 | goto done; | ||
594 | |||
470 | /* | 595 | /* |
471 | * We only stop the watchdog if we received the magic character | 596 | * We only stop the watchdog if we received the magic character |
472 | * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then | 597 | * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then |
@@ -474,29 +599,24 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
474 | */ | 599 | */ |
475 | if (!test_bit(WDOG_ACTIVE, &wdd->status)) | 600 | if (!test_bit(WDOG_ACTIVE, &wdd->status)) |
476 | err = 0; | 601 | err = 0; |
477 | else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || | 602 | else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || |
478 | !(wdd->info->options & WDIOF_MAGICCLOSE)) | 603 | !(wdd->info->options & WDIOF_MAGICCLOSE)) |
479 | err = watchdog_stop(wdd); | 604 | err = watchdog_stop(wdd); |
480 | 605 | ||
481 | /* If the watchdog was not stopped, send a keepalive ping */ | 606 | /* If the watchdog was not stopped, send a keepalive ping */ |
482 | if (err < 0) { | 607 | if (err < 0) { |
483 | mutex_lock(&wdd->lock); | 608 | pr_crit("watchdog%d: watchdog did not stop!\n", wdd->id); |
484 | if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) | ||
485 | dev_crit(wdd->dev, "watchdog did not stop!\n"); | ||
486 | mutex_unlock(&wdd->lock); | ||
487 | watchdog_ping(wdd); | 609 | watchdog_ping(wdd); |
488 | } | 610 | } |
489 | 611 | ||
490 | /* Allow the owner module to be unloaded again */ | ||
491 | module_put(wdd->ops->owner); | ||
492 | |||
493 | /* make sure that /dev/watchdog can be re-opened */ | 612 | /* make sure that /dev/watchdog can be re-opened */ |
494 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | 613 | clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
495 | |||
496 | /* Note wdd may be gone after this, do not use after this! */ | ||
497 | if (wdd->ops->unref) | ||
498 | wdd->ops->unref(wdd); | ||
499 | 614 | ||
615 | done: | ||
616 | mutex_unlock(&wd_data->lock); | ||
617 | /* Allow the owner module to be unloaded again */ | ||
618 | module_put(wd_data->cdev.owner); | ||
619 | kref_put(&wd_data->kref, watchdog_core_data_release); | ||
500 | return 0; | 620 | return 0; |
501 | } | 621 | } |
502 | 622 | ||
@@ -515,20 +635,31 @@ static struct miscdevice watchdog_miscdev = { | |||
515 | }; | 635 | }; |
516 | 636 | ||
517 | /* | 637 | /* |
518 | * watchdog_dev_register: register a watchdog device | 638 | * watchdog_cdev_register: register watchdog character device |
519 | * @wdd: watchdog device | 639 | * @wdd: watchdog device |
640 | * @devno: character device number | ||
520 | * | 641 | * |
521 | * Register a watchdog device including handling the legacy | 642 | * Register a watchdog character device including handling the legacy |
522 | * /dev/watchdog node. /dev/watchdog is actually a miscdevice and | 643 | * /dev/watchdog node. /dev/watchdog is actually a miscdevice and |
523 | * thus we set it up like that. | 644 | * thus we set it up like that. |
524 | */ | 645 | */ |
525 | 646 | ||
526 | int watchdog_dev_register(struct watchdog_device *wdd) | 647 | static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) |
527 | { | 648 | { |
528 | int err, devno; | 649 | struct watchdog_core_data *wd_data; |
650 | int err; | ||
651 | |||
652 | wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); | ||
653 | if (!wd_data) | ||
654 | return -ENOMEM; | ||
655 | kref_init(&wd_data->kref); | ||
656 | mutex_init(&wd_data->lock); | ||
657 | |||
658 | wd_data->wdd = wdd; | ||
659 | wdd->wd_data = wd_data; | ||
529 | 660 | ||
530 | if (wdd->id == 0) { | 661 | if (wdd->id == 0) { |
531 | old_wdd = wdd; | 662 | old_wd_data = wd_data; |
532 | watchdog_miscdev.parent = wdd->parent; | 663 | watchdog_miscdev.parent = wdd->parent; |
533 | err = misc_register(&watchdog_miscdev); | 664 | err = misc_register(&watchdog_miscdev); |
534 | if (err != 0) { | 665 | if (err != 0) { |
@@ -537,48 +668,106 @@ int watchdog_dev_register(struct watchdog_device *wdd) | |||
537 | if (err == -EBUSY) | 668 | if (err == -EBUSY) |
538 | pr_err("%s: a legacy watchdog module is probably present.\n", | 669 | pr_err("%s: a legacy watchdog module is probably present.\n", |
539 | wdd->info->identity); | 670 | wdd->info->identity); |
540 | old_wdd = NULL; | 671 | old_wd_data = NULL; |
672 | kfree(wd_data); | ||
541 | return err; | 673 | return err; |
542 | } | 674 | } |
543 | } | 675 | } |
544 | 676 | ||
545 | /* Fill in the data structures */ | 677 | /* Fill in the data structures */ |
546 | devno = MKDEV(MAJOR(watchdog_devt), wdd->id); | 678 | cdev_init(&wd_data->cdev, &watchdog_fops); |
547 | cdev_init(&wdd->cdev, &watchdog_fops); | 679 | wd_data->cdev.owner = wdd->ops->owner; |
548 | wdd->cdev.owner = wdd->ops->owner; | ||
549 | 680 | ||
550 | /* Add the device */ | 681 | /* Add the device */ |
551 | err = cdev_add(&wdd->cdev, devno, 1); | 682 | err = cdev_add(&wd_data->cdev, devno, 1); |
552 | if (err) { | 683 | if (err) { |
553 | pr_err("watchdog%d unable to add device %d:%d\n", | 684 | pr_err("watchdog%d unable to add device %d:%d\n", |
554 | wdd->id, MAJOR(watchdog_devt), wdd->id); | 685 | wdd->id, MAJOR(watchdog_devt), wdd->id); |
555 | if (wdd->id == 0) { | 686 | if (wdd->id == 0) { |
556 | misc_deregister(&watchdog_miscdev); | 687 | misc_deregister(&watchdog_miscdev); |
557 | old_wdd = NULL; | 688 | old_wd_data = NULL; |
689 | kref_put(&wd_data->kref, watchdog_core_data_release); | ||
558 | } | 690 | } |
559 | } | 691 | } |
560 | return err; | 692 | return err; |
561 | } | 693 | } |
562 | 694 | ||
563 | /* | 695 | /* |
564 | * watchdog_dev_unregister: unregister a watchdog device | 696 | * watchdog_cdev_unregister: unregister watchdog character device |
565 | * @watchdog: watchdog device | 697 | * @watchdog: watchdog device |
566 | * | 698 | * |
567 | * Unregister the watchdog and if needed the legacy /dev/watchdog device. | 699 | * Unregister watchdog character device and if needed the legacy |
700 | * /dev/watchdog device. | ||
568 | */ | 701 | */ |
569 | 702 | ||
570 | int watchdog_dev_unregister(struct watchdog_device *wdd) | 703 | static void watchdog_cdev_unregister(struct watchdog_device *wdd) |
571 | { | 704 | { |
572 | mutex_lock(&wdd->lock); | 705 | struct watchdog_core_data *wd_data = wdd->wd_data; |
573 | set_bit(WDOG_UNREGISTERED, &wdd->status); | ||
574 | mutex_unlock(&wdd->lock); | ||
575 | 706 | ||
576 | cdev_del(&wdd->cdev); | 707 | cdev_del(&wd_data->cdev); |
577 | if (wdd->id == 0) { | 708 | if (wdd->id == 0) { |
578 | misc_deregister(&watchdog_miscdev); | 709 | misc_deregister(&watchdog_miscdev); |
579 | old_wdd = NULL; | 710 | old_wd_data = NULL; |
580 | } | 711 | } |
581 | return 0; | 712 | |
713 | mutex_lock(&wd_data->lock); | ||
714 | wd_data->wdd = NULL; | ||
715 | wdd->wd_data = NULL; | ||
716 | mutex_unlock(&wd_data->lock); | ||
717 | |||
718 | kref_put(&wd_data->kref, watchdog_core_data_release); | ||
719 | } | ||
720 | |||
721 | static struct class watchdog_class = { | ||
722 | .name = "watchdog", | ||
723 | .owner = THIS_MODULE, | ||
724 | .dev_groups = wdt_groups, | ||
725 | }; | ||
726 | |||
727 | /* | ||
728 | * watchdog_dev_register: register a watchdog device | ||
729 | * @wdd: watchdog device | ||
730 | * | ||
731 | * Register a watchdog device including handling the legacy | ||
732 | * /dev/watchdog node. /dev/watchdog is actually a miscdevice and | ||
733 | * thus we set it up like that. | ||
734 | */ | ||
735 | |||
736 | int watchdog_dev_register(struct watchdog_device *wdd) | ||
737 | { | ||
738 | struct device *dev; | ||
739 | dev_t devno; | ||
740 | int ret; | ||
741 | |||
742 | devno = MKDEV(MAJOR(watchdog_devt), wdd->id); | ||
743 | |||
744 | ret = watchdog_cdev_register(wdd, devno); | ||
745 | if (ret) | ||
746 | return ret; | ||
747 | |||
748 | dev = device_create_with_groups(&watchdog_class, wdd->parent, | ||
749 | devno, wdd, wdd->groups, | ||
750 | "watchdog%d", wdd->id); | ||
751 | if (IS_ERR(dev)) { | ||
752 | watchdog_cdev_unregister(wdd); | ||
753 | return PTR_ERR(dev); | ||
754 | } | ||
755 | |||
756 | return ret; | ||
757 | } | ||
758 | |||
759 | /* | ||
760 | * watchdog_dev_unregister: unregister a watchdog device | ||
761 | * @watchdog: watchdog device | ||
762 | * | ||
763 | * Unregister watchdog device and if needed the legacy | ||
764 | * /dev/watchdog device. | ||
765 | */ | ||
766 | |||
767 | void watchdog_dev_unregister(struct watchdog_device *wdd) | ||
768 | { | ||
769 | device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); | ||
770 | watchdog_cdev_unregister(wdd); | ||
582 | } | 771 | } |
583 | 772 | ||
584 | /* | 773 | /* |
@@ -589,10 +778,22 @@ int watchdog_dev_unregister(struct watchdog_device *wdd) | |||
589 | 778 | ||
590 | int __init watchdog_dev_init(void) | 779 | int __init watchdog_dev_init(void) |
591 | { | 780 | { |
592 | int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); | 781 | int err; |
593 | if (err < 0) | 782 | |
783 | err = class_register(&watchdog_class); | ||
784 | if (err < 0) { | ||
785 | pr_err("couldn't register class\n"); | ||
786 | return err; | ||
787 | } | ||
788 | |||
789 | err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); | ||
790 | if (err < 0) { | ||
594 | pr_err("watchdog: unable to allocate char dev region\n"); | 791 | pr_err("watchdog: unable to allocate char dev region\n"); |
595 | return err; | 792 | class_unregister(&watchdog_class); |
793 | return err; | ||
794 | } | ||
795 | |||
796 | return 0; | ||
596 | } | 797 | } |
597 | 798 | ||
598 | /* | 799 | /* |
@@ -604,4 +805,5 @@ int __init watchdog_dev_init(void) | |||
604 | void __exit watchdog_dev_exit(void) | 805 | void __exit watchdog_dev_exit(void) |
605 | { | 806 | { |
606 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); | 807 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); |
808 | class_unregister(&watchdog_class); | ||
607 | } | 809 | } |
diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c new file mode 100644 index 000000000000..0c7cb7302cf0 --- /dev/null +++ b/drivers/watchdog/ziirave_wdt.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Zodiac Inflight Innovations | ||
3 | * | ||
4 | * Author: Martyn Welch <martyn.welch@collabora.co.uk> | ||
5 | * | ||
6 | * Based on twl4030_wdt.c by Timo Kokkonen <timo.t.kokkonen at nokia.com>: | ||
7 | * | ||
8 | * Copyright (C) Nokia Corporation | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | */ | ||
20 | |||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/sysfs.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/version.h> | ||
28 | #include <linux/watchdog.h> | ||
29 | |||
30 | #define ZIIRAVE_TIMEOUT_MIN 3 | ||
31 | #define ZIIRAVE_TIMEOUT_MAX 255 | ||
32 | |||
33 | #define ZIIRAVE_PING_VALUE 0x0 | ||
34 | |||
35 | #define ZIIRAVE_STATE_INITIAL 0x0 | ||
36 | #define ZIIRAVE_STATE_OFF 0x1 | ||
37 | #define ZIIRAVE_STATE_ON 0x2 | ||
38 | |||
39 | static char *ziirave_reasons[] = {"power cycle", "triggered", NULL, NULL, | ||
40 | "host request", NULL, "illegal configuration", | ||
41 | "illegal instruction", "illegal trap", | ||
42 | "unknown"}; | ||
43 | |||
44 | #define ZIIRAVE_WDT_FIRM_VER_MAJOR 0x1 | ||
45 | #define ZIIRAVE_WDT_BOOT_VER_MAJOR 0x3 | ||
46 | #define ZIIRAVE_WDT_RESET_REASON 0x5 | ||
47 | #define ZIIRAVE_WDT_STATE 0x6 | ||
48 | #define ZIIRAVE_WDT_TIMEOUT 0x7 | ||
49 | #define ZIIRAVE_WDT_TIME_LEFT 0x8 | ||
50 | #define ZIIRAVE_WDT_PING 0x9 | ||
51 | #define ZIIRAVE_WDT_RESET_DURATION 0xa | ||
52 | |||
53 | struct ziirave_wdt_rev { | ||
54 | unsigned char major; | ||
55 | unsigned char minor; | ||
56 | }; | ||
57 | |||
58 | struct ziirave_wdt_data { | ||
59 | struct watchdog_device wdd; | ||
60 | struct ziirave_wdt_rev bootloader_rev; | ||
61 | struct ziirave_wdt_rev firmware_rev; | ||
62 | int reset_reason; | ||
63 | }; | ||
64 | |||
65 | static int wdt_timeout; | ||
66 | module_param(wdt_timeout, int, 0); | ||
67 | MODULE_PARM_DESC(wdt_timeout, "Watchdog timeout in seconds"); | ||
68 | |||
69 | static int reset_duration; | ||
70 | module_param(reset_duration, int, 0); | ||
71 | MODULE_PARM_DESC(reset_duration, | ||
72 | "Watchdog reset pulse duration in milliseconds"); | ||
73 | |||
74 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
75 | module_param(nowayout, bool, 0); | ||
76 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default=" | ||
77 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
78 | |||
79 | static int ziirave_wdt_revision(struct i2c_client *client, | ||
80 | struct ziirave_wdt_rev *rev, u8 command) | ||
81 | { | ||
82 | int ret; | ||
83 | |||
84 | ret = i2c_smbus_read_byte_data(client, command); | ||
85 | if (ret < 0) | ||
86 | return ret; | ||
87 | |||
88 | rev->major = ret; | ||
89 | |||
90 | ret = i2c_smbus_read_byte_data(client, command + 1); | ||
91 | if (ret < 0) | ||
92 | return ret; | ||
93 | |||
94 | rev->minor = ret; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int ziirave_wdt_set_state(struct watchdog_device *wdd, int state) | ||
100 | { | ||
101 | struct i2c_client *client = to_i2c_client(wdd->parent); | ||
102 | |||
103 | return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_STATE, state); | ||
104 | } | ||
105 | |||
106 | static int ziirave_wdt_start(struct watchdog_device *wdd) | ||
107 | { | ||
108 | return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_ON); | ||
109 | } | ||
110 | |||
111 | static int ziirave_wdt_stop(struct watchdog_device *wdd) | ||
112 | { | ||
113 | return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_OFF); | ||
114 | } | ||
115 | |||
116 | static int ziirave_wdt_ping(struct watchdog_device *wdd) | ||
117 | { | ||
118 | struct i2c_client *client = to_i2c_client(wdd->parent); | ||
119 | |||
120 | return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_PING, | ||
121 | ZIIRAVE_PING_VALUE); | ||
122 | } | ||
123 | |||
124 | static int ziirave_wdt_set_timeout(struct watchdog_device *wdd, | ||
125 | unsigned int timeout) | ||
126 | { | ||
127 | struct i2c_client *client = to_i2c_client(wdd->parent); | ||
128 | int ret; | ||
129 | |||
130 | ret = i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_TIMEOUT, timeout); | ||
131 | if (!ret) | ||
132 | wdd->timeout = timeout; | ||
133 | |||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd) | ||
138 | { | ||
139 | struct i2c_client *client = to_i2c_client(wdd->parent); | ||
140 | int ret; | ||
141 | |||
142 | ret = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIME_LEFT); | ||
143 | if (ret < 0) | ||
144 | ret = 0; | ||
145 | |||
146 | return ret; | ||
147 | } | ||
148 | |||
149 | static const struct watchdog_info ziirave_wdt_info = { | ||
150 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | ||
151 | .identity = "Zodiac RAVE Watchdog", | ||
152 | }; | ||
153 | |||
154 | static const struct watchdog_ops ziirave_wdt_ops = { | ||
155 | .owner = THIS_MODULE, | ||
156 | .start = ziirave_wdt_start, | ||
157 | .stop = ziirave_wdt_stop, | ||
158 | .ping = ziirave_wdt_ping, | ||
159 | .set_timeout = ziirave_wdt_set_timeout, | ||
160 | .get_timeleft = ziirave_wdt_get_timeleft, | ||
161 | }; | ||
162 | |||
163 | static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev, | ||
164 | struct device_attribute *attr, | ||
165 | char *buf) | ||
166 | { | ||
167 | struct i2c_client *client = to_i2c_client(dev->parent); | ||
168 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | ||
169 | |||
170 | return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, | ||
171 | w_priv->firmware_rev.minor); | ||
172 | } | ||
173 | |||
174 | static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm, | ||
175 | NULL); | ||
176 | |||
177 | static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev, | ||
178 | struct device_attribute *attr, | ||
179 | char *buf) | ||
180 | { | ||
181 | struct i2c_client *client = to_i2c_client(dev->parent); | ||
182 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | ||
183 | |||
184 | return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, | ||
185 | w_priv->bootloader_rev.minor); | ||
186 | } | ||
187 | |||
188 | static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot, | ||
189 | NULL); | ||
190 | |||
191 | static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev, | ||
192 | struct device_attribute *attr, | ||
193 | char *buf) | ||
194 | { | ||
195 | struct i2c_client *client = to_i2c_client(dev->parent); | ||
196 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | ||
197 | |||
198 | return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]); | ||
199 | } | ||
200 | |||
201 | static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason, | ||
202 | NULL); | ||
203 | |||
204 | static struct attribute *ziirave_wdt_attrs[] = { | ||
205 | &dev_attr_firmware_version.attr, | ||
206 | &dev_attr_bootloader_version.attr, | ||
207 | &dev_attr_reset_reason.attr, | ||
208 | NULL | ||
209 | }; | ||
210 | ATTRIBUTE_GROUPS(ziirave_wdt); | ||
211 | |||
212 | static int ziirave_wdt_init_duration(struct i2c_client *client) | ||
213 | { | ||
214 | int ret; | ||
215 | |||
216 | if (!reset_duration) { | ||
217 | /* See if the reset pulse duration is provided in an of_node */ | ||
218 | if (!client->dev.of_node) | ||
219 | ret = -ENODEV; | ||
220 | else | ||
221 | ret = of_property_read_u32(client->dev.of_node, | ||
222 | "reset-duration-ms", | ||
223 | &reset_duration); | ||
224 | if (ret) { | ||
225 | dev_info(&client->dev, | ||
226 | "Unable to set reset pulse duration, using default\n"); | ||
227 | return 0; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | if (reset_duration < 1 || reset_duration > 255) | ||
232 | return -EINVAL; | ||
233 | |||
234 | dev_info(&client->dev, "Setting reset duration to %dms", | ||
235 | reset_duration); | ||
236 | |||
237 | return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_RESET_DURATION, | ||
238 | reset_duration); | ||
239 | } | ||
240 | |||
241 | static int ziirave_wdt_probe(struct i2c_client *client, | ||
242 | const struct i2c_device_id *id) | ||
243 | { | ||
244 | int ret; | ||
245 | struct ziirave_wdt_data *w_priv; | ||
246 | int val; | ||
247 | |||
248 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
249 | return -ENODEV; | ||
250 | |||
251 | w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL); | ||
252 | if (!w_priv) | ||
253 | return -ENOMEM; | ||
254 | |||
255 | w_priv->wdd.info = &ziirave_wdt_info; | ||
256 | w_priv->wdd.ops = &ziirave_wdt_ops; | ||
257 | w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN; | ||
258 | w_priv->wdd.max_timeout = ZIIRAVE_TIMEOUT_MAX; | ||
259 | w_priv->wdd.parent = &client->dev; | ||
260 | w_priv->wdd.groups = ziirave_wdt_groups; | ||
261 | |||
262 | ret = watchdog_init_timeout(&w_priv->wdd, wdt_timeout, &client->dev); | ||
263 | if (ret) { | ||
264 | dev_info(&client->dev, | ||
265 | "Unable to select timeout value, using default\n"); | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * The default value set in the watchdog should be perfectly valid, so | ||
270 | * pass that in if we haven't provided one via the module parameter or | ||
271 | * of property. | ||
272 | */ | ||
273 | if (w_priv->wdd.timeout == 0) { | ||
274 | val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT); | ||
275 | if (val < 0) | ||
276 | return val; | ||
277 | |||
278 | if (val < ZIIRAVE_TIMEOUT_MIN) | ||
279 | return -ENODEV; | ||
280 | |||
281 | w_priv->wdd.timeout = val; | ||
282 | } else { | ||
283 | ret = ziirave_wdt_set_timeout(&w_priv->wdd, | ||
284 | w_priv->wdd.timeout); | ||
285 | if (ret) | ||
286 | return ret; | ||
287 | |||
288 | dev_info(&client->dev, "Timeout set to %ds.", | ||
289 | w_priv->wdd.timeout); | ||
290 | } | ||
291 | |||
292 | watchdog_set_nowayout(&w_priv->wdd, nowayout); | ||
293 | |||
294 | i2c_set_clientdata(client, w_priv); | ||
295 | |||
296 | /* If in unconfigured state, set to stopped */ | ||
297 | val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE); | ||
298 | if (val < 0) | ||
299 | return val; | ||
300 | |||
301 | if (val == ZIIRAVE_STATE_INITIAL) | ||
302 | ziirave_wdt_stop(&w_priv->wdd); | ||
303 | |||
304 | ret = ziirave_wdt_init_duration(client); | ||
305 | if (ret) | ||
306 | return ret; | ||
307 | |||
308 | ret = ziirave_wdt_revision(client, &w_priv->firmware_rev, | ||
309 | ZIIRAVE_WDT_FIRM_VER_MAJOR); | ||
310 | if (ret) | ||
311 | return ret; | ||
312 | |||
313 | ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev, | ||
314 | ZIIRAVE_WDT_BOOT_VER_MAJOR); | ||
315 | if (ret) | ||
316 | return ret; | ||
317 | |||
318 | w_priv->reset_reason = i2c_smbus_read_byte_data(client, | ||
319 | ZIIRAVE_WDT_RESET_REASON); | ||
320 | if (w_priv->reset_reason < 0) | ||
321 | return w_priv->reset_reason; | ||
322 | |||
323 | if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) || | ||
324 | !ziirave_reasons[w_priv->reset_reason]) | ||
325 | return -ENODEV; | ||
326 | |||
327 | ret = watchdog_register_device(&w_priv->wdd); | ||
328 | |||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static int ziirave_wdt_remove(struct i2c_client *client) | ||
333 | { | ||
334 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | ||
335 | |||
336 | watchdog_unregister_device(&w_priv->wdd); | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static struct i2c_device_id ziirave_wdt_id[] = { | ||
342 | { "ziirave-wdt", 0 }, | ||
343 | { } | ||
344 | }; | ||
345 | MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); | ||
346 | |||
347 | static const struct of_device_id zrv_wdt_of_match[] = { | ||
348 | { .compatible = "zii,rave-wdt", }, | ||
349 | { }, | ||
350 | }; | ||
351 | MODULE_DEVICE_TABLE(of, zrv_wdt_of_match); | ||
352 | |||
353 | static struct i2c_driver ziirave_wdt_driver = { | ||
354 | .driver = { | ||
355 | .name = "ziirave_wdt", | ||
356 | .of_match_table = zrv_wdt_of_match, | ||
357 | }, | ||
358 | .probe = ziirave_wdt_probe, | ||
359 | .remove = ziirave_wdt_remove, | ||
360 | .id_table = ziirave_wdt_id, | ||
361 | }; | ||
362 | |||
363 | module_i2c_driver(ziirave_wdt_driver); | ||
364 | |||
365 | MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk"); | ||
366 | MODULE_DESCRIPTION("Zodiac Aerospace RAVE Switch Watchdog Processor Driver"); | ||
367 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/bcm47xx_wdt.h b/include/linux/bcm47xx_wdt.h index 5582c211f594..8d9d07ec22a5 100644 --- a/include/linux/bcm47xx_wdt.h +++ b/include/linux/bcm47xx_wdt.h | |||
@@ -1,7 +1,6 @@ | |||
1 | #ifndef LINUX_BCM47XX_WDT_H_ | 1 | #ifndef LINUX_BCM47XX_WDT_H_ |
2 | #define LINUX_BCM47XX_WDT_H_ | 2 | #define LINUX_BCM47XX_WDT_H_ |
3 | 3 | ||
4 | #include <linux/notifier.h> | ||
5 | #include <linux/timer.h> | 4 | #include <linux/timer.h> |
6 | #include <linux/types.h> | 5 | #include <linux/types.h> |
7 | #include <linux/watchdog.h> | 6 | #include <linux/watchdog.h> |
@@ -15,8 +14,6 @@ struct bcm47xx_wdt { | |||
15 | void *driver_data; | 14 | void *driver_data; |
16 | 15 | ||
17 | struct watchdog_device wdd; | 16 | struct watchdog_device wdd; |
18 | struct notifier_block notifier; | ||
19 | struct notifier_block restart_handler; | ||
20 | 17 | ||
21 | struct timer_list soft_timer; | 18 | struct timer_list soft_timer; |
22 | atomic_t soft_ticks; | 19 | atomic_t soft_ticks; |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 027b1f43f12d..b585fa2507ee 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -12,10 +12,12 @@ | |||
12 | #include <linux/bitops.h> | 12 | #include <linux/bitops.h> |
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/cdev.h> | 14 | #include <linux/cdev.h> |
15 | #include <linux/notifier.h> | ||
15 | #include <uapi/linux/watchdog.h> | 16 | #include <uapi/linux/watchdog.h> |
16 | 17 | ||
17 | struct watchdog_ops; | 18 | struct watchdog_ops; |
18 | struct watchdog_device; | 19 | struct watchdog_device; |
20 | struct watchdog_core_data; | ||
19 | 21 | ||
20 | /** struct watchdog_ops - The watchdog-devices operations | 22 | /** struct watchdog_ops - The watchdog-devices operations |
21 | * | 23 | * |
@@ -26,8 +28,7 @@ struct watchdog_device; | |||
26 | * @status: The routine that shows the status of the watchdog device. | 28 | * @status: The routine that shows the status of the watchdog device. |
27 | * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). | 29 | * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). |
28 | * @get_timeleft:The routine that gets the time left before a reset (in seconds). | 30 | * @get_timeleft:The routine that gets the time left before a reset (in seconds). |
29 | * @ref: The ref operation for dyn. allocated watchdog_device structs | 31 | * @restart: The routine for restarting the machine. |
30 | * @unref: The unref operation for dyn. allocated watchdog_device structs | ||
31 | * @ioctl: The routines that handles extra ioctl calls. | 32 | * @ioctl: The routines that handles extra ioctl calls. |
32 | * | 33 | * |
33 | * The watchdog_ops structure contains a list of low-level operations | 34 | * The watchdog_ops structure contains a list of low-level operations |
@@ -45,25 +46,26 @@ struct watchdog_ops { | |||
45 | unsigned int (*status)(struct watchdog_device *); | 46 | unsigned int (*status)(struct watchdog_device *); |
46 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 47 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
47 | unsigned int (*get_timeleft)(struct watchdog_device *); | 48 | unsigned int (*get_timeleft)(struct watchdog_device *); |
48 | void (*ref)(struct watchdog_device *); | 49 | int (*restart)(struct watchdog_device *); |
49 | void (*unref)(struct watchdog_device *); | ||
50 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 50 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
51 | }; | 51 | }; |
52 | 52 | ||
53 | /** struct watchdog_device - The structure that defines a watchdog device | 53 | /** struct watchdog_device - The structure that defines a watchdog device |
54 | * | 54 | * |
55 | * @id: The watchdog's ID. (Allocated by watchdog_register_device) | 55 | * @id: The watchdog's ID. (Allocated by watchdog_register_device) |
56 | * @cdev: The watchdog's Character device. | ||
57 | * @dev: The device for our watchdog | ||
58 | * @parent: The parent bus device | 56 | * @parent: The parent bus device |
57 | * @groups: List of sysfs attribute groups to create when creating the | ||
58 | * watchdog device. | ||
59 | * @info: Pointer to a watchdog_info structure. | 59 | * @info: Pointer to a watchdog_info structure. |
60 | * @ops: Pointer to the list of watchdog operations. | 60 | * @ops: Pointer to the list of watchdog operations. |
61 | * @bootstatus: Status of the watchdog device at boot. | 61 | * @bootstatus: Status of the watchdog device at boot. |
62 | * @timeout: The watchdog devices timeout value (in seconds). | 62 | * @timeout: The watchdog devices timeout value (in seconds). |
63 | * @min_timeout:The watchdog devices minimum timeout value (in seconds). | 63 | * @min_timeout:The watchdog devices minimum timeout value (in seconds). |
64 | * @max_timeout:The watchdog devices maximum timeout value (in seconds). | 64 | * @max_timeout:The watchdog devices maximum timeout value (in seconds). |
65 | * @driver-data:Pointer to the drivers private data. | 65 | * @reboot_nb: The notifier block to stop watchdog on reboot. |
66 | * @lock: Lock for watchdog core internal use only. | 66 | * @restart_nb: The notifier block to register a restart function. |
67 | * @driver_data:Pointer to the drivers private data. | ||
68 | * @wd_data: Pointer to watchdog core internal data. | ||
67 | * @status: Field that contains the devices internal status bits. | 69 | * @status: Field that contains the devices internal status bits. |
68 | * @deferred: entry in wtd_deferred_reg_list which is used to | 70 | * @deferred: entry in wtd_deferred_reg_list which is used to |
69 | * register early initialized watchdogs. | 71 | * register early initialized watchdogs. |
@@ -79,24 +81,23 @@ struct watchdog_ops { | |||
79 | */ | 81 | */ |
80 | struct watchdog_device { | 82 | struct watchdog_device { |
81 | int id; | 83 | int id; |
82 | struct cdev cdev; | ||
83 | struct device *dev; | ||
84 | struct device *parent; | 84 | struct device *parent; |
85 | const struct attribute_group **groups; | ||
85 | const struct watchdog_info *info; | 86 | const struct watchdog_info *info; |
86 | const struct watchdog_ops *ops; | 87 | const struct watchdog_ops *ops; |
87 | unsigned int bootstatus; | 88 | unsigned int bootstatus; |
88 | unsigned int timeout; | 89 | unsigned int timeout; |
89 | unsigned int min_timeout; | 90 | unsigned int min_timeout; |
90 | unsigned int max_timeout; | 91 | unsigned int max_timeout; |
92 | struct notifier_block reboot_nb; | ||
93 | struct notifier_block restart_nb; | ||
91 | void *driver_data; | 94 | void *driver_data; |
92 | struct mutex lock; | 95 | struct watchdog_core_data *wd_data; |
93 | unsigned long status; | 96 | unsigned long status; |
94 | /* Bit numbers for status flags */ | 97 | /* Bit numbers for status flags */ |
95 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ | 98 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ |
96 | #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ | 99 | #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ |
97 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ | 100 | #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ |
98 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ | ||
99 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ | ||
100 | struct list_head deferred; | 101 | struct list_head deferred; |
101 | }; | 102 | }; |
102 | 103 | ||
@@ -116,6 +117,12 @@ static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool noway | |||
116 | set_bit(WDOG_NO_WAY_OUT, &wdd->status); | 117 | set_bit(WDOG_NO_WAY_OUT, &wdd->status); |
117 | } | 118 | } |
118 | 119 | ||
120 | /* Use the following function to stop the watchdog on reboot */ | ||
121 | static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd) | ||
122 | { | ||
123 | set_bit(WDOG_STOP_ON_REBOOT, &wdd->status); | ||
124 | } | ||
125 | |||
119 | /* Use the following function to check if a timeout value is invalid */ | 126 | /* Use the following function to check if a timeout value is invalid */ |
120 | static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) | 127 | static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) |
121 | { | 128 | { |
@@ -142,6 +149,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) | |||
142 | } | 149 | } |
143 | 150 | ||
144 | /* drivers/watchdog/watchdog_core.c */ | 151 | /* drivers/watchdog/watchdog_core.c */ |
152 | void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); | ||
145 | extern int watchdog_init_timeout(struct watchdog_device *wdd, | 153 | extern int watchdog_init_timeout(struct watchdog_device *wdd, |
146 | unsigned int timeout_parm, struct device *dev); | 154 | unsigned int timeout_parm, struct device *dev); |
147 | extern int watchdog_register_device(struct watchdog_device *); | 155 | extern int watchdog_register_device(struct watchdog_device *); |