hbm.c 21.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *
 * Intel Management Engine Interface (Intel MEI) Linux driver
 * Copyright (c) 2003-2012, Intel Corporation.
 *
 * 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>
18 19 20 21
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mei.h>
22
#include <linux/pm_runtime.h>
23 24

#include "mei_dev.h"
25
#include "hbm.h"
26
#include "client.h"
27

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
{
#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
	switch (status) {
	MEI_CL_CS(SUCCESS);
	MEI_CL_CS(NOT_FOUND);
	MEI_CL_CS(ALREADY_STARTED);
	MEI_CL_CS(OUT_OF_RESOURCES);
	MEI_CL_CS(MESSAGE_SMALL);
	default: return "unknown";
	}
#undef MEI_CL_CCS
}

/**
 * mei_cl_conn_status_to_errno - convert client connect response
 * status to error code
 *
 * @status: client connect response status
 *
 * returns corresponding error code
 */
static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
{
	switch (status) {
	case MEI_CL_CONN_SUCCESS:          return 0;
	case MEI_CL_CONN_NOT_FOUND:        return -ENOTTY;
	case MEI_CL_CONN_ALREADY_STARTED:  return -EBUSY;
	case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
	case MEI_CL_CONN_MESSAGE_SMALL:    return -EINVAL;
	default:                           return -EINVAL;
	}
}

62 63 64 65 66 67 68 69 70 71 72 73
/**
 * mei_hbm_idle - set hbm to idle state
 *
 * @dev: the device structure
 */
void mei_hbm_idle(struct mei_device *dev)
{
	dev->init_clients_timer = 0;
	dev->hbm_state = MEI_HBM_IDLE;
}

/**
74
 * mei_me_cl_remove_all - remove all me clients
75 76 77
 *
 * @dev: the device structure
 */
78
static void mei_me_cl_remove_all(struct mei_device *dev)
79
{
80
	struct mei_me_client *me_cl, *next;
81 82 83 84 85
	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
			list_del(&me_cl->list);
			kfree(me_cl);
	}
}
86

87 88 89 90 91 92 93
/**
 * mei_hbm_reset - reset hbm counters and book keeping data structurs
 *
 * @dev: the device structure
 */
void mei_hbm_reset(struct mei_device *dev)
{
94 95 96
	dev->me_client_presentation_num = 0;
	dev->me_client_index = 0;

97
	mei_me_cl_remove_all(dev);
98 99 100 101

	mei_hbm_idle(dev);
}

102 103
/**
 * mei_hbm_cl_hdr - construct client hbm header
104
 *
105
 * @cl: client
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
 * @hbm_cmd: host bus message command
 * @buf: buffer for cl header
 * @len: buffer length
 */
static inline
void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
{
	struct mei_hbm_cl_cmd *cmd = buf;

	memset(cmd, 0, len);

	cmd->hbm_cmd = hbm_cmd;
	cmd->host_addr = cl->host_client_id;
	cmd->me_addr = cl->me_client_id;
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
/**
 * mei_hbm_cl_write - write simple hbm client message
 *
 * @dev: the device structure
 * @cl: client
 * @hbm_cmd: host bus message command
 * @len: buffer length
 */
static inline
int mei_hbm_cl_write(struct mei_device *dev,
		     struct mei_cl *cl, u8 hbm_cmd, size_t len)
{
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;

	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, hbm_cmd, dev->wr_msg.data, len);

	return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
}

142
/**
143
 * mei_hbm_cl_addr_equal - tells if they have the same address
144
 *
145 146
 * @cl: - client
 * @buf: buffer with cl header
147
 *
148
 * returns true if addresses are the same
149 150 151 152 153 154 155 156 157 158
 */
static inline
bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
{
	struct mei_hbm_cl_cmd *cmd = buf;
	return cl->host_client_id == cmd->host_addr &&
		cl->me_client_id == cmd->me_addr;
}


