| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Intel Management Engine Interface (Intel MEI) Linux driver | 
					
						
							| 
									
										
										
										
											2012-02-09 19:25:53 +02:00
										 |  |  |  * Copyright (c) 2003-2012, Intel Corporation. | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms and conditions of the GNU General Public License, | 
					
						
							|  |  |  |  * version 2, as published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope it will be useful, but WITHOUT | 
					
						
							|  |  |  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
					
						
							|  |  |  |  * more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/moduleparam.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:09 +03:00
										 |  |  | #include <linux/watchdog.h>
 | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-25 19:06:03 +02:00
										 |  |  | #include <linux/mei.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | #include "mei_dev.h"
 | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:12 +02:00
										 |  |  | #include "hbm.h"
 | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:14 +02:00
										 |  |  | #include "client.h"
 | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; | 
					
						
							|  |  |  | static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-22 18:50:50 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * AMT Watchdog Device | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define INTEL_AMT_WATCHDOG_ID "INTCAMT"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | /* UUIDs for AMT F/W clients */ | 
					
						
							|  |  |  | const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, | 
					
						
							|  |  |  | 						0x9D, 0xA9, 0x15, 0x14, 0xCB, | 
					
						
							|  |  |  | 						0x32, 0xAB); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-19 17:58:43 +02:00
										 |  |  | static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 	dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 	memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); | 
					
						
							|  |  |  | 	memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2012-07-04 19:24:51 +03:00
										 |  |  |  * mei_wd_host_init - connect to the watchdog client | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @dev: the device structure | 
					
						
							| 
									
										
										
										
											2013-04-05 01:05:05 +09:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: -ENOTTY if wd client cannot be found | 
					
						
							| 
									
										
										
										
											2012-04-03 23:34:58 +03:00
										 |  |  |  *         -EIO if write has failed | 
					
						
							| 
									
										
										
										
											2012-07-04 19:24:51 +03:00
										 |  |  |  *         0 on success | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-04-03 23:34:58 +03:00
										 |  |  | int mei_wd_host_init(struct mei_device *dev) | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:22 +02:00
										 |  |  | 	struct mei_cl *cl = &dev->wd_cl; | 
					
						
							| 
									
										
										
										
											2014-08-24 12:08:55 +03:00
										 |  |  | 	struct mei_me_client *me_cl; | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:22 +02:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mei_cl_init(cl, dev); | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 	dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | 	dev->wd_state = MEI_WD_IDLE; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:22 +02:00
										 |  |  | 	/* check for valid client id */ | 
					
						
							| 
									
										
										
										
											2014-08-24 12:08:55 +03:00
										 |  |  | 	me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); | 
					
						
							|  |  |  | 	if (!me_cl) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_info(dev->dev, "wd: failed to find the client\n"); | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:49 +02:00
										 |  |  | 		return -ENOTTY; | 
					
						
							| 
									
										
										
										
											2012-04-03 23:34:58 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-24 12:08:55 +03:00
										 |  |  | 	cl->me_client_id = me_cl->client_id; | 
					
						
							| 
									
										
										
										
											2014-08-21 14:29:15 +03:00
										 |  |  | 	cl->cl_uuid = me_cl->props.protocol_name; | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_info(dev->dev, "wd: failed link client\n"); | 
					
						
							| 
									
										
										
										
											2013-10-21 22:05:39 +03:00
										 |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:22 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | 	ret = mei_cl_connect(cl, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_err(dev->dev, "wd: failed to connect = %d\n", ret); | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | 		mei_cl_unlink(cl); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:07 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | 	ret = mei_watchdog_register(dev); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		mei_cl_disconnect(cl); | 
					
						
							|  |  |  | 		mei_cl_unlink(cl); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * mei_wd_send - sends watch dog message to fw. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @dev: the device structure | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: 0 if success, | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  |  *	-EIO when message send fails | 
					
						
							|  |  |  |  *	-EINVAL when invalid message is to be sent | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  |  *	-ENODEV on flow control failure | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  |  */ | 
					
						
							|  |  |  | int mei_wd_send(struct mei_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 	struct mei_cl *cl = &dev->wd_cl; | 
					
						
							| 
									
										
										
										
											2012-12-25 19:06:10 +02:00
										 |  |  | 	struct mei_msg_hdr hdr; | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 	hdr.host_addr = cl->host_client_id; | 
					
						
							|  |  |  | 	hdr.me_addr = cl->me_client_id; | 
					
						
							| 
									
										
										
										
											2012-12-25 19:06:10 +02:00
										 |  |  | 	hdr.msg_complete = 1; | 
					
						
							|  |  |  | 	hdr.reserved = 0; | 
					
						
							| 
									
										
										
										
											2013-12-17 15:56:56 +02:00
										 |  |  | 	hdr.internal = 0; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 	if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) | 
					
						
							| 
									
										
										
										
											2012-12-25 19:06:10 +02:00
										 |  |  | 		hdr.length = MEI_WD_START_MSG_SIZE; | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 	else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) | 
					
						
							| 
									
										
										
										
											2012-12-25 19:06:10 +02:00
										 |  |  | 		hdr.length = MEI_WD_STOP_MSG_SIZE; | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 	else { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = mei_write_message(dev, &hdr, dev->wd_data); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_err(dev->dev, "wd: write message failed\n"); | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 	ret = mei_cl_flow_ctrl_reduce(cl); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:17 +03:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * mei_wd_stop - sends watchdog stop message to fw. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @dev: the device structure | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: 0 if success | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  |  * on error: | 
					
						
							|  |  |  |  *	-EIO    when message send fails | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:17 +03:00
										 |  |  |  *	-EINVAL when invalid message is to be sent | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  |  *	-ETIME  on message timeout | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:17 +03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | int mei_wd_stop(struct mei_device *dev) | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | 	if (dev->wd_cl.state != MEI_FILE_CONNECTED || | 
					
						
							|  |  |  | 	    dev->wd_state != MEI_WD_RUNNING) | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 	memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dev->wd_state = MEI_WD_STOPPING; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 23:07:14 +02:00
										 |  |  | 	ret = mei_cl_flow_ctrl_creds(&dev->wd_cl); | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	if (ret < 0) | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  | 		goto err; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:47 +02:00
										 |  |  | 	if (ret && mei_hbuf_acquire(dev)) { | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 		ret = mei_wd_send(dev); | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  | 			goto err; | 
					
						
							| 
									
										
										
										
											2011-05-25 17:28:22 +03:00
										 |  |  | 		dev->wd_pending = false; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2011-05-25 17:28:22 +03:00
										 |  |  | 		dev->wd_pending = true; | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	mutex_unlock(&dev->device_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  | 	ret = wait_event_timeout(dev->wait_stop_wd, | 
					
						
							|  |  |  | 				dev->wd_state == MEI_WD_IDLE, | 
					
						
							|  |  |  | 				msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	mutex_lock(&dev->device_lock); | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  | 	if (dev->wd_state != MEI_WD_IDLE) { | 
					
						
							|  |  |  | 		/* timeout */ | 
					
						
							|  |  |  | 		ret = -ETIME; | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  | 		goto err; | 
					
						
							| 
									
										
										
										
											2011-06-13 16:39:31 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 	dev_dbg(dev->dev, "wd: stop completed after %u msec\n", | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:51 +02:00
										 |  |  | 			MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | err: | 
					
						
							| 
									
										
										
										
											2011-05-15 13:43:47 +03:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * mei_wd_ops_start - wd start command from the watchdog core. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @wd_dev - watchdog device struct | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: 0 if success, negative errno code for failure | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int mei_wd_ops_start(struct watchdog_device *wd_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err = -ENODEV; | 
					
						
							|  |  |  | 	struct mei_device *dev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 	dev = watchdog_get_drvdata(wd_dev); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&dev->device_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-07 00:03:56 +03:00
										 |  |  | 	if (dev->dev_state != MEI_DEV_ENABLED) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED  dev_state = %s\n", | 
					
						
							| 
									
										
										
										
											2012-08-07 00:03:56 +03:00
										 |  |  | 			mei_dev_state_str(dev->dev_state)); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 		goto end_unlock; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->wd_cl.state != MEI_FILE_CONNECTED)	{ | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_dbg(dev->dev, "MEI Driver is not connected to Watchdog Client\n"); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 		goto end_unlock; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | 	mei_wd_set_start_timeout(dev, dev->wd_timeout); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = 0; | 
					
						
							|  |  |  | end_unlock: | 
					
						
							|  |  |  | 	mutex_unlock(&dev->device_lock); | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * mei_wd_ops_stop -  wd stop command from the watchdog core. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @wd_dev - watchdog device struct | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: 0 if success, negative errno code for failure | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int mei_wd_ops_stop(struct watchdog_device *wd_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct mei_device *dev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 	dev = watchdog_get_drvdata(wd_dev); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&dev->device_lock); | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | 	mei_wd_stop(dev); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 	mutex_unlock(&dev->device_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * mei_wd_ops_ping - wd ping command from the watchdog core. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @wd_dev - watchdog device struct | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: 0 if success, negative errno code for failure | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int mei_wd_ops_ping(struct watchdog_device *wd_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct mei_device *dev; | 
					
						
							| 
									
										
										
										
											2014-10-02 13:39:32 +03:00
										 |  |  | 	struct mei_cl *cl; | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:47 +02:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 	dev = watchdog_get_drvdata(wd_dev); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-02 13:39:32 +03:00
										 |  |  | 	cl = &dev->wd_cl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 	mutex_lock(&dev->device_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-02 13:39:32 +03:00
										 |  |  | 	if (cl->state != MEI_FILE_CONNECTED) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_err(dev->dev, "wd: not connected.\n"); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 		ret = -ENODEV; | 
					
						
							|  |  |  | 		goto end; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:43 +03:00
										 |  |  | 	dev->wd_state = MEI_WD_RUNNING; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-02 13:39:32 +03:00
										 |  |  | 	ret = mei_cl_flow_ctrl_creds(cl); | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:47 +02:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto end; | 
					
						
							| 
									
										
										
										
											2014-10-02 13:39:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 	/* Check if we can send the ping to HW*/ | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:47 +02:00
										 |  |  | 	if (ret && mei_hbuf_acquire(dev)) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_dbg(dev->dev, "wd: sending ping\n"); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 		ret = mei_wd_send(dev); | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 			goto end; | 
					
						
							| 
									
										
										
										
											2014-02-19 17:35:50 +02:00
										 |  |  | 		dev->wd_pending = false; | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		dev->wd_pending = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  | 	mutex_unlock(&dev->device_lock); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @wd_dev - watchdog device struct | 
					
						
							|  |  |  |  * @timeout - timeout value to set | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:49 +03:00
										 |  |  |  * Return: 0 if success, negative errno code for failure | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-04-13 23:41:25 +03:00
										 |  |  | static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, | 
					
						
							|  |  |  | 		unsigned int timeout) | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct mei_device *dev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 	dev = watchdog_get_drvdata(wd_dev); | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check Timeout value */ | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 	if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&dev->device_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->wd_timeout = timeout; | 
					
						
							| 
									
										
										
										
											2012-02-29 20:20:58 +01:00
										 |  |  | 	wd_dev->timeout = timeout; | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | 	mei_wd_set_start_timeout(dev, dev->wd_timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_unlock(&dev->device_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Watchdog Device structs | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-10-23 18:30:39 +02:00
										 |  |  | static const struct watchdog_ops wd_ops = { | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 		.owner = THIS_MODULE, | 
					
						
							|  |  |  | 		.start = mei_wd_ops_start, | 
					
						
							|  |  |  | 		.stop = mei_wd_ops_stop, | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:11 +03:00
										 |  |  | 		.ping = mei_wd_ops_ping, | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:12 +03:00
										 |  |  | 		.set_timeout = mei_wd_ops_set_timeout, | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-10-23 18:30:39 +02:00
										 |  |  | static const struct watchdog_info wd_info = { | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 		.identity = INTEL_AMT_WATCHDOG_ID, | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:41 +03:00
										 |  |  | 		.options = WDIOF_KEEPALIVEPING | | 
					
						
							|  |  |  | 			   WDIOF_SETTIMEOUT | | 
					
						
							|  |  |  | 			   WDIOF_ALARMONLY, | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-02 20:32:38 +03:00
										 |  |  | static struct watchdog_device amt_wd_dev = { | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | 		.info = &wd_info, | 
					
						
							|  |  |  | 		.ops = &wd_ops, | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:42 +03:00
										 |  |  | 		.timeout = MEI_WD_DEFAULT_TIMEOUT, | 
					
						
							|  |  |  | 		.min_timeout = MEI_WD_MIN_TIMEOUT, | 
					
						
							|  |  |  | 		.max_timeout = MEI_WD_MAX_TIMEOUT, | 
					
						
							| 
									
										
										
										
											2011-09-07 09:03:10 +03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | int mei_watchdog_register(struct mei_device *dev) | 
					
						
							| 
									
										
										
										
											2011-12-22 18:50:50 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* unlock to perserve correct locking order */ | 
					
						
							|  |  |  | 	mutex_unlock(&dev->device_lock); | 
					
						
							|  |  |  | 	ret = watchdog_register_device(&amt_wd_dev); | 
					
						
							|  |  |  | 	mutex_lock(&dev->device_lock); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 		dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | 			ret); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2011-12-22 18:50:50 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 16:31:42 +03:00
										 |  |  | 	dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 	watchdog_set_drvdata(&amt_wd_dev, dev); | 
					
						
							| 
									
										
										
										
											2014-02-17 15:13:21 +02:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2011-12-22 18:50:50 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void mei_watchdog_unregister(struct mei_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-16 13:23:17 +02:00
										 |  |  | 	if (watchdog_get_drvdata(&amt_wd_dev) == NULL) | 
					
						
							| 
									
										
										
										
											2012-08-16 19:39:44 +03:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	watchdog_set_drvdata(&amt_wd_dev, NULL); | 
					
						
							|  |  |  | 	watchdog_unregister_device(&amt_wd_dev); | 
					
						
							| 
									
										
										
										
											2011-12-22 18:50:50 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 |