init.c 9.4 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
 *
 * 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.
 *
 */

17
#include <linux/export.h>
O
Oren Weil 已提交
18 19 20 21 22
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>

23 24
#include <linux/mei.h>

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

29 30 31 32 33 34 35
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);
B
Bill Nottingham 已提交
36
	MEI_DEV_STATE(RESETTING);
37 38 39 40
	MEI_DEV_STATE(DISABLED);
	MEI_DEV_STATE(POWER_DOWN);
	MEI_DEV_STATE(POWER_UP);
	default:
M
Masanari Iida 已提交
41
		return "unknown";
42 43 44 45
	}
#undef MEI_DEV_STATE
}

46 47 48 49 50 51 52 53 54 55 56 57
const char *mei_pg_state_str(enum mei_pg_state state)
{
#define MEI_PG_STATE(state) case MEI_PG_##state: return #state
	switch (state) {
	MEI_PG_STATE(OFF);
	MEI_PG_STATE(ON);
	default:
		return "unknown";
	}
#undef MEI_PG_STATE
}

O
Oren Weil 已提交
58

59 60 61 62
/**
 * mei_cancel_work. Cancel mei background jobs
 *
 * @dev: the device structure
63 64
 *
 * returns 0 on success or < 0 if the reset hasn't succeeded
65
 */
T
Tomas Winkler 已提交
66 67 68
void mei_cancel_work(struct mei_device *dev)
{
	cancel_work_sync(&dev->init_work);
69
	cancel_work_sync(&dev->reset_work);
T
Tomas Winkler 已提交
70 71 72 73 74

	cancel_delayed_work(&dev->timer_work);
}
EXPORT_SYMBOL_GPL(mei_cancel_work);

O
Oren Weil 已提交
75 76 77 78 79
/**
 * mei_reset - resets host and fw.
 *
 * @dev: the device structure
 */
80
int mei_reset(struct mei_device *dev)
O
Oren Weil 已提交
81
{
82 83
	enum mei_dev_state state = dev->dev_state;
	bool interrupts_enabled;
84
	int ret;
O
Oren Weil 已提交
85

86 87 88
	if (state != MEI_DEV_INITIALIZING &&
	    state != MEI_DEV_DISABLED &&
	    state != MEI_DEV_POWER_DOWN &&
89 90
	    state != MEI_DEV_POWER_UP) {
		struct mei_fw_status fw_status;
91

92 93 94 95 96
		mei_fw_status(dev, &fw_status);
		dev_warn(&dev->pdev->dev,
			"unexpected reset: dev_state = %s " FW_STS_FMT "\n",
			mei_dev_state_str(state), FW_STS_PRM(fw_status));
	}
97

98 99 100 101 102 103 104
	/* we're already in reset, cancel the init timer
	 * if the reset was called due the hbm protocol error
	 * we need to call it before hw start
	 * so the hbm watchdog won't kick in
	 */
	mei_hbm_idle(dev);

105 106 107
	/* enter reset flow */
	interrupts_enabled = state != MEI_DEV_POWER_DOWN;
	dev->dev_state = MEI_DEV_RESETTING;
O
Oren Weil 已提交
108

109 110 111 112 113 114 115
	dev->reset_count++;
	if (dev->reset_count > MEI_MAX_CONSEC_RESET) {
		dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
		dev->dev_state = MEI_DEV_DISABLED;
		return -ENODEV;
	}

116 117
	ret = mei_hw_reset(dev, interrupts_enabled);
	/* fall through and remove the sw state even if hw reset has failed */
O
Oren Weil 已提交
118

119 120 121
	/* no need to clean up software state in case of power up */
	if (state != MEI_DEV_INITIALIZING &&
	    state != MEI_DEV_POWER_UP) {
O
Oren Weil 已提交
122

123 124 125
		/* remove all waiting requests */
		mei_cl_all_write_clear(dev);

126 127
		mei_cl_all_disconnect(dev);

128
		/* wake up all readers and writers so they can be interrupted */
129 130
		mei_cl_all_wakeup(dev);

O
Oren Weil 已提交
131
		/* remove entry if already in list */
132
		dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
T
Tomas Winkler 已提交
133 134
		mei_cl_unlink(&dev->wd_cl);
		mei_cl_unlink(&dev->iamthif_cl);
135
		mei_amthif_reset_params(dev);
O
Oren Weil 已提交
136 137
	}

138
	mei_hbm_reset(dev);
139

O
Oren Weil 已提交
140
	dev->rd_msg_hdr = 0;
141
	dev->wd_pending = false;
O
Oren Weil 已提交
142

143 144 145 146 147 148 149 150 151
	if (ret) {
		dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret);
		return ret;
	}

	if (state == MEI_DEV_POWER_DOWN) {
		dev_dbg(&dev->pdev->dev, "powering down: end of reset\n");
		dev->dev_state = MEI_DEV_DISABLED;
		return 0;
T
Tomas Winkler 已提交
152 153
	}

