| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 1 | /* | 
 | 2 |  * | 
 | 3 |  * Intel Management Engine Interface (Intel MEI) Linux driver | 
| Tomas Winkler | 733ba91 | 2012-02-09 19:25:53 +0200 | [diff] [blame] | 4 |  * Copyright (c) 2003-2012, Intel Corporation. | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 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 |  | 
| Tomas Winkler | 40e0b67 | 2013-03-27 16:58:30 +0200 | [diff] [blame] | 17 | #include <linux/export.h> | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 18 | #include <linux/pci.h> | 
 | 19 | #include <linux/sched.h> | 
 | 20 | #include <linux/wait.h> | 
 | 21 | #include <linux/delay.h> | 
 | 22 |  | 
| Tomas Winkler | 4f3afe1 | 2012-05-09 16:38:59 +0300 | [diff] [blame] | 23 | #include <linux/mei.h> | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 24 |  | 
| Tomas Winkler | 47a7380 | 2012-12-25 19:06:03 +0200 | [diff] [blame] | 25 | #include "mei_dev.h" | 
| Tomas Winkler | aafae7e | 2013-03-11 18:27:03 +0200 | [diff] [blame] | 26 | #include "hbm.h" | 
| Tomas Winkler | 90e0b5f | 2013-01-08 23:07:14 +0200 | [diff] [blame] | 27 | #include "client.h" | 
| Tomas Winkler | 47a7380 | 2012-12-25 19:06:03 +0200 | [diff] [blame] | 28 |  | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 29 | const char *mei_dev_state_str(int state) | 
 | 30 | { | 
 | 31 | #define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state | 
 | 32 | 	switch (state) { | 
 | 33 | 	MEI_DEV_STATE(INITIALIZING); | 
 | 34 | 	MEI_DEV_STATE(INIT_CLIENTS); | 
 | 35 | 	MEI_DEV_STATE(ENABLED); | 
| Bill Nottingham | 0cfee51 | 2013-04-19 22:01:36 +0300 | [diff] [blame] | 36 | 	MEI_DEV_STATE(RESETTING); | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 37 | 	MEI_DEV_STATE(DISABLED); | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 38 | 	MEI_DEV_STATE(POWER_DOWN); | 
 | 39 | 	MEI_DEV_STATE(POWER_UP); | 
 | 40 | 	default: | 
 | 41 | 		return "unkown"; | 
 | 42 | 	} | 
 | 43 | #undef MEI_DEV_STATE | 
 | 44 | } | 
 | 45 |  | 
| Tomas Winkler | 52c3456 | 2013-02-06 14:06:40 +0200 | [diff] [blame] | 46 | void mei_device_init(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 47 | { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 48 | 	/* setup our list array */ | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 49 | 	INIT_LIST_HEAD(&dev->file_list); | 
| Samuel Ortiz | a7b71bc | 2013-03-27 17:29:56 +0200 | [diff] [blame] | 50 | 	INIT_LIST_HEAD(&dev->device_list); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 51 | 	mutex_init(&dev->device_lock); | 
| Tomas Winkler | aafae7e | 2013-03-11 18:27:03 +0200 | [diff] [blame] | 52 | 	init_waitqueue_head(&dev->wait_hw_ready); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 53 | 	init_waitqueue_head(&dev->wait_recvd_msg); | 
 | 54 | 	init_waitqueue_head(&dev->wait_stop_wd); | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 55 | 	dev->dev_state = MEI_DEV_INITIALIZING; | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 56 |  | 
 | 57 | 	mei_io_list_init(&dev->read_list); | 
 | 58 | 	mei_io_list_init(&dev->write_list); | 
 | 59 | 	mei_io_list_init(&dev->write_waiting_list); | 
 | 60 | 	mei_io_list_init(&dev->ctrl_wr_list); | 
 | 61 | 	mei_io_list_init(&dev->ctrl_rd_list); | 
