hbm.c 22.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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/**
 * 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;
}

/**
 * mei_hbm_reset - reset hbm counters and book keeping data structurs
 *
 * @dev: the device structure
 */
void mei_hbm_reset(struct mei_device *dev)
{
	dev->me_clients_num = 0;
	dev->me_client_presentation_num = 0;
	dev->me_client_index = 0;

	kfree(dev->me_clients);
	dev->me_clients = NULL;

	mei_hbm_idle(dev);
}

90 91 92 93 94
/**
 * mei_hbm_me_cl_allocate - allocates storage for me clients
 *
 * @dev: the device structure
 *
95
 * returns 0 on success -ENOMEM on allocation failure
96
 */
97
static int mei_hbm_me_cl_allocate(struct mei_device *dev)
98 99 100 101
{
	struct mei_me_client *clients;
	int b;

102
	mei_hbm_reset(dev);
103

104 105 106 107
	/* count how many ME clients we have */
	for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
		dev->me_clients_num++;

108
	if (dev->me_clients_num == 0)
109
		return 0;
110

111
	dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
112 113 114 115 116 117
		dev->me_clients_num * sizeof(struct mei_me_client));
	/* allocate storage for ME clients representation */
	clients = kcalloc(dev->me_clients_num,
			sizeof(struct mei_me_client), GFP_KERNEL);
	if (!clients) {
		dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
118
		return -ENOMEM;
119 120
	}
	dev->me_clients = clients;
121
	return 0;
122 123
}

124 125
/**
 * mei_hbm_cl_hdr - construct client hbm header
126
 *
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
 * @cl: - client
 * @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;
}

/**
145
 * mei_hbm_cl_addr_equal - tells if they have the same address
146
 *
147 148
 * @cl: - client
 * @buf: buffer with cl header
149
 *
150
 * returns true if addresses are the same
151 152 153 154 155 156 157 158 159 160
 */
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 已提交
161 162 163 164 165 166 167 168 169
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 ||
170
			dev->hbm_state >= MEI_HBM_STARTED,
171
			mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
T
Tomas Winkler 已提交
172 173 174 175
	mutex_lock(&dev->device_lock);

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

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

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
 * mei_hbm_prop_req - request property for a single client
252 253 254
 *
 * @dev: the device structure
 *
255
 * returns 0 on success and < 0 on failure
256
 */
257

258
static int mei_hbm_prop_req(struct mei_device *dev)
259 260
{

261
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
262 263 264
	struct hbm_props_request *prop_req;
	const size_t len = sizeof(struct hbm_props_request);
	unsigned long next_client_index;
265
	unsigned long client_num;
266
	int ret;
267 268 269 270 271 272 273 274

	client_num = dev->me_client_presentation_num;

	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 已提交
275
		dev->hbm_state = MEI_HBM_STARTED;
276 277 278 279 280 281 282 283
		schedule_work(&dev->init_work);

		return 0;
	}

	dev->me_clients[client_num].client_id = next_client_index;
	dev->me_clients[client_num].mei_flow_ctrl_creds = 0;

284 285
	mei_hbm_hdr(mei_hdr, len);
	prop_req = (struct hbm_props_request *)dev->wr_msg.data;
286 287 288 289 290 291 292

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


	prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
	prop_req->address = next_client_index;

293 294 295 296 297
	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;
298 299 300 301 302 303 304 305
	}

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

	return 0;
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
/*
 * 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);

334
/**
T
Tomas Winkler 已提交
335
 * mei_hbm_stop_req - send stop request message
336 337
 *
 * @dev - mei device
T
Tomas Winkler 已提交
338 339 340
 * @cl: client info
 *
 * This function returns -EIO on write failure
341
 */
T
Tomas Winkler 已提交
342
static int mei_hbm_stop_req(struct mei_device *dev)
343
{
T
Tomas Winkler 已提交
344
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
345
	struct hbm_host_stop_request *req =
T
Tomas Winkler 已提交
346
			(struct hbm_host_stop_request *)dev->wr_msg.data;
347 348 349 350 351 352 353
	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 已提交
354 355

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

358
/**
359
 * mei_hbm_cl_flow_control_req - sends flow control request.
360 361
 *
 * @dev: the device structure
362
 * @cl: client info
363 364 365
 *
 * This function returns -EIO on write failure
 */
366
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
367
{
368
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
369 370
	const size_t len = sizeof(struct hbm_flow_control);

371 372
	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
373

374
	cl_dbg(dev, cl, "sending flow control\n");
375

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

379
/**
380
 * mei_hbm_add_single_flow_creds - adds single buffer credentials.
381
 *
382
 * @dev: the device structure
383
 * @flow: flow control.
384 385
 *
 * return 0 on success, < 0 otherwise
386
 */
387
static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
388 389
				  struct hbm_flow_control *flow)
{
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
	struct mei_me_client *me_cl;
	int id;

	id = mei_me_cl_by_id(dev, flow->me_addr);
	if (id < 0) {
		dev_err(&dev->pdev->dev, "no such me client %d\n",
			flow->me_addr);
		return id;
	}

	me_cl = &dev->me_clients[id];
	if (me_cl->props.single_recv_buf) {
		me_cl->mei_flow_ctrl_creds++;
		dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
		    flow->me_addr);
		dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
		    me_cl->mei_flow_ctrl_creds);
	} else {
		BUG();	/* error in flow control */
409
	}
