mei: add mei_hbuf_acquire wrapper
[firefly-linux-kernel-4.4.55.git] / drivers / misc / mei / wd.c
1 /*
2  *
3  * Intel Management Engine Interface (Intel MEI) Linux driver
4  * Copyright (c) 2003-2012, Intel Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  */
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/moduleparam.h>
19 #include <linux/device.h>
20 #include <linux/pci.h>
21 #include <linux/sched.h>
22 #include <linux/watchdog.h>
23
24 #include <linux/mei.h>
25
26 #include "mei_dev.h"
27 #include "hbm.h"
28 #include "hw-me.h"
29 #include "client.h"
30
31 static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
32 static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
33
34 /*
35  * AMT Watchdog Device
36  */
37 #define INTEL_AMT_WATCHDOG_ID "INTCAMT"
38
39 /* UUIDs for AMT F/W clients */
40 const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89,
41                                                 0x9D, 0xA9, 0x15, 0x14, 0xCB,
42                                                 0x32, 0xAB);
43
44 static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
45 {
46         dev_dbg(&dev->pdev->dev, "wd: set timeout=%d.\n", timeout);
47         memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE);
48         memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16));
49 }
50
51 /**
52  * mei_wd_host_init - connect to the watchdog client
53  *
54  * @dev: the device structure
55  *
56  * returns -ENENT if wd client cannot be found
57  *         -EIO if write has failed
58  *         0 on success
59  */
60 int mei_wd_host_init(struct mei_device *dev)
61 {
62         struct mei_cl *cl = &dev->wd_cl;
63         int id;
64         int ret;
65
66         mei_cl_init(cl, dev);
67
68         dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
69         dev->wd_state = MEI_WD_IDLE;
70
71
72         /* check for valid client id */
73         id = mei_me_cl_by_uuid(dev, &mei_wd_guid);
74         if (id < 0) {
75                 dev_info(&dev->pdev->dev, "wd: failed to find the client\n");
76                 return id;
77         }
78
79         cl->me_client_id = dev->me_clients[id].client_id;
80
81         ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
82
83         if (ret < 0) {
84                 dev_info(&dev->pdev->dev, "wd: failed link client\n");
85                 return ret;
86         }
87
88         cl->state = MEI_FILE_CONNECTING;
89
90         ret = mei_cl_connect(cl, NULL);
91
92         if (ret) {
93                 dev_err(&dev->pdev->dev, "wd: failed to connect = %d\n", ret);
94                 mei_cl_unlink(cl);
95                 return ret;
96         }
97
98         ret = mei_watchdog_register(dev);
99         if (ret) {
100                 mei_cl_disconnect(cl);
101                 mei_cl_unlink(cl);
102         }
103         return ret;
104 }
105
106 /**
107  * mei_wd_send - sends watch dog message to fw.
108  *
109  * @dev: the device structure
110  *
111  * returns 0 if success,
112  *      -EIO when message send fails
113  *      -EINVAL when invalid message is to be sent
114  */
115 int mei_wd_send(struct mei_device *dev)
116 {
117         struct mei_msg_hdr hdr;
118
119         hdr.host_addr = dev->wd_cl.host_client_id;
120         hdr.me_addr = dev->wd_cl.me_client_id;
121         hdr.msg_complete = 1;
122         hdr.reserved = 0;
123         hdr.internal = 0;
124
125         if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE))
126                 hdr.length = MEI_WD_START_MSG_SIZE;
127         else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE))
128                 hdr.length = MEI_WD_STOP_MSG_SIZE;
129         else
130                 return -EINVAL;
131
132         return mei_write_message(dev, &hdr, dev->wd_data);
133 }
134
135 /**
136  * mei_wd_stop - sends watchdog stop message to fw.
137  *
138  * @dev: the device structure
139  * @preserve: indicate if to keep the timeout value
140  *
141  * returns 0 if success,
142  *      -EIO when message send fails
143  *      -EINVAL when invalid message is to be sent
144  */
145 int mei_wd_stop(struct mei_device *dev)
146 {
147         int ret;
148
149         if (dev->wd_cl.state != MEI_FILE_CONNECTED ||
150             dev->wd_state != MEI_WD_RUNNING)
151                 return 0;
152
153         memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE);
154
155         dev->wd_state = MEI_WD_STOPPING;
156
157         ret = mei_cl_flow_ctrl_creds(&dev->wd_cl);
158         if (ret < 0)
159                 goto out;
160
161         if (ret && mei_hbuf_acquire(dev)) {
162                 ret = 0;
163
164                 if (!mei_wd_send(dev)) {
165                         ret = mei_cl_flow_ctrl_reduce(&dev->wd_cl);
166                         if (ret)
167                                 goto out;
168                 } else {
169                         dev_err(&dev->pdev->dev, "wd: send stop failed\n");
170                 }
171
172                 dev->wd_pending = false;
173         } else {
174                 dev->wd_pending = true;
175         }
176
177         mutex_unlock(&dev->device_lock);
178
179         ret = wait_event_interruptible_timeout(dev->wait_stop_wd,
180                                         dev->wd_state == MEI_WD_IDLE,
181                                         msecs_to_jiffies(MEI_WD_STOP_TIMEOUT));
182         mutex_lock(&dev->device_lock);
183         if (dev->wd_state == MEI_WD_IDLE) {
184                 dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret);
185                 ret = 0;
186         } else {
187                 if (!ret)
188                         ret = -ETIMEDOUT;
189                 dev_warn(&dev->pdev->dev,
190                         "wd: stop failed to complete ret=%d.\n", ret);
191         }
192
193 out:
194         return ret;
195 }
196
197 /*
198  * mei_wd_ops_start - wd start command from the watchdog core.
199  *
200  * @wd_dev - watchdog device struct
201  *
202  * returns 0 if success, negative errno code for failure
203  */
204 static int mei_wd_ops_start(struct watchdog_device *wd_dev)
205 {
206         int err = -ENODEV;
207         struct mei_device *dev;
208
209         dev = watchdog_get_drvdata(wd_dev);
210         if (!dev)
211                 return -ENODEV;
212
213         mutex_lock(&dev->device_lock);
214
215         if (dev->dev_state != MEI_DEV_ENABLED) {
216                 dev_dbg(&dev->pdev->dev,
217                         "wd: dev_state != MEI_DEV_ENABLED  dev_state = %s\n",
218                         mei_dev_state_str(dev->dev_state));
219                 goto end_unlock;
220         }
221
222         if (dev->wd_cl.state != MEI_FILE_CONNECTED)     {
223                 dev_dbg(&dev->pdev->dev,
224                         "MEI Driver is not connected to Watchdog Client\n");
225                 goto end_unlock;
226         }
227
228         mei_wd_set_start_timeout(dev, dev->wd_timeout);
229
230         err = 0;
231 end_unlock:
232         mutex_unlock(&dev->device_lock);
233         return err;
234 }
235
236 /*
237  * mei_wd_ops_stop -  wd stop command from the watchdog core.
238  *
239  * @wd_dev - watchdog device struct
240  *
241  * returns 0 if success, negative errno code for failure
242  */
243 static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
244 {
245         struct mei_device *dev;
246
247         dev = watchdog_get_drvdata(wd_dev);
248         if (!dev)
249                 return -ENODEV;
250
251         mutex_lock(&dev->device_lock);
252         mei_wd_stop(dev);
253         mutex_unlock(&dev->device_lock);
254
255         return 0;
256 }
257
258 /*
259  * mei_wd_ops_ping - wd ping command from the watchdog core.
260  *
261  * @wd_dev - watchdog device struct
262  *
263  * returns 0 if success, negative errno code for failure
264  */
265 static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
266 {
267         struct mei_device *dev;
268         int ret;
269
270         dev = watchdog_get_drvdata(wd_dev);
271         if (!dev)
272                 return -ENODEV;
273
274         mutex_lock(&dev->device_lock);
275
276         if (dev->wd_cl.state != MEI_FILE_CONNECTED) {
277                 dev_err(&dev->pdev->dev, "wd: not connected.\n");
278                 ret = -ENODEV;
279                 goto end;
280         }
281
282         dev->wd_state = MEI_WD_RUNNING;
283
284         ret = mei_cl_flow_ctrl_creds(&dev->wd_cl);
285         if (ret < 0)
286                 goto end;
287         /* Check if we can send the ping to HW*/
288         if (ret && mei_hbuf_acquire(dev)) {
289
290                 dev_dbg(&dev->pdev->dev, "wd: sending ping\n");
291
292                 if (mei_wd_send(dev)) {
293                         dev_err(&dev->pdev->dev, "wd: send failed.\n");
294                         ret = -EIO;
295                         goto end;
296                 }
297
298                 if (mei_cl_flow_ctrl_reduce(&dev->wd_cl)) {
299                         dev_err(&dev->pdev->dev, "wd: mei_cl_flow_ctrl_reduce() failed.\n");
300                         ret = -EIO;
301                         goto end;
302                 }
303
304         } else {
305                 dev->wd_pending = true;
306         }
307
308 end:
309         mutex_unlock(&dev->device_lock);
310         return ret;
311 }
312
313 /*
314  * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core.
315  *
316  * @wd_dev - watchdog device struct
317  * @timeout - timeout value to set
318  *
319  * returns 0 if success, negative errno code for failure
320  */
321 static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev,
322                 unsigned int timeout)
323 {
324         struct mei_device *dev;
325
326         dev = watchdog_get_drvdata(wd_dev);
327         if (!dev)
328                 return -ENODEV;
329
330         /* Check Timeout value */
331         if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT)
332                 return -EINVAL;
333
334         mutex_lock(&dev->device_lock);
335
336         dev->wd_timeout = timeout;
337         wd_dev->timeout = timeout;
338         mei_wd_set_start_timeout(dev, dev->wd_timeout);
339
340         mutex_unlock(&dev->device_lock);
341
342         return 0;
343 }
344
345 /*
346  * Watchdog Device structs
347  */
348 static const struct watchdog_ops wd_ops = {
349                 .owner = THIS_MODULE,
350                 .start = mei_wd_ops_start,
351                 .stop = mei_wd_ops_stop,
352                 .ping = mei_wd_ops_ping,
353                 .set_timeout = mei_wd_ops_set_timeout,
354 };
355 static const struct watchdog_info wd_info = {
356                 .identity = INTEL_AMT_WATCHDOG_ID,
357                 .options = WDIOF_KEEPALIVEPING |
358                            WDIOF_SETTIMEOUT |
359                            WDIOF_ALARMONLY,
360 };
361
362 static struct watchdog_device amt_wd_dev = {
363                 .info = &wd_info,
364                 .ops = &wd_ops,
365                 .timeout = MEI_WD_DEFAULT_TIMEOUT,
366                 .min_timeout = MEI_WD_MIN_TIMEOUT,
367                 .max_timeout = MEI_WD_MAX_TIMEOUT,
368 };
369
370
371 int mei_watchdog_register(struct mei_device *dev)
372 {
373
374         int ret;
375
376         /* unlock to perserve correct locking order */
377         mutex_unlock(&dev->device_lock);
378         ret = watchdog_register_device(&amt_wd_dev);
379         mutex_lock(&dev->device_lock);
380         if (ret) {
381                 dev_err(&dev->pdev->dev, "wd: unable to register watchdog device = %d.\n",
382                         ret);
383                 return ret;
384         }
385
386         dev_dbg(&dev->pdev->dev,
387                 "wd: successfully register watchdog interface.\n");
388         watchdog_set_drvdata(&amt_wd_dev, dev);
389         return 0;
390 }
391
392 void mei_watchdog_unregister(struct mei_device *dev)
393 {
394         if (watchdog_get_drvdata(&amt_wd_dev) == NULL)
395                 return;
396
397         watchdog_set_drvdata(&amt_wd_dev, NULL);
398         watchdog_unregister_device(&amt_wd_dev);
399 }
400