| Tomas Winkler | d0265f1 | 2013-03-27 16:58:27 +0200 | [diff] [blame] | 62 |  | 
 | 63 | 	INIT_DELAYED_WORK(&dev->timer_work, mei_timer); | 
 | 64 | 	INIT_WORK(&dev->init_work, mei_host_client_init); | 
 | 65 |  | 
 | 66 | 	INIT_LIST_HEAD(&dev->wd_cl.link); | 
 | 67 | 	INIT_LIST_HEAD(&dev->iamthif_cl.link); | 
 | 68 | 	mei_io_list_init(&dev->amthif_cmd_list); | 
 | 69 | 	mei_io_list_init(&dev->amthif_rd_complete_list); | 
 | 70 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 71 | } | 
| Tomas Winkler | 40e0b67 | 2013-03-27 16:58:30 +0200 | [diff] [blame] | 72 | EXPORT_SYMBOL_GPL(mei_device_init); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 73 |  | 
 | 74 | /** | 
| Tomas Winkler | c4d589b | 2013-03-27 16:58:28 +0200 | [diff] [blame] | 75 |  * mei_start - initializes host and fw to start work. | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 76 |  * | 
 | 77 |  * @dev: the device structure | 
 | 78 |  * | 
 | 79 |  * returns 0 on success, <0 on failure. | 
 | 80 |  */ | 
| Tomas Winkler | c4d589b | 2013-03-27 16:58:28 +0200 | [diff] [blame] | 81 | int mei_start(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 82 | { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 83 | 	mutex_lock(&dev->device_lock); | 
 | 84 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 85 | 	/* acknowledge interrupt and stop interupts */ | 
| Tomas Winkler | 3a65dd4 | 2012-12-25 19:06:06 +0200 | [diff] [blame] | 86 | 	mei_clear_interrupts(dev); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 87 |  | 
| Tomas Winkler | e7e0c23 | 2013-01-08 23:07:31 +0200 | [diff] [blame] | 88 | 	mei_hw_config(dev); | 
| Tomas Winkler | 24aadc8 | 2012-06-25 23:46:27 +0300 | [diff] [blame] | 89 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 90 | 	dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); | 
 | 91 |  | 
 | 92 | 	mei_reset(dev, 1); | 
 | 93 |  | 
| Tomas Winkler | 9b0d5ef | 2013-04-18 23:03:48 +0300 | [diff] [blame] | 94 | 	if (mei_hbm_start_wait(dev)) { | 
 | 95 | 		dev_err(&dev->pdev->dev, "HBM haven't started"); | 
| Tomas Winkler | e7e0c23 | 2013-01-08 23:07:31 +0200 | [diff] [blame] | 96 | 		goto err; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 97 | 	} | 
 | 98 |  | 
| Tomas Winkler | e7e0c23 | 2013-01-08 23:07:31 +0200 | [diff] [blame] | 99 | 	if (!mei_host_is_ready(dev)) { | 
 | 100 | 		dev_err(&dev->pdev->dev, "host is not ready.\n"); | 
 | 101 | 		goto err; | 
 | 102 | 	} | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 103 |  | 
| Tomas Winkler | 827eef5 | 2013-02-06 14:06:41 +0200 | [diff] [blame] | 104 | 	if (!mei_hw_is_ready(dev)) { | 
| Tomas Winkler | e7e0c23 | 2013-01-08 23:07:31 +0200 | [diff] [blame] | 105 | 		dev_err(&dev->pdev->dev, "ME is not ready.\n"); | 
 | 106 | 		goto err; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 107 | 	} | 
 | 108 |  | 
 | 109 | 	if (dev->version.major_version != HBM_MAJOR_VERSION || | 
 | 110 | 	    dev->version.minor_version != HBM_MINOR_VERSION) { | 
 | 111 | 		dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); | 
| Tomas Winkler | e7e0c23 | 2013-01-08 23:07:31 +0200 | [diff] [blame] | 112 | 		goto err; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 113 | 	} | 
 | 114 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 115 | 	dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 116 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 117 | 	mutex_unlock(&dev->device_lock); | 
