init.c 7.9 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
}

O
Oren Weil 已提交
46

47 48 49 50
/**
 * mei_cancel_work. Cancel mei background jobs
 *
 * @dev: the device structure
51 52
 *
 * returns 0 on success or < 0 if the reset hasn't succeeded
53
 */
T
Tomas Winkler 已提交
54 55 56
void mei_cancel_work(struct mei_device *dev)
{
	cancel_work_sync(&dev->init_work);
57
	cancel_work_sync(&dev->reset_work);
T
Tomas Winkler 已提交
58 59 60 61 62

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

O
Oren Weil 已提交
63 64 65 66 67
/**
 * mei_reset - resets host and fw.
 *
 * @dev: the device structure
 */
68
int mei_reset(struct mei_device *dev)
O
Oren Weil 已提交
69
{
70 71
	enum mei_dev_state state = dev->dev_state;
	bool interrupts_enabled;
72
	int ret;
O
Oren Weil 已提交
73

74 75 76 77
	if (state != MEI_DEV_INITIALIZING &&
	    state != MEI_DEV_DISABLED &&
	    state != MEI_DEV_POWER_DOWN &&
	    state != MEI_DEV_POWER_UP)
78
		dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
79
			 mei_dev_state_str(state));
80

81 82 83 84 85 86 87
	/* 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);

88 89 90
	/* enter reset flow */
	interrupts_enabled = state != MEI_DEV_POWER_DOWN;
	dev->dev_state = MEI_DEV_RESETTING;
O
Oren Weil 已提交
91

92 93 94 95 96 97 98
	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;
	}

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

102 103 104
	/* 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 已提交
105

106 107 108
		/* remove all waiting requests */
		mei_cl_all_write_clear(dev);

109 110
		mei_cl_all_disconnect(dev);

111
		/* wake up all readers and writers so they can be interrupted */
112 113
		mei_cl_all_wakeup(dev);

O
Oren Weil 已提交
114
		/* remove entry if already in list */
115
		dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
T
Tomas Winkler 已提交
116 117
		mei_cl_unlink(&dev->wd_cl);
		mei_cl_unlink(&dev->iamthif_cl);
118
		mei_amthif_reset_params(dev);
O
Oren Weil 已提交
119 120
	}

121

122
	dev->me_clients_num = 0;
O
Oren Weil 已提交
123
	dev->rd_msg_hdr = 0;
124
	dev->wd_pending = false;
O
Oren Weil 已提交
125

126 127 128 129 130 131 132 133 134
	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 已提交
135 136
	}

137 138
	ret = mei_hw_start(dev);
	if (ret) {
139 140
		dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
		return ret;
141
	}
T
Tomas Winkler 已提交
142 143 144 145

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

	dev->dev_state = MEI_DEV_INIT_CLIENTS;
146 147
	ret = mei_hbm_start_req(dev);
	if (ret) {
148
		dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
149
		dev->dev_state = MEI_DEV_RESETTING;
150
		return ret;
151
	}
152 153

	return 0;
O
Oren Weil 已提交
154
}
155
EXPORT_SYMBOL_GPL(mei_reset);
O
Oren Weil 已提交
156

157 158 159 160 161 162 163 164 165
/**
 * 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)
{
166
	int ret;
167 168 169 170 171 172 173 174 175
	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");

176
	dev->reset_count = 0;
177 178 179 180 181 182 183 184 185
	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);
186

187
	/* we cannot start the device w/o hbm start message completed */
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 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
	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;
241
	dev->reset_count = 0;
242 243 244 245 246

	err = mei_reset(dev);

	mutex_unlock(&dev->device_lock);

247 248
	if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
		dev_err(&dev->pdev->dev, "device disabled = %d\n", err);
249
		return -ENODEV;
250 251 252 253 254 255
	}

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

256 257 258 259 260

	return 0;
}
EXPORT_SYMBOL_GPL(mei_restart);

261 262 263 264
static void mei_reset_work(struct work_struct *work)
{
	struct mei_device *dev =
		container_of(work, struct mei_device,  reset_work);
265
	int ret;
266 267 268

	mutex_lock(&dev->device_lock);

269
	ret = mei_reset(dev);
270 271

	mutex_unlock(&dev->device_lock);
272

273 274 275 276 277 278 279 280
	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);
281 282
}

283 284 285 286
void mei_stop(struct mei_device *dev)
{
	dev_dbg(&dev->pdev->dev, "stopping the device.\n");

T
Tomas Winkler 已提交
287
	mei_cancel_work(dev);
288

T
Tomas Winkler 已提交
289
	mei_nfc_host_exit(dev);
290

291 292
	mei_cl_bus_remove_devices(dev);

T
Tomas Winkler 已提交
293
	mutex_lock(&dev->device_lock);
294 295 296 297

	mei_wd_stop(dev);

	dev->dev_state = MEI_DEV_POWER_DOWN;
298
	mei_reset(dev);
299 300 301

	mutex_unlock(&dev->device_lock);

302
	mei_watchdog_unregister(dev);
303
}
304
EXPORT_SYMBOL_GPL(mei_stop);
O
Oren Weil 已提交
305

306

O
Oren Weil 已提交
307

308 309 310 311 312 313 314 315 316 317
void mei_device_init(struct mei_device *dev)
{
	/* setup our list array */
	INIT_LIST_HEAD(&dev->file_list);
	INIT_LIST_HEAD(&dev->device_list);
	mutex_init(&dev->device_lock);
	init_waitqueue_head(&dev->wait_hw_ready);
	init_waitqueue_head(&dev->wait_recvd_msg);
	init_waitqueue_head(&dev->wait_stop_wd);
	dev->dev_state = MEI_DEV_INITIALIZING;
318
	dev->reset_count = 0;
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345

	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);
}
EXPORT_SYMBOL_GPL(mei_device_init);