410 411

	return 0;
412 413 414 415 416 417 418 419 420 421 422
}

/**
 * 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)
{
423
	struct mei_cl *cl;
424 425 426 427 428 429 430 431

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

	/* normal connection */
432
	list_for_each_entry(cl, &dev->file_list, link) {
433 434 435 436 437 438 439 440 441 442 443 444
		if (mei_hbm_cl_addr_equal(cl, flow_control)) {
			cl->mei_flow_ctrl_creds++;
			dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
				flow_control->host_addr, flow_control->me_addr);
			dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
				    cl->mei_flow_ctrl_creds);
				break;
		}
	}
}


445
/**
446
 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
447 448
 *
 * @dev: the device structure
449
 * @cl: a client to disconnect from
450 451 452
 *
 * This function returns -EIO on write failure
 */
453
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
454
{
455
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
456 457
	const size_t len = sizeof(struct hbm_client_connect_request);

458 459
	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
460

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

T
Tomas Winkler 已提交
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
/**
 * 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)
{
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
	const size_t len = sizeof(struct hbm_client_connect_response);

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

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

483 484 485 486 487 488 489 490 491 492
/**
 * 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;
493
	struct mei_cl_cb *cb, *next;
494

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

498 499
	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
		cl = cb->cl;
500

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

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

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

519
/**
520
 * mei_hbm_cl_connect_req - send connection request to specific me client
521 522
 *
 * @dev: the device structure
523
 * @cl: a client to connect to
524
 *
525
 * returns -EIO on write failure
526
 */
527
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
528
{
529
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
530 531
	const size_t len = sizeof(struct hbm_client_connect_request);

532 533
	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
534

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

538
/**
539
 * mei_hbm_cl_connect_res - connect response from the ME
540 541 542 543 544 545 546 547 548
 *
 * @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;
549
	struct mei_cl_cb *cb, *next;
550

551 552 553
	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));
554

555
	cl = NULL;
556

557
	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
558

559 560 561 562 563 564
		cl = cb->cl;
		/* this should not happen */
		if (WARN_ON(!cl)) {
			list_del_init(&cb->list);
			continue;
		}
565

566 567
		if (cb->fop_type !=  MEI_FOP_CONNECT)
			continue;
568

569 570 571
		if (mei_hbm_cl_addr_equal(cl, rs)) {
			list_del(&cb->list);
			break;
572 573
		}
	}
574 575 576 577 578 579 580 581 582 583

	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);
584 585 586
}


587
/**
588 589
 * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
 *  host sends disconnect response
590 591
 *
 * @dev: the device structure.
592
 * @disconnect_req: disconnect request bus message from the me
T
Tomas Winkler 已提交
593 594
 *
 * returns -ENOMEM on allocation failure
595
 */
T
Tomas Winkler 已提交
596
static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
597 598
		struct hbm_client_connect_request *disconnect_req)
{
599
	struct mei_cl *cl;
T
Tomas Winkler 已提交
600
	struct mei_cl_cb *cb;
601

602
	list_for_each_entry(cl, &dev->file_list, link) {
603
		if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
604 605 606
			dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
					disconnect_req->host_addr,
					disconnect_req->me_addr);
607 608
			cl->state = MEI_FILE_DISCONNECTED;
			cl->timer_count = 0;
609

T
Tomas Winkler 已提交
610 611 612 613 614 615 616
			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);

617 618 619
			break;
		}
	}
T
Tomas Winkler 已提交
620
	return 0;
621 622 623
}


T
Tomas Winkler 已提交
624 625 626 627 628 629 630 631 632 633 634 635 636 637
/**
 * 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);
}

638 639 640 641 642 643
/**
 * 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
644 645
 *
 * returns 0 on success and < 0 on failure
646
 */