| Tomas Winkler | e7e0c23 | 2013-01-08 23:07:31 +0200 | [diff] [blame] | 118 | 	return 0; | 
 | 119 | err: | 
 | 120 | 	dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); | 
 | 121 | 	dev->dev_state = MEI_DEV_DISABLED; | 
 | 122 | 	mutex_unlock(&dev->device_lock); | 
 | 123 | 	return -ENODEV; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 124 | } | 
| Tomas Winkler | 40e0b67 | 2013-03-27 16:58:30 +0200 | [diff] [blame] | 125 | EXPORT_SYMBOL_GPL(mei_start); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 126 |  | 
 | 127 | /** | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 128 |  * mei_reset - resets host and fw. | 
 | 129 |  * | 
 | 130 |  * @dev: the device structure | 
 | 131 |  * @interrupts_enabled: if interrupt should be enabled after reset. | 
 | 132 |  */ | 
 | 133 | void mei_reset(struct mei_device *dev, int interrupts_enabled) | 
 | 134 | { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 135 | 	bool unexpected; | 
 | 136 |  | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 137 | 	unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && | 
 | 138 | 			dev->dev_state != MEI_DEV_DISABLED && | 
 | 139 | 			dev->dev_state != MEI_DEV_POWER_DOWN && | 
 | 140 | 			dev->dev_state != MEI_DEV_POWER_UP); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 141 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 142 | 	mei_hw_reset(dev, interrupts_enabled); | 
 | 143 |  | 
| Tomas Winkler | 9b0d5ef | 2013-04-18 23:03:48 +0300 | [diff] [blame] | 144 | 	dev->hbm_state = MEI_HBM_IDLE; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 145 |  | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 146 | 	if (dev->dev_state != MEI_DEV_INITIALIZING) { | 
 | 147 | 		if (dev->dev_state != MEI_DEV_DISABLED && | 
 | 148 | 		    dev->dev_state != MEI_DEV_POWER_DOWN) | 
| Bill Nottingham | 0cfee51 | 2013-04-19 22:01:36 +0300 | [diff] [blame] | 149 | 			dev->dev_state = MEI_DEV_RESETTING; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 150 |  | 
| Tomas Winkler | 074b4c0 | 2013-02-06 14:06:44 +0200 | [diff] [blame] | 151 | 		mei_cl_all_disconnect(dev); | 
 | 152 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 153 | 		/* remove entry if already in list */ | 
| Tomas Winkler | ff8b2f4 | 2012-11-11 17:38:03 +0200 | [diff] [blame] | 154 | 		dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); | 
| Tomas Winkler | 90e0b5f | 2013-01-08 23:07:14 +0200 | [diff] [blame] | 155 | 		mei_cl_unlink(&dev->wd_cl); | 
| Tomas Winkler | 781d0d8 | 2013-01-08 23:07:22 +0200 | [diff] [blame] | 156 | 		if (dev->open_handle_count > 0) | 
 | 157 | 			dev->open_handle_count--; | 
| Tomas Winkler | 90e0b5f | 2013-01-08 23:07:14 +0200 | [diff] [blame] | 158 | 		mei_cl_unlink(&dev->iamthif_cl); | 
| Tomas Winkler | 781d0d8 | 2013-01-08 23:07:22 +0200 | [diff] [blame] | 159 | 		if (dev->open_handle_count > 0) | 
 | 160 | 			dev->open_handle_count--; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 161 |  | 