T
Tomas Winkler 已提交
159 160 161 162 163 164 165 166 167
int mei_hbm_start_wait(struct mei_device *dev)
{
	int ret;
	if (dev->hbm_state > MEI_HBM_START)
		return 0;

	mutex_unlock(&dev->device_lock);
	ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
			dev->hbm_state == MEI_HBM_IDLE ||
168
			dev->hbm_state >= MEI_HBM_STARTED,
169
			mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
T
Tomas Winkler 已提交
170 171 172 173
	mutex_lock(&dev->device_lock);

	if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
		dev->hbm_state = MEI_HBM_IDLE;
M
Masanari Iida 已提交
174
		dev_err(&dev->pdev->dev, "waiting for mei start failed\n");
175
		return -ETIME;
T
Tomas Winkler 已提交
176 177 178 179
	}
	return 0;
}

180
/**
181
 * mei_hbm_start_req - sends start request message.
182 183
 *
 * @dev: the device structure
184 185
 *
 * returns 0 on success and < 0 on failure
186
 */
T
Tomas Winkler 已提交
187
int mei_hbm_start_req(struct mei_device *dev)
188
{
189
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
190 191
	struct hbm_host_version_request *start_req;
	const size_t len = sizeof(struct hbm_host_version_request);
192
	int ret;
193

194 195
	mei_hbm_reset(dev);

196
	mei_hbm_hdr(mei_hdr, len);
197 198

	/* host start message */
199
	start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
200 201 202 203 204
	memset(start_req, 0, len);
	start_req->hbm_cmd = HOST_START_REQ_CMD;
	start_req->host_version.major_version = HBM_MAJOR_VERSION;
	start_req->host_version.minor_version = HBM_MINOR_VERSION;

T
Tomas Winkler 已提交
205
	dev->hbm_state = MEI_HBM_IDLE;
206 207 208 209 210
	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret) {
		dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n",
			ret);
		return ret;
211
	}
212

T
Tomas Winkler 已提交
213
	dev->hbm_state = MEI_HBM_START;
214
	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
T
Tomas Winkler 已提交
215
	return 0;
216 217
}

T
Tomas Winkler 已提交
218
/*
219
 * mei_hbm_enum_clients_req - sends enumeration client request message.
220 221 222
 *
 * @dev: the device structure
 *
223
 * returns 0 on success and < 0 on failure
224
 */
225
static int mei_hbm_enum_clients_req(struct mei_device *dev)
226
{
227
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
228 229
	struct hbm_host_enum_request *enum_req;
	const size_t len = sizeof(struct hbm_host_enum_request);
230 231
	int ret;

232
	/* enumerate clients */
233
	mei_hbm_hdr(mei_hdr, len);
234

235 236
	enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
	memset(enum_req, 0, len);
237 238
	enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;

239 240 241 242 243
	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret) {
		dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n",
			ret);
		return ret;
244
	}
T
Tomas Winkler 已提交
245
	dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
246
	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
247
	return 0;
248 249
}

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
/*
 * mei_hbm_me_cl_add - add new me client to the list
 *
 * @dev: the device structure
 * @res: hbm property response
 *
 * returns 0 on success and -ENOMEM on allocation failure
 */

static int mei_hbm_me_cl_add(struct mei_device *dev,
			     struct hbm_props_response *res)
{
	struct mei_me_client *me_cl;

	me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL);
	if (!me_cl)
		return -ENOMEM;

	me_cl->props = res->client_properties;
	me_cl->client_id = res->me_addr;
	me_cl->mei_flow_ctrl_creds = 0;

	list_add(&me_cl->list, &dev->me_clients);
	return 0;
}

276
/**
277
 * mei_hbm_prop_req - request property for a single client
278 279 280
 *
 * @dev: the device structure
 *
281
 * returns 0 on success and < 0 on failure
282
 */
283

284
static int mei_hbm_prop_req(struct mei_device *dev)
285 286
{

287
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
288 289 290
	struct hbm_props_request *prop_req;
	const size_t len = sizeof(struct hbm_props_request);
	unsigned long next_client_index;
291
	int ret;
292 293 294 295 296 297

	next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
					  dev->me_client_index);

	/* We got all client properties */
	if (next_client_index == MEI_CLIENTS_MAX) {
T
Tomas Winkler 已提交
298
		dev->hbm_state = MEI_HBM_STARTED;
299 300 301 302 303
		schedule_work(&dev->init_work);

		return 0;
	}