154 155
	ret = mei_hw_start(dev);
	if (ret) {
156 157
		dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
		return ret;
158
	}
T
Tomas Winkler 已提交
159 160 161 162

	dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");

	dev->dev_state = MEI_DEV_INIT_CLIENTS;
163 164
	ret = mei_hbm_start_req(dev);
	if (ret) {
165
		dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
166
		dev->dev_state = MEI_DEV_RESETTING;
167
		return ret;
168
	}
169 170

	return 0;
O
Oren Weil 已提交
171
}
172
EXPORT_SYMBOL_GPL(mei_reset);
O
Oren Weil 已提交
173

174 175 176 177 178 179 180 181 182
/**
 * mei_start - initializes host and fw to start work.
 *
 * @dev: the device structure
 *
 * returns 0 on success, <0 on failure.
 */
int mei_start(struct mei_device *dev)
{
183
	int ret;
184

185 186 187 188 189 190 191 192 193
	mutex_lock(&dev->device_lock);

	/* acknowledge interrupt and stop interrupts */
	mei_clear_interrupts(dev);

	mei_hw_config(dev);

	dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");

194
	dev->reset_count = 0;
195 196 197 198 199 200 201 202 203
	do {
		dev->dev_state = MEI_DEV_INITIALIZING;
		ret = mei_reset(dev);

		if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
			dev_err(&dev->pdev->dev, "reset failed ret = %d", ret);
			goto err;
		}
	} while (ret);
204

205
	/* we cannot start the device w/o hbm start message completed */
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	if (dev->dev_state == MEI_DEV_DISABLED) {
		dev_err(&dev->pdev->dev, "reset failed");
		goto err;
	}

	if (mei_hbm_start_wait(dev)) {
		dev_err(&dev->pdev->dev, "HBM haven't started");
		goto err;
	}

	if (!mei_host_is_ready(dev)) {
		dev_err(&dev->pdev->dev, "host is not ready.\n");
		goto err;
	}

	if (!mei_hw_is_ready(dev)) {
		dev_err(&dev->pdev->dev, "ME is not ready.\n");
		goto err;
	}

	if (!mei_hbm_version_is_supported(dev)) {
		dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
		goto err;
	}

	dev_dbg(&dev->pdev->dev, "link layer has been established.\n");

	mutex_unlock(&dev->device_lock);
	return 0;
err:
	dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
	dev->dev_state = MEI_DEV_DISABLED;
	mutex_unlock(&dev->device_lock);
	return -ENODEV;
}
EXPORT_SYMBOL_GPL(mei_start);

/**
 * mei_restart - restart device after suspend
 *
 * @dev: the device structure
 *
 * returns 0 on success or -ENODEV if the restart hasn't succeeded
 */
int mei_restart(struct mei_device *dev)
{
	int err;

	mutex_lock(&dev->device_lock);

	mei_clear_interrupts(dev);

	dev->dev_state = MEI_DEV_POWER_UP;
259
	dev->reset_count = 0;
260 261 262 263 264

	err = mei_reset(dev);

	mutex_unlock(&dev->device_lock);

265 266
	if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
		dev_err(&dev->pdev->dev, "device disabled = %d\n", err);
267
		return -ENODEV;
268 269 270 271 272 273
	}

	/* try to start again */
	if (err)
		schedule_work(&dev->reset_work);

274 275 276 277 278

	return 0;
}
EXPORT_SYMBOL_GPL(mei_restart);

