1
2
3
4
5
6
7
8
9#include <linux/delay.h>
10#include <linux/suspend.h>
11#include <linux/workqueue.h>
12#include "greybus.h"
13
14#define SVC_WATCHDOG_PERIOD (2*HZ)
15
16struct gb_svc_watchdog {
17 struct delayed_work work;
18 struct gb_svc *svc;
19 bool enabled;
20 struct notifier_block pm_notifier;
21};
22
23static struct delayed_work reset_work;
24
25static int svc_watchdog_pm_notifier(struct notifier_block *notifier,
26 unsigned long pm_event, void *unused)
27{
28 struct gb_svc_watchdog *watchdog =
29 container_of(notifier, struct gb_svc_watchdog, pm_notifier);
30
31 switch (pm_event) {
32 case PM_SUSPEND_PREPARE:
33 gb_svc_watchdog_disable(watchdog->svc);
34 break;
35 case PM_POST_SUSPEND:
36 gb_svc_watchdog_enable(watchdog->svc);
37 break;
38 default:
39 break;
40 }
41
42 return NOTIFY_DONE;
43}
44
45static void greybus_reset(struct work_struct *work)
46{
47 static char start_path[256] = "/system/bin/start";
48 static char *envp[] = {
49 "HOME=/",
50 "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
51 NULL,
52 };
53 static char *argv[] = {
54 start_path,
55 "unipro_reset",
56 NULL,
57 };
58
59 printk(KERN_ERR "svc_watchdog: calling \"%s %s\" to reset greybus network!\n",
60 argv[0], argv[1]);
61 call_usermodehelper(start_path, argv, envp, UMH_WAIT_EXEC);
62}
63
64static void do_work(struct work_struct *work)
65{
66 struct gb_svc_watchdog *watchdog;
67 struct gb_svc *svc;
68 int retval;
69
70 watchdog = container_of(work, struct gb_svc_watchdog, work.work);
71 svc = watchdog->svc;
72
73 dev_dbg(&svc->dev, "%s: ping.\n", __func__);
74 retval = gb_svc_ping(svc);
75 if (retval) {
76
77
78
79
80
81
82
83 dev_err(&svc->dev,
84 "SVC ping has returned %d, something is wrong!!!\n",
85 retval);
86
87 if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) {
88 panic("SVC is not responding\n");
89 } else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) {
90 dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n");
91
92 INIT_DELAYED_WORK(&reset_work, greybus_reset);
93 schedule_delayed_work(&reset_work, HZ / 2);
94
95
96
97
98
99 watchdog->enabled = false;
100 }
101 }
102
103
104 if (watchdog->enabled)
105 schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD);
106}
107
108int gb_svc_watchdog_create(struct gb_svc *svc)
109{
110 struct gb_svc_watchdog *watchdog;
111 int retval;
112
113 if (svc->watchdog)
114 return 0;
115
116 watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL);
117 if (!watchdog)
118 return -ENOMEM;
119
120 watchdog->enabled = false;
121 watchdog->svc = svc;
122 INIT_DELAYED_WORK(&watchdog->work, do_work);
123 svc->watchdog = watchdog;
124
125 watchdog->pm_notifier.notifier_call = svc_watchdog_pm_notifier;
126 retval = register_pm_notifier(&watchdog->pm_notifier);
127 if (retval) {
128 dev_err(&svc->dev, "error registering pm notifier(%d)\n",
129 retval);
130 goto svc_watchdog_create_err;
131 }
132
133 retval = gb_svc_watchdog_enable(svc);
134 if (retval) {
135 dev_err(&svc->dev, "error enabling watchdog (%d)\n", retval);
136 unregister_pm_notifier(&watchdog->pm_notifier);
137 goto svc_watchdog_create_err;
138 }
139 return retval;
140
141svc_watchdog_create_err:
142 svc->watchdog = NULL;
143 kfree(watchdog);
144
145 return retval;
146}
147
148void gb_svc_watchdog_destroy(struct gb_svc *svc)
149{
150 struct gb_svc_watchdog *watchdog = svc->watchdog;
151
152 if (!watchdog)
153 return;
154
155 unregister_pm_notifier(&watchdog->pm_notifier);
156 gb_svc_watchdog_disable(svc);
157 svc->watchdog = NULL;
158 kfree(watchdog);
159}
160
161bool gb_svc_watchdog_enabled(struct gb_svc *svc)
162{
163 if (!svc || !svc->watchdog)
164 return false;
165 return svc->watchdog->enabled;
166}
167
168int gb_svc_watchdog_enable(struct gb_svc *svc)
169{
170 struct gb_svc_watchdog *watchdog;
171
172 if (!svc->watchdog)
173 return -ENODEV;
174
175 watchdog = svc->watchdog;
176 if (watchdog->enabled)
177 return 0;
178
179 watchdog->enabled = true;
180 schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD);
181 return 0;
182}
183
184int gb_svc_watchdog_disable(struct gb_svc *svc)
185{
186 struct gb_svc_watchdog *watchdog;
187
188 if (!svc->watchdog)
189 return -ENODEV;
190
191 watchdog = svc->watchdog;
192 if (!watchdog->enabled)
193 return 0;
194
195 watchdog->enabled = false;
196 cancel_delayed_work_sync(&watchdog->work);
197 return 0;
198}
199