| Tomas Winkler | 19838fb | 2012-11-01 21:17:15 +0200 | [diff] [blame] | 162 | 		mei_amthif_reset_params(dev); | 
| Tomas Winkler | 5fb54fb | 2012-11-18 15:13:15 +0200 | [diff] [blame] | 163 | 		memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 164 | 	} | 
 | 165 |  | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 166 | 	dev->me_clients_num = 0; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 167 | 	dev->rd_msg_hdr = 0; | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 168 | 	dev->wd_pending = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 169 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 170 | 	if (unexpected) | 
| Tomas Winkler | b210d75 | 2012-08-07 00:03:56 +0300 | [diff] [blame] | 171 | 		dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", | 
 | 172 | 			 mei_dev_state_str(dev->dev_state)); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 173 |  | 
| Tomas Winkler | aafae7e | 2013-03-11 18:27:03 +0200 | [diff] [blame] | 174 | 	if (!interrupts_enabled) { | 
 | 175 | 		dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); | 
 | 176 | 		return; | 
 | 177 | 	} | 
 | 178 |  | 
 | 179 | 	mei_hw_start(dev); | 
 | 180 |  | 
 | 181 | 	dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); | 
 | 182 | 	/* link is established * start sending messages.  */ | 
 | 183 |  | 
 | 184 | 	dev->dev_state = MEI_DEV_INIT_CLIENTS; | 
 | 185 |  | 
 | 186 | 	mei_hbm_start_req(dev); | 
 | 187 |  | 
| Tomas Winkler | 074b4c0 | 2013-02-06 14:06:44 +0200 | [diff] [blame] | 188 | 	/* wake up all readings so they can be interrupted */ | 
 | 189 | 	mei_cl_all_read_wakeup(dev); | 
 | 190 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 191 | 	/* remove all waiting requests */ | 
| Tomas Winkler | 074b4c0 | 2013-02-06 14:06:44 +0200 | [diff] [blame] | 192 | 	mei_cl_all_write_clear(dev); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 193 | } | 
| Tomas Winkler | 40e0b67 | 2013-03-27 16:58:30 +0200 | [diff] [blame] | 194 | EXPORT_SYMBOL_GPL(mei_reset); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 195 |  | 
| Tomas Winkler | 7cb035d | 2013-03-10 13:56:08 +0200 | [diff] [blame] | 196 | void mei_stop(struct mei_device *dev) | 
 | 197 | { | 
 | 198 | 	dev_dbg(&dev->pdev->dev, "stopping the device.\n"); | 
 | 199 |  | 
 | 200 | 	mutex_lock(&dev->device_lock); | 
 | 201 |  | 
 | 202 | 	cancel_delayed_work(&dev->timer_work); | 
 | 203 |  | 
 | 204 | 	mei_wd_stop(dev); | 
 | 205 |  | 
| Samuel Ortiz | 59fcd7c | 2013-04-11 03:03:29 +0200 | [diff] [blame] | 206 | 	mei_nfc_host_exit(); | 
 | 207 |  | 
| Tomas Winkler | 7cb035d | 2013-03-10 13:56:08 +0200 | [diff] [blame] | 208 | 	dev->dev_state = MEI_DEV_POWER_DOWN; | 
 | 209 | 	mei_reset(dev, 0); | 
 | 210 |  | 
 | 211 | 	mutex_unlock(&dev->device_lock); | 
 | 212 |  | 
 | 213 | 	flush_scheduled_work(); | 
| Tomas Winkler | 2e64712 | 2013-03-27 16:58:26 +0200 | [diff] [blame] | 214 |  | 
 | 215 | 	mei_watchdog_unregister(dev); | 
| Tomas Winkler | 7cb035d | 2013-03-10 13:56:08 +0200 | [diff] [blame] | 216 | } | 
| Tomas Winkler | 40e0b67 | 2013-03-27 16:58:30 +0200 | [diff] [blame] | 217 | EXPORT_SYMBOL_GPL(mei_stop); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 218 |  | 
| Samuel Ortiz | c1174c0 | 2012-11-18 15:13:20 +0200 | [diff] [blame] | 219 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 220 |  |