304 305
	mei_hbm_hdr(mei_hdr, len);
	prop_req = (struct hbm_props_request *)dev->wr_msg.data;
306 307 308 309

	memset(prop_req, 0, sizeof(struct hbm_props_request));

	prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
310
	prop_req->me_addr = next_client_index;
311

312 313 314 315 316
	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret) {
		dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n",
			ret);
		return ret;
317 318 319 320 321 322 323 324
	}

	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
	dev->me_client_index = next_client_index;

	return 0;
}

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
/*
 * mei_hbm_pg - sends pg command
 *
 * @dev: the device structure
 * @pg_cmd: the pg command code
 *
 * This function returns -EIO on write failure
 */
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
{
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
	struct hbm_power_gate *req;
	const size_t len = sizeof(struct hbm_power_gate);
	int ret;

	mei_hbm_hdr(mei_hdr, len);

	req = (struct hbm_power_gate *)dev->wr_msg.data;
	memset(req, 0, len);
	req->hbm_cmd = pg_cmd;

	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret)
		dev_err(&dev->pdev->dev, "power gate command write failed.\n");
	return ret;
}
EXPORT_SYMBOL_GPL(mei_hbm_pg);

353
/**
T
Tomas Winkler 已提交
354
 * mei_hbm_stop_req - send stop request message
355 356
 *
 * @dev - mei device
T
Tomas Winkler 已提交
357 358 359
 * @cl: client info
 *
 * This function returns -EIO on write failure
360
 */
T
Tomas Winkler 已提交
361
static int mei_hbm_stop_req(struct mei_device *dev)
362
{
T
Tomas Winkler 已提交
363
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
364
	struct hbm_host_stop_request *req =
T
Tomas Winkler 已提交
365
			(struct hbm_host_stop_request *)dev->wr_msg.data;
366 367 368 369 370 371 372
	const size_t len = sizeof(struct hbm_host_stop_request);

	mei_hbm_hdr(mei_hdr, len);

	memset(req, 0, len);
	req->hbm_cmd = HOST_STOP_REQ_CMD;
	req->reason = DRIVER_STOP_REQUEST;
T
Tomas Winkler 已提交
373 374

	return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
375 376
}

377
/**
378
 * mei_hbm_cl_flow_control_req - sends flow control request.
379 380
 *
 * @dev: the device structure
381
 * @cl: client info
382 383 384
 *
 * This function returns -EIO on write failure
 */
385
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
386 387
{
	const size_t len = sizeof(struct hbm_flow_control);
388
	cl_dbg(dev, cl, "sending flow control\n");
389
	return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, len);
390 391
}

392
/**
393
 * mei_hbm_add_single_flow_creds - adds single buffer credentials.
394
 *
395
 * @dev: the device structure
396
 * @flow: flow control.
397 398
 *
 * return 0 on success, < 0 otherwise
399
 */
400
static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
401 402
				  struct hbm_flow_control *flow)
{
403 404
	struct mei_me_client *me_cl;

405 406
	me_cl = mei_me_cl_by_id(dev, flow->me_addr);
	if (!me_cl) {
407 408
		dev_err(&dev->pdev->dev, "no such me client %d\n",
			flow->me_addr);
409
		return -ENOENT;
410 411
	}

412 413 414 415 416 417
	if (WARN_ON(me_cl->props.single_recv_buf == 0))
		return -EINVAL;

	me_cl->mei_flow_ctrl_creds++;
	dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
	    flow->me_addr, me_cl->mei_flow_ctrl_creds);
418 419

	return 0;
420 421 422 423 424 425 426 427 428 429 430
}

/**
 * mei_hbm_cl_flow_control_res - flow control response from me
 *
 * @dev: the device structure
 * @flow_control: flow control response bus message
 */
static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
		struct hbm_flow_control *flow_control)
{
431
	struct mei_cl *cl;
432 433 434 435 436 437 438 439

	if (!flow_control->host_addr) {
		/* single receive buffer */
		mei_hbm_add_single_flow_creds(dev, flow_control);
		return;
	}

