init.c 7.5 KB
Newer Older
O
Oren Weil 已提交
1 2 3
/*
 *
 * Intel Management Engine Interface (Intel MEI) Linux driver
4
 * Copyright (c) 2003-2012, Intel Corporation.
O
Oren Weil 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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/pci.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>

22 23
#include <linux/mei.h>

O
Oren Weil 已提交
24
#include "mei_dev.h"
T
Tomas Winkler 已提交
25
#include "client.h"
O
Oren Weil 已提交
26

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
const char *mei_dev_state_str(int state)
{
#define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
	switch (state) {
	MEI_DEV_STATE(INITIALIZING);
	MEI_DEV_STATE(INIT_CLIENTS);
	MEI_DEV_STATE(ENABLED);
	MEI_DEV_STATE(RESETING);
	MEI_DEV_STATE(DISABLED);
	MEI_DEV_STATE(RECOVERING_FROM_RESET);
	MEI_DEV_STATE(POWER_DOWN);
	MEI_DEV_STATE(POWER_UP);
	default:
		return "unkown";
	}
#undef MEI_DEV_STATE
}


46

O
Oren Weil 已提交
47 48 49 50 51 52 53 54

/**
 * init_mei_device - allocates and initializes the mei device structure
 *
 * @pdev: The pci device structure
 *
 * returns The mei_device_device pointer on success, NULL on failure.
 */
55
struct mei_device *mei_device_init(struct pci_dev *pdev)
O
Oren Weil 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69
{
	struct mei_device *dev;

	dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL);
	if (!dev)
		return NULL;

	/* setup our list array */
	INIT_LIST_HEAD(&dev->file_list);
	INIT_LIST_HEAD(&dev->wd_cl.link);
	INIT_LIST_HEAD(&dev->iamthif_cl.link);
	mutex_init(&dev->device_lock);
	init_waitqueue_head(&dev->wait_recvd_msg);
	init_waitqueue_head(&dev->wait_stop_wd);
70
	dev->dev_state = MEI_DEV_INITIALIZING;
71 72 73 74 75 76

	mei_io_list_init(&dev->read_list);
	mei_io_list_init(&dev->write_list);
	mei_io_list_init(&dev->write_waiting_list);
	mei_io_list_init(&dev->ctrl_wr_list);
	mei_io_list_init(&dev->ctrl_rd_list);
77 78
	mei_io_list_init(&dev->amthif_cmd_list);
	mei_io_list_init(&dev->amthif_rd_complete_list);
O
Oren Weil 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	dev->pdev = pdev;
	return dev;
}

/**
 * mei_hw_init - initializes host and fw to start work.
 *
 * @dev: the device structure
 *
 * returns 0 on success, <0 on failure.
 */
int mei_hw_init(struct mei_device *dev)
{
	int err = 0;
	int ret;

	mutex_lock(&dev->device_lock);

	dev->host_hw_state = mei_hcsr_read(dev);
	dev->me_hw_state = mei_mecsr_read(dev);
	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, mestate = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);

	/* acknowledge interrupt and stop interupts */
103
	mei_clear_interrupts(dev);
O
Oren Weil 已提交
104

105 106 107
	/* Doesn't change in runtime */
	dev->hbuf_depth = (dev->host_hw_state & H_CBD) >> 24;

108
	dev->recvd_msg = false;
O
Oren Weil 已提交
109 110 111 112 113 114 115 116 117 118 119
	dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");

	mei_reset(dev, 1);

	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);

	/* wait for ME to turn on ME_RDY */
	if (!dev->recvd_msg) {
		mutex_unlock(&dev->device_lock);
		err = wait_event_interruptible_timeout(dev->wait_recvd_msg,
120 121
			dev->recvd_msg,
			mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
O
Oren Weil 已提交
122 123 124
		mutex_lock(&dev->device_lock);
	}

125
	if (err <= 0 && !dev->recvd_msg) {
126
		dev->dev_state = MEI_DEV_DISABLED;
O
Oren Weil 已提交
127 128 129 130 131 132 133 134 135
		dev_dbg(&dev->pdev->dev,
			"wait_event_interruptible_timeout failed"
			"on wait for ME to turn on ME_RDY.\n");
		ret = -ENODEV;
		goto out;
	}

	if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
	      ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
136
		dev->dev_state = MEI_DEV_DISABLED;
O
Oren Weil 已提交
137 138 139 140
		dev_dbg(&dev->pdev->dev,
			"host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
			dev->host_hw_state, dev->me_hw_state);

D
Dan Carpenter 已提交
141
		if (!(dev->host_hw_state & H_RDY))
O
Oren Weil 已提交
142 143
			dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n");

D
Dan Carpenter 已提交
144
		if (!(dev->me_hw_state & ME_RDY_HRA))
O
Oren Weil 已提交
145 146
			dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n");

147
		dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
O
Oren Weil 已提交
148 149 150 151 152 153 154 155 156 157 158
		ret = -ENODEV;
		goto out;
	}

	if (dev->version.major_version != HBM_MAJOR_VERSION ||
	    dev->version.minor_version != HBM_MINOR_VERSION) {
		dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
		ret = -ENODEV;
		goto out;
	}