279 280 281 282
static void mei_reset_work(struct work_struct *work)
{
	struct mei_device *dev =
		container_of(work, struct mei_device,  reset_work);
283
	int ret;
284 285 286

	mutex_lock(&dev->device_lock);

287
	ret = mei_reset(dev);
288 289

	mutex_unlock(&dev->device_lock);
290

291 292 293 294 295 296 297 298
	if (dev->dev_state == MEI_DEV_DISABLED) {
		dev_err(&dev->pdev->dev, "device disabled = %d\n", ret);
		return;
	}

	/* retry reset in case of failure */
	if (ret)
		schedule_work(&dev->reset_work);
299 300
}

301 302 303 304
void mei_stop(struct mei_device *dev)
{
	dev_dbg(&dev->pdev->dev, "stopping the device.\n");

T
Tomas Winkler 已提交
305
	mei_cancel_work(dev);
306

T
Tomas Winkler 已提交
307
	mei_nfc_host_exit(dev);
308

309 310
	mei_cl_bus_remove_devices(dev);

T
Tomas Winkler 已提交
311
	mutex_lock(&dev->device_lock);
312 313 314 315

	mei_wd_stop(dev);

	dev->dev_state = MEI_DEV_POWER_DOWN;
316
	mei_reset(dev);
317 318 319

	mutex_unlock(&dev->device_lock);

320
	mei_watchdog_unregister(dev);
321
}
322
EXPORT_SYMBOL_GPL(mei_stop);
O
Oren Weil 已提交
323

324 325 326 327 328 329 330 331 332 333 334 335
/**
 * mei_write_is_idle - check if the write queues are idle
 *
 * @dev: the device structure
 *
 * returns true of there is no pending write
 */
bool mei_write_is_idle(struct mei_device *dev)
{
	bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
		list_empty(&dev->ctrl_wr_list.list) &&
		list_empty(&dev->write_list.list));
336

337 338 339 340 341 342 343 344 345
	dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
		idle,
		mei_dev_state_str(dev->dev_state),
		list_empty(&dev->ctrl_wr_list.list),
		list_empty(&dev->write_list.list));

	return idle;
}
EXPORT_SYMBOL_GPL(mei_write_is_idle);
O
Oren Weil 已提交
346

347 348 349
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status)
{
	const struct mei_fw_status *fw_src = &dev->cfg->fw_status;
350 351
	int ret;
	int i;
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

	if (!fw_status)
		return -EINVAL;

	fw_status->count = fw_src->count;
	for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
		ret = pci_read_config_dword(dev->pdev,
			fw_src->status[i], &fw_status->status[i]);
		if (ret)
			return ret;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(mei_fw_status);

void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg)
369 370 371 372
{
	/* setup our list array */
	INIT_LIST_HEAD(&dev->file_list);
	INIT_LIST_HEAD(&dev->device_list);
373
	INIT_LIST_HEAD(&dev->me_clients);
374 375
	mutex_init(&dev->device_lock);
	init_waitqueue_head(&dev->wait_hw_ready);
376
	init_waitqueue_head(&dev->wait_pg);
377
	init_waitqueue_head(&dev->wait_hbm_start);
378 379
	init_waitqueue_head(&dev->wait_stop_wd);
	dev->dev_state = MEI_DEV_INITIALIZING;
380
	dev->reset_count = 0;
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

	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);

	INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
	INIT_WORK(&dev->init_work, mei_host_client_init);
	INIT_WORK(&dev->reset_work, mei_reset_work);

	INIT_LIST_HEAD(&dev->wd_cl.link);
	INIT_LIST_HEAD(&dev->iamthif_cl.link);
	mei_io_list_init(&dev->amthif_cmd_list);
	mei_io_list_init(&dev->amthif_rd_complete_list);

	bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
	dev->open_handle_count = 0;

	/*
	 * Reserving the first client ID
	 * 0: Reserved for MEI Bus Message communications
	 */
	bitmap_set(dev->host_clients_map, 0, 1);
405 406

	dev->pg_event = MEI_PG_EVENT_IDLE;
407
	dev->cfg      = cfg;
408 409 410
}
EXPORT_SYMBOL_GPL(mei_device_init);