	/* normal connection */
440
	list_for_each_entry(cl, &dev->file_list, link) {
441 442
		if (mei_hbm_cl_addr_equal(cl, flow_control)) {
			cl->mei_flow_ctrl_creds++;
443 444 445 446
			dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d creds %d.\n",
				flow_control->host_addr, flow_control->me_addr,
				cl->mei_flow_ctrl_creds);
			break;
447 448 449 450 451
		}
	}
}


452
/**
453
 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
454 455
 *
 * @dev: the device structure
456
 * @cl: a client to disconnect from
457 458 459
 *
 * This function returns -EIO on write failure
 */
460
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
461 462
{
	const size_t len = sizeof(struct hbm_client_connect_request);
463
	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, len);
464 465
}

T
Tomas Winkler 已提交
466 467 468 469 470 471 472 473 474 475 476
/**
 * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
 *
 * @dev: the device structure
 * @cl: a client to disconnect from
 *
 * This function returns -EIO on write failure
 */
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
{
	const size_t len = sizeof(struct hbm_client_connect_response);
477
	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, len);
T
Tomas Winkler 已提交
478 479
}

480 481 482 483 484 485 486 487 488 489
/**
 * mei_hbm_cl_disconnect_res - disconnect response from ME
 *
 * @dev: the device structure
 * @rs: disconnect response bus message
 */
static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
		struct hbm_client_connect_response *rs)
{
	struct mei_cl *cl;
490
	struct mei_cl_cb *cb, *next;
491

492 493
	dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n",
			rs->me_addr, rs->host_addr, rs->status);
494

495 496
	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
		cl = cb->cl;
497

498 499 500
		/* this should not happen */
		if (WARN_ON(!cl)) {
			list_del(&cb->list);
501 502 503 504
			return;
		}

		if (mei_hbm_cl_addr_equal(cl, rs)) {
505
			list_del(&cb->list);
506
			if (rs->status == MEI_CL_DISCONN_SUCCESS)
507 508 509 510 511 512 513 514 515
				cl->state = MEI_FILE_DISCONNECTED;

			cl->status = 0;
			cl->timer_count = 0;
			break;
		}
	}
}

516
/**
517
 * mei_hbm_cl_connect_req - send connection request to specific me client
518 519
 *
 * @dev: the device structure
520
 * @cl: a client to connect to
521
 *
522
 * returns -EIO on write failure
523
 */
524
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
525 526
{
	const size_t len = sizeof(struct hbm_client_connect_request);
527
	return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, len);
528 529
}

530
/**
531
 * mei_hbm_cl_connect_res - connect response from the ME
532 533 534 535 536 537 538 539 540
 *
 * @dev: the device structure
 * @rs: connect response bus message
 */
static void mei_hbm_cl_connect_res(struct mei_device *dev,
		struct hbm_client_connect_response *rs)
{

	struct mei_cl *cl;
541
	struct mei_cl_cb *cb, *next;
542

543 544 545
	dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n",
			rs->me_addr, rs->host_addr,
			mei_cl_conn_status_str(rs->status));
546

547
	cl = NULL;
548

549
	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
550

551 552 553 554 555 556
		cl = cb->cl;
		/* this should not happen */
		if (WARN_ON(!cl)) {
			list_del_init(&cb->list);
			continue;
		}
557

558 559
		if (cb->fop_type !=  MEI_FOP_CONNECT)
			continue;
560

561 562 563
		if (mei_hbm_cl_addr_equal(cl, rs)) {
			list_del(&cb->list);
			break;
564 565
		}
	}
566 567 568 569 570 571 572 573 574 575

	if (!cl)
		return;

	cl->timer_count = 0;
	if (rs->status == MEI_CL_CONN_SUCCESS)
		cl->state = MEI_FILE_CONNECTED;
	else
		cl->state = MEI_FILE_DISCONNECTED;
	cl->status = mei_cl_conn_status_to_errno(rs->status);
576 577 578
}


579
/**
580 581
 * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
 *  host sends disconnect response
582 583
 *
 * @dev: the device structure.
584
 * @disconnect_req: disconnect request bus message from the me
T
Tomas Winkler 已提交
585 586
 *
 * returns -ENOMEM on allocation failure
587
 */