159
	dev->recvd_msg = false;
O
Oren Weil 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);
	dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n");
	dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
	dev_dbg(&dev->pdev->dev, "MEI  start success.\n");
	ret = 0;

out:
	mutex_unlock(&dev->device_lock);
	return ret;
}

/**
 * mei_hw_reset - resets fw via mei csr register.
 *
 * @dev: the device structure
 * @interrupts_enabled: if interrupt should be enabled after reset.
 */
static void mei_hw_reset(struct mei_device *dev, int interrupts_enabled)
{
	dev->host_hw_state |= (H_RST | H_IG);

	if (interrupts_enabled)
		mei_enable_interrupts(dev);
	else
		mei_disable_interrupts(dev);
}

/**
 * mei_reset - resets host and fw.
 *
 * @dev: the device structure
 * @interrupts_enabled: if interrupt should be enabled after reset.
 */
void mei_reset(struct mei_device *dev, int interrupts_enabled)
{
	struct mei_cl *cl_pos = NULL;
	struct mei_cl *cl_next = NULL;
	struct mei_cl_cb *cb_pos = NULL;
	struct mei_cl_cb *cb_next = NULL;
	bool unexpected;

202
	if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
203
		dev->need_reset = true;
O
Oren Weil 已提交
204 205 206
		return;
	}

207 208 209 210
	unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
			dev->dev_state != MEI_DEV_DISABLED &&
			dev->dev_state != MEI_DEV_POWER_DOWN &&
			dev->dev_state != MEI_DEV_POWER_UP);
O
Oren Weil 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

	dev->host_hw_state = mei_hcsr_read(dev);

	dev_dbg(&dev->pdev->dev, "before reset host_hw_state = 0x%08x.\n",
	    dev->host_hw_state);

	mei_hw_reset(dev, interrupts_enabled);

	dev->host_hw_state &= ~H_RST;
	dev->host_hw_state |= H_IG;

	mei_hcsr_set(dev);

	dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n",
	    dev->host_hw_state);

227
	dev->need_reset = false;
O
Oren Weil 已提交
228

229 230 231 232
	if (dev->dev_state != MEI_DEV_INITIALIZING) {
		if (dev->dev_state != MEI_DEV_DISABLED &&
		    dev->dev_state != MEI_DEV_POWER_DOWN)
			dev->dev_state = MEI_DEV_RESETING;
O
Oren Weil 已提交
233 234 235 236 237 238 239 240 241

		list_for_each_entry_safe(cl_pos,
				cl_next, &dev->file_list, link) {
			cl_pos->state = MEI_FILE_DISCONNECTED;
			cl_pos->mei_flow_ctrl_creds = 0;
			cl_pos->read_cb = NULL;
			cl_pos->timer_count = 0;
		}
		/* remove entry if already in list */
242
		dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
T
Tomas Winkler 已提交
243
		mei_cl_unlink(&dev->wd_cl);
244 245
		if (dev->open_handle_count > 0)
			dev->open_handle_count--;
T
Tomas Winkler 已提交
246
		mei_cl_unlink(&dev->iamthif_cl);
247 248
		if (dev->open_handle_count > 0)
			dev->open_handle_count--;
O
Oren Weil 已提交
249

250
		mei_amthif_reset_params(dev);
251
		memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
O
Oren Weil 已提交
252 253
	}

254
	dev->me_clients_num = 0;
O
Oren Weil 已提交
255
	dev->rd_msg_hdr = 0;
256
	dev->wd_pending = false;
O
Oren Weil 已提交
257 258 259 260 261 262 263 264 265

	/* update the state of the registers after reset */
	dev->host_hw_state = mei_hcsr_read(dev);
	dev->me_hw_state = mei_mecsr_read(dev);

	dev_dbg(&dev->pdev->dev, "after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);

	if (unexpected)
266 267
		dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
			 mei_dev_state_str(dev->dev_state));
O
Oren Weil 已提交
268 269 270 271 272 273 274 275 276

	/* Wake up all readings so they can be interrupted */
	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
		if (waitqueue_active(&cl_pos->rx_wait)) {
			dev_dbg(&dev->pdev->dev, "Waking up client!\n");
			wake_up_interruptible(&cl_pos->rx_wait);
		}
	}
	/* remove all waiting requests */
277 278
	list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) {
		list_del(&cb_pos->list);
279
		mei_io_cb_free(cb_pos);
O
Oren Weil 已提交
280 281 282 283
	}
}


284

O
Oren Weil 已提交
285