647
int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
{
	struct mei_bus_message *mei_msg;
	struct mei_me_client *me_client;
	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;

664 665 666 667 668 669 670 671
	/* 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;
	}

672 673
	switch (mei_msg->hbm_cmd) {
	case HOST_START_RES_CMD:
674 675 676 677
		dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");

		dev->init_clients_timer = 0;

678
		version_res = (struct hbm_host_version_response *)mei_msg;
T
Tomas Winkler 已提交
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695

		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)) {
696
			dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
697

698
			dev->hbm_state = MEI_HBM_STOPPED;
T
Tomas Winkler 已提交
699
			if (mei_hbm_stop_req(dev)) {
700 701 702 703 704
				dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
				return -EIO;
			}
			break;
		}
T
Tomas Winkler 已提交
705

706 707 708 709 710
		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;
711
		}
712

713 714 715 716 717
		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;
718 719
		}

T
Tomas Winkler 已提交
720
		wake_up_interruptible(&dev->wait_recvd_msg);
721 722 723
		break;

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

726
		connect_res = (struct hbm_client_connect_response *) mei_msg;
727
		mei_hbm_cl_connect_res(dev, connect_res);
728 729 730 731
		wake_up(&dev->wait_recvd_msg);
		break;

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

734
		disconnect_res = (struct hbm_client_connect_response *) mei_msg;
735
		mei_hbm_cl_disconnect_res(dev, disconnect_res);
736 737 738 739
		wake_up(&dev->wait_recvd_msg);
		break;

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

742
		flow_control = (struct hbm_flow_control *) mei_msg;
743
		mei_hbm_cl_flow_control_res(dev, flow_control);
744 745
		break;

746 747
	case MEI_PG_ISOLATION_ENTRY_RES_CMD:
		dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
748
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
749 750 751 752 753 754
		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");
755
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
756 757
		if (waitqueue_active(&dev->wait_pg))
			wake_up(&dev->wait_pg);
758 759 760 761 762 763 764
		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);
765 766
		break;

767
	case HOST_CLIENT_PROPERTIES_RES_CMD:
768 769 770 771 772 773 774 775 776
		dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");

		dev->init_clients_timer = 0;

		if (dev->me_clients == NULL) {
			dev_err(&dev->pdev->dev, "hbm: properties response: mei_clients not allocated\n");
			return -EPROTO;
		}

777 778 779
		props_res = (struct hbm_props_response *)mei_msg;
		me_client = &dev->me_clients[dev->me_client_presentation_num];

780 781 782 783
		if (props_res->status) {
			dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d\n",
				props_res->status);
			return -EPROTO;
784 785 786
		}

		if (me_client->client_id != props_res->address) {
787 788 789
			dev_err(&dev->pdev->dev, "hbm: properties response: address mismatch %d ?= %d\n",
				me_client->client_id, props_res->address);
			return -EPROTO;
790 791 792
		}

		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
T
Tomas Winkler 已提交
793
		    dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
794 795 796
			dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
797 798 799 800 801 802
		}

		me_client->props = props_res->client_properties;
		dev->me_client_index++;
		dev->me_client_presentation_num++;

803
		/* request property for the next client */
804 805
		if (mei_hbm_prop_req(dev))
			return -EIO;
806 807 808 809

		break;

	case HOST_ENUM_RES_CMD:
810 811 812 813
		dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n");

		dev->init_clients_timer = 0;

814
		enum_res = (struct hbm_host_enum_response *) mei_msg;
815 816 817 818
		BUILD_BUG_ON(sizeof(dev->me_clients_map)
				< sizeof(enum_res->valid_addresses));
		memcpy(dev->me_clients_map, enum_res->valid_addresses,
			sizeof(enum_res->valid_addresses));
819 820 821 822 823 824

		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;
825
		}
826 827 828 829 830 831 832 833 834 835 836 837

		if (mei_hbm_me_cl_allocate(dev)) {
			dev_err(&dev->pdev->dev, "hbm: enumeration response: cannot allocate clients array\n");
			return -ENOMEM;
		}

		dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;

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

838 839 840
		break;

	case HOST_STOP_RES_CMD:
841 842 843 844 845 846 847 848 849
		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 已提交
850

851
		dev->dev_state = MEI_DEV_POWER_DOWN;
852 853 854
		dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
		/* force the reset */
		return -EPROTO;
855 856 857
		break;

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

860
		disconnect_req = (struct hbm_client_connect_request *)mei_msg;
861
		mei_hbm_fw_disconnect_req(dev, disconnect_req);
862 863 864
		break;

	case ME_STOP_REQ_CMD:
865 866
		dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
		dev->hbm_state = MEI_HBM_STOPPED;
T
Tomas Winkler 已提交
867 868 869 870
		if (mei_hbm_stop_req(dev)) {
			dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
			return -EIO;
		}
871 872 873 874 875 876
		break;
	default:
		BUG();
		break;

	}
877
	return 0;
878 879
}