T
Tomas Winkler 已提交
588
static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
589 590
		struct hbm_client_connect_request *disconnect_req)
{
591
	struct mei_cl *cl;
T
Tomas Winkler 已提交
592
	struct mei_cl_cb *cb;
593

594
	list_for_each_entry(cl, &dev->file_list, link) {
595
		if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
596 597 598
			dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
					disconnect_req->host_addr,
					disconnect_req->me_addr);
599 600
			cl->state = MEI_FILE_DISCONNECTED;
			cl->timer_count = 0;
601

T
Tomas Winkler 已提交
602 603 604 605 606 607 608
			cb = mei_io_cb_init(cl, NULL);
			if (!cb)
				return -ENOMEM;
			cb->fop_type = MEI_FOP_DISCONNECT_RSP;
			cl_dbg(dev, cl, "add disconnect response as first\n");
			list_add(&cb->list, &dev->ctrl_wr_list.list);

609 610 611
			break;
		}
	}
T
Tomas Winkler 已提交
612
	return 0;
613 614 615
}


T
Tomas Winkler 已提交
616 617 618 619 620 621 622 623 624 625 626 627 628 629
/**
 * mei_hbm_version_is_supported - checks whether the driver can
 *     support the hbm version of the device
 *
 * @dev: the device structure
 * returns true if driver can support hbm version of the device
 */
bool mei_hbm_version_is_supported(struct mei_device *dev)
{
	return	(dev->version.major_version < HBM_MAJOR_VERSION) ||
		(dev->version.major_version == HBM_MAJOR_VERSION &&
		 dev->version.minor_version <= HBM_MINOR_VERSION);
}

630 631 632 633 634 635
/**
 * mei_hbm_dispatch - bottom half read routine after ISR to
 * handle the read bus message cmd processing.
 *
 * @dev: the device structure
 * @mei_hdr: header of bus message
636 637
 *
 * returns 0 on success and < 0 on failure
638
 */
639
int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
{
	struct mei_bus_message *mei_msg;
	struct hbm_host_version_response *version_res;
	struct hbm_client_connect_response *connect_res;
	struct hbm_client_connect_response *disconnect_res;
	struct hbm_client_connect_request *disconnect_req;
	struct hbm_flow_control *flow_control;
	struct hbm_props_response *props_res;
	struct hbm_host_enum_response *enum_res;

	/* read the message to our buffer */
	BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
	mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
	mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;

655 656 657 658 659 660 661 662
	/* ignore spurious message and prevent reset nesting
	 * hbm is put to idle during system reset
	 */
	if (dev->hbm_state == MEI_HBM_IDLE) {
		dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n");
		return 0;
	}

663 664
	switch (mei_msg->hbm_cmd) {
	case HOST_START_RES_CMD:
665 666 667 668
		dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");

		dev->init_clients_timer = 0;

669
		version_res = (struct hbm_host_version_response *)mei_msg;
T
Tomas Winkler 已提交
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686

		dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
				HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
				version_res->me_max_version.major_version,
				version_res->me_max_version.minor_version);

		if (version_res->host_version_supported) {
			dev->version.major_version = HBM_MAJOR_VERSION;
			dev->version.minor_version = HBM_MINOR_VERSION;
		} else {
			dev->version.major_version =
				version_res->me_max_version.major_version;
			dev->version.minor_version =
				version_res->me_max_version.minor_version;
		}

		if (!mei_hbm_version_is_supported(dev)) {
687
			dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
688

689
			dev->hbm_state = MEI_HBM_STOPPED;
T
Tomas Winkler 已提交
690
			if (mei_hbm_stop_req(dev)) {
691 692 693 694 695
				dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
				return -EIO;
			}
			break;
		}
T
Tomas Winkler 已提交
696

697 698 699 700 701
		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
		    dev->hbm_state != MEI_HBM_START) {
			dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n",
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
702
		}
703

704 705 706 707 708
		dev->hbm_state = MEI_HBM_STARTED;

		if (mei_hbm_enum_clients_req(dev)) {
			dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n");
			return -EIO;
709 710
		}

T
Tomas Winkler 已提交
711
		wake_up_interruptible(&dev->wait_recvd_msg);
712 713 714
		break;

	case CLIENT_CONNECT_RES_CMD:
