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 17 #include <linux/pci.h> 18 #include <linux/sched.h> 19 #include <linux/wait.h> 20 #include <linux/delay.h> 21 22 #include <linux/mei.h> 23 24 #include "mei_dev.h" 25 #include "hbm.h" 26 #include "client.h" 27 28 const char *mei_dev_state_str(int state) 29 { 30 #define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state 31 switch (state) { 32 MEI_DEV_STATE(INITIALIZING); 33 MEI_DEV_STATE(INIT_CLIENTS); 34 MEI_DEV_STATE(ENABLED); 35 MEI_DEV_STATE(RESETING); 36 MEI_DEV_STATE(DISABLED); 37 MEI_DEV_STATE(RECOVERING_FROM_RESET); 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 46 void mei_device_init(struct mei_device *dev) 47 { 48 /* setup our list array */ 49 INIT_LIST_HEAD(&dev->file_list); 50 mutex_init(&dev->device_lock); 51 init_waitqueue_head(&dev->wait_hw_ready); 52 init_waitqueue_head(&dev->wait_recvd_msg); 53 init_waitqueue_head(&dev->wait_stop_wd); 54 dev->dev_state = MEI_DEV_INITIALIZING; 55 56 mei_io_list_init(&dev->read_list); 57 mei_io_list_init(&dev->write_list); 58 mei_io_list_init(&dev->write_waiting_list); 59 mei_io_list_init(&dev->ctrl_wr_list); 60 mei_io_list_init(&dev->ctrl_rd_list); 61 } 62 63 /** 64 * mei_hw_init - initializes host and fw to start work. 65 * 66 * @dev: the device structure 67 * 68 * returns 0 on success, <0 on failure. 69 */ 70 int mei_hw_init(struct mei_device *dev) 71 { 72 int ret = 0; 73 74 mutex_lock(&dev->device_lock); 75 76 /* acknowledge interrupt and stop interupts */ 77 mei_clear_interrupts(dev); 78 79 mei_hw_config(dev); 80 81 dev->recvd_msg = false; 82 dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); 83 84 mei_reset(dev, 1); 85 86 /* wait for ME to turn on ME_RDY */ 87 if (!dev->recvd_msg) { 88 mutex_unlock(&dev->device_lock); 89 ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, 90 dev->recvd_msg, 91 mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); 92 mutex_lock(&dev->device_lock); 93 } 94 95 if (ret <= 0 && !dev->recvd_msg) { 96 dev->dev_state = MEI_DEV_DISABLED; 97 dev_dbg(&dev->pdev->dev, 98 "wait_event_interruptible_timeout failed" 99 "on wait for ME to turn on ME_RDY.\n"); 100 goto err; 101 } 102 103 104 if (!mei_host_is_ready(dev)) { 105 dev_err(&dev->pdev->dev, "host is not ready.\n"); 106 goto err; 107 } 108 109 if (!mei_hw_is_ready(dev)) { 110 dev_err(&dev->pdev->dev, "ME is not ready.\n"); 111 goto err; 112 } 113 114 if (dev->version.major_version != HBM_MAJOR_VERSION || 115 dev->version.minor_version != HBM_MINOR_VERSION) { 116 dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); 117 goto err; 118 } 119 120 dev->recvd_msg = false; 121 dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); 122 123 mutex_unlock(&dev->device_lock); 124 return 0; 125 err: 126 dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); 127 dev->dev_state = MEI_DEV_DISABLED; 128 mutex_unlock(&dev->device_lock); 129 return -ENODEV; 130 } 131 132 /** 133 * mei_reset - resets host and fw. 134 * 135 * @dev: the device structure 136 * @interrupts_enabled: if interrupt should be enabled after reset. 137 */ 138 void mei_reset(struct mei_device *dev, int interrupts_enabled) 139 { 140 bool unexpected; 141 142 if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) 143 return; 144 145 unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && 146 dev->dev_state != MEI_DEV_DISABLED && 147 dev->dev_state != MEI_DEV_POWER_DOWN && 148 dev->dev_state != MEI_DEV_POWER_UP); 149 150 mei_hw_reset(dev, interrupts_enabled); 151 152 153 if (dev->dev_state != MEI_DEV_INITIALIZING) { 154 if (dev->dev_state != MEI_DEV_DISABLED && 155 dev->dev_state != MEI_DEV_POWER_DOWN) 156 dev->dev_state = MEI_DEV_RESETING; 157 158 mei_cl_all_disconnect(dev); 159 160 /* remove entry if already in list */ 161 dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); 162 mei_cl_unlink(&dev->wd_cl); 163 if (dev->open_handle_count > 0) 164 dev->open_handle_count--; 165 mei_cl_unlink(&dev->iamthif_cl); 166 if (dev->open_handle_count > 0) 167 dev->open_handle_count--; 168 169 mei_amthif_reset_params(dev); 170 memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); 171 } 172 173 dev->me_clients_num = 0; 174 dev->rd_msg_hdr = 0; 175 dev->wd_pending = false; 176 177 if (unexpected) 178 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", 179 mei_dev_state_str(dev->dev_state)); 180 181 if (!interrupts_enabled) { 182 dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); 183 return; 184 } 185 186 mei_hw_start(dev); 187 188 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); 189 /* link is established * start sending messages. */ 190 191 dev->dev_state = MEI_DEV_INIT_CLIENTS; 192 193 mei_hbm_start_req(dev); 194 195 /* wake up all readings so they can be interrupted */ 196 mei_cl_all_read_wakeup(dev); 197 198 /* remove all waiting requests */ 199 mei_cl_all_write_clear(dev); 200 } 201 202 203 204 205