715 716
		dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n");

717
		connect_res = (struct hbm_client_connect_response *) mei_msg;
718
		mei_hbm_cl_connect_res(dev, connect_res);
719 720 721 722
		wake_up(&dev->wait_recvd_msg);
		break;

	case CLIENT_DISCONNECT_RES_CMD:
723 724
		dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n");

725
		disconnect_res = (struct hbm_client_connect_response *) mei_msg;
726
		mei_hbm_cl_disconnect_res(dev, disconnect_res);
727 728 729 730
		wake_up(&dev->wait_recvd_msg);
		break;

	case MEI_FLOW_CONTROL_CMD:
731 732
		dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n");

733
		flow_control = (struct hbm_flow_control *) mei_msg;
734
		mei_hbm_cl_flow_control_res(dev, flow_control);
735 736
		break;

737 738
	case MEI_PG_ISOLATION_ENTRY_RES_CMD:
		dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
739
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
740 741 742 743 744 745
		if (waitqueue_active(&dev->wait_pg))
			wake_up(&dev->wait_pg);
		break;

	case MEI_PG_ISOLATION_EXIT_REQ_CMD:
		dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
746
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
747 748
		if (waitqueue_active(&dev->wait_pg))
			wake_up(&dev->wait_pg);
749 750 751 752 753 754 755
		else
			/*
			* If the driver is not waiting on this then
			* this is HW initiated exit from PG.
			* Start runtime pm resume sequence to exit from PG.
			*/
			pm_request_resume(&dev->pdev->dev);
756 757
		break;

758
	case HOST_CLIENT_PROPERTIES_RES_CMD:
759 760 761 762
		dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");

		dev->init_clients_timer = 0;

763 764 765 766
		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
		    dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
			dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
				dev->dev_state, dev->hbm_state);
767 768 769
			return -EPROTO;
		}

770 771
		props_res = (struct hbm_props_response *)mei_msg;

772 773 774 775
		if (props_res->status) {
			dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d\n",
				props_res->status);
			return -EPROTO;
776 777
		}

778
		mei_hbm_me_cl_add(dev, props_res);
779 780 781 782

		dev->me_client_index++;
		dev->me_client_presentation_num++;

783
		/* request property for the next client */
784 785
		if (mei_hbm_prop_req(dev))
			return -EIO;
786 787 788 789

		break;

	case HOST_ENUM_RES_CMD:
790 791 792 793
		dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n");

		dev->init_clients_timer = 0;

794
		enum_res = (struct hbm_host_enum_response *) mei_msg;
795 796 797
		BUILD_BUG_ON(sizeof(dev->me_clients_map)
				< sizeof(enum_res->valid_addresses));
		memcpy(dev->me_clients_map, enum_res->valid_addresses,
798
				sizeof(enum_res->valid_addresses));
799 800 801 802 803 804

		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
		    dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
			dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
805
		}
806 807 808 809 810 811 812

		dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;

		/* first property request */
		if (mei_hbm_prop_req(dev))
			return -EIO;

813 814 815
		break;

	case HOST_STOP_RES_CMD:
816 817 818 819 820 821 822 823 824
		dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n");

		dev->init_clients_timer = 0;

		if (dev->hbm_state != MEI_HBM_STOPPED) {
			dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
		}
T
Tomas Winkler 已提交
825

826
		dev->dev_state = MEI_DEV_POWER_DOWN;
827 828 829
		dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
		/* force the reset */
		return -EPROTO;
830 831 832
		break;

	case CLIENT_DISCONNECT_REQ_CMD:
833 834
		dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n");

835
		disconnect_req = (struct hbm_client_connect_request *)mei_msg;
836
		mei_hbm_fw_disconnect_req(dev, disconnect_req);
837 838 839
		break;

	case ME_STOP_REQ_CMD:
840 841
		dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
		dev->hbm_state = MEI_HBM_STOPPED;
T
Tomas Winkler 已提交
842 843 844 845
		if (mei_hbm_stop_req(dev)) {
			dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
			return -EIO;
		}
846 847 848 849 850 851
		break;
	default:
		BUG();
		break;

	}
852
	return 0;
853 854
}