hbm.c 25.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
#include <linux/sched.h>
#include <linux/wait.h>
20
#include <linux/pm_runtime.h>
21 22 23
#include <linux/slab.h>

#include <linux/mei.h>
24 25

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

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
static const char *mei_hbm_status_str(enum mei_hbm_status status)
{
#define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status
	switch (status) {
	MEI_HBM_STATUS(SUCCESS);
	MEI_HBM_STATUS(CLIENT_NOT_FOUND);
	MEI_HBM_STATUS(ALREADY_EXISTS);
	MEI_HBM_STATUS(REJECTED);
	MEI_HBM_STATUS(INVALID_PARAMETER);
	MEI_HBM_STATUS(NOT_ALLOWED);
	MEI_HBM_STATUS(ALREADY_STARTED);
	MEI_HBM_STATUS(NOT_STARTED);
	default: return "unknown";
	}
#undef MEI_HBM_STATUS
};

46 47 48 49 50 51 52 53 54 55 56 57 58 59
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
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
const char *mei_hbm_state_str(enum mei_hbm_state state)
{
#define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state
	switch (state) {
	MEI_HBM_STATE(IDLE);
	MEI_HBM_STATE(STARTING);
	MEI_HBM_STATE(STARTED);
	MEI_HBM_STATE(ENUM_CLIENTS);
	MEI_HBM_STATE(CLIENT_PROPERTIES);
	MEI_HBM_STATE(STOPPED);
	default:
		return "unknown";
	}
#undef MEI_HBM_STATE
}

76 77 78 79 80 81
/**
 * mei_cl_conn_status_to_errno - convert client connect response
 * status to error code
 *
 * @status: client connect response status
 *
82
 * Return: corresponding error code
83 84 85 86 87 88 89 90 91 92 93 94 95
 */
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;
	}
}

96 97 98 99 100 101 102 103 104 105 106
/**
 * 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;
}

107 108 109 110 111 112 113
/**
 * mei_hbm_reset - reset hbm counters and book keeping data structurs
 *
 * @dev: the device structure
 */
void mei_hbm_reset(struct mei_device *dev)
{
114 115
	dev->me_client_index = 0;

116
	mei_me_cl_rm_all(dev);
117 118 119 120

	mei_hbm_idle(dev);
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
/**
 * mei_hbm_hdr - construct hbm header
 *
 * @hdr: hbm header
 * @length: payload length
 */

static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
{
	hdr->host_addr = 0;
	hdr->me_addr = 0;
	hdr->length = length;
	hdr->msg_complete = 1;
	hdr->reserved = 0;
}

137 138
/**
 * mei_hbm_cl_hdr - construct client hbm header
139
 *
140
 * @cl: client
141 142 143 144 145 146 147 148 149 150 151 152
 * @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;
153
	cmd->host_addr = mei_cl_host_addr(cl);
154
	cmd->me_addr = mei_cl_me_id(cl);
155 156
}

157 158 159 160 161 162 163
/**
 * mei_hbm_cl_write - write simple hbm client message
 *
 * @dev: the device structure
 * @cl: client
 * @hbm_cmd: host bus message command
 * @len: buffer length
A
Alexander Usyskin 已提交
164 165
 *
 * Return: 0 on success, <0 on failure.
166 167 168 169 170 171 172 173 174 175 176 177 178
 */
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);
}

179
/**
180 181
 * mei_hbm_cl_addr_equal - check if the client's and
 *	the message address match
182
 *
183 184
 * @cl: client
 * @cmd: hbm client message
185
 *
186
 * Return: true if addresses are the same
187 188
 */
static inline
189
bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
190
{
191
	return  mei_cl_host_addr(cl) == cmd->host_addr &&
192
		mei_cl_me_id(cl) == cmd->me_addr;
193 194
}

195 196 197 198 199 200
/**
 * mei_hbm_cl_find_by_cmd - find recipient client
 *
 * @dev: the device structure
 * @buf: a buffer with hbm cl command
 *
201
 * Return: the recipient client or NULL if not found
202 203 204 205 206 207 208 209 210 211 212 213 214 215
 */
static inline
struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf)
{
	struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf;
	struct mei_cl *cl;

	list_for_each_entry(cl, &dev->file_list, link)
		if (mei_hbm_cl_addr_equal(cl, cmd))
			return cl;
	return NULL;
}


216 217 218 219 220
/**
 * mei_hbm_start_wait - wait for start response message.
 *
 * @dev: the device structure
 *
221
 * Return: 0 on success and < 0 on failure
222
 */
T
Tomas Winkler 已提交
223 224 225
int mei_hbm_start_wait(struct mei_device *dev)
{
	int ret;
226 227

	if (dev->hbm_state > MEI_HBM_STARTING)
T
Tomas Winkler 已提交
228 229 230
		return 0;

	mutex_unlock(&dev->device_lock);
231 232
	ret = wait_event_timeout(dev->wait_hbm_start,
			dev->hbm_state != MEI_HBM_STARTING,
233
			mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
T
Tomas Winkler 已提交
234 235
	mutex_lock(&dev->device_lock);

236
	if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) {
T
Tomas Winkler 已提交
237
		dev->hbm_state = MEI_HBM_IDLE;
238
		dev_err(dev->dev, "waiting for mei start failed\n");
239
		return -ETIME;
T
Tomas Winkler 已提交
240 241 242 243
	}
	return 0;
}

244
/**
245
 * mei_hbm_start_req - sends start request message.
246 247
 *
 * @dev: the device structure
248
 *
249
 * Return: 0 on success and < 0 on failure
250
 */
T
Tomas Winkler 已提交
251
int mei_hbm_start_req(struct mei_device *dev)
252
{
253
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
254 255
	struct hbm_host_version_request *start_req;
	const size_t len = sizeof(struct hbm_host_version_request);
256
	int ret;
257

258 259
	mei_hbm_reset(dev);

260
	mei_hbm_hdr(mei_hdr, len);
261 262

	/* host start message */
263
	start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
264 265 266 267 268
	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 已提交
269
	dev->hbm_state = MEI_HBM_IDLE;
270 271
	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret) {
272
		dev_err(dev->dev, "version message write failed: ret = %d\n",
273 274
			ret);
		return ret;
275
	}
276

277
	dev->hbm_state = MEI_HBM_STARTING;
278
	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
T
Tomas Winkler 已提交
279
	return 0;
280 281
}

T
Tomas Winkler 已提交
282
/*
283
 * mei_hbm_enum_clients_req - sends enumeration client request message.
284 285 286
 *
 * @dev: the device structure
 *
287
 * Return: 0 on success and < 0 on failure
288
 */
289
static int mei_hbm_enum_clients_req(struct mei_device *dev)
290
{
291
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
292 293
	struct hbm_host_enum_request *enum_req;
	const size_t len = sizeof(struct hbm_host_enum_request);
294 295
	int ret;

296
	/* enumerate clients */
297
	mei_hbm_hdr(mei_hdr, len);
298

299 300
	enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
	memset(enum_req, 0, len);
301
	enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
T
Tomas Winkler 已提交
302
	enum_req->allow_add = dev->hbm_f_dc_supported;
303

304 305
	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret) {
306
		dev_err(dev->dev, "enumeration request write failed: ret = %d.\n",
307 308
			ret);
		return ret;
309
	}
T
Tomas Winkler 已提交
310
	dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
311
	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
312
	return 0;
313 314
}

315 316 317 318 319 320
/*
 * mei_hbm_me_cl_add - add new me client to the list
 *
 * @dev: the device structure
 * @res: hbm property response
 *
321
 * Return: 0 on success and -ENOMEM on allocation failure
322 323 324 325 326 327
 */

static int mei_hbm_me_cl_add(struct mei_device *dev,
			     struct hbm_props_response *res)
{
	struct mei_me_client *me_cl;
328 329 330
	const uuid_le *uuid = &res->client_properties.protocol_name;

	mei_me_cl_rm_by_uuid(dev, uuid);
331 332 333 334 335

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

336 337
	mei_me_cl_init(me_cl);

338 339 340 341
	me_cl->props = res->client_properties;
	me_cl->client_id = res->me_addr;
	me_cl->mei_flow_ctrl_creds = 0;

342 343
	mei_me_cl_add(dev, me_cl);

344 345 346
	return 0;
}

T
Tomas Winkler 已提交
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 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_hbm_add_cl_resp - send response to fw on client add request
 *
 * @dev: the device structure
 * @addr: me address
 * @status: response status
 *
 * Return: 0 on success and < 0 on failure
 */
static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status)
{
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
	struct hbm_add_client_response *resp;
	const size_t len = sizeof(struct hbm_add_client_response);
	int ret;

	dev_dbg(dev->dev, "adding client response\n");

	resp = (struct hbm_add_client_response *)dev->wr_msg.data;

	mei_hbm_hdr(mei_hdr, len);
	memset(resp, 0, sizeof(struct hbm_add_client_response));

	resp->hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD;
	resp->me_addr = addr;
	resp->status  = status;

	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret)
		dev_err(dev->dev, "add client response write failed: ret = %d\n",
			ret);
	return ret;
}

/**
 * mei_hbm_fw_add_cl_req - request from the fw to add a client
 *
 * @dev: the device structure
 * @req: add client request
 *
 * Return: 0 on success and < 0 on failure
 */
static int mei_hbm_fw_add_cl_req(struct mei_device *dev,
			      struct hbm_add_client_request *req)
{
	int ret;
	u8 status = MEI_HBMS_SUCCESS;

	BUILD_BUG_ON(sizeof(struct hbm_add_client_request) !=
			sizeof(struct hbm_props_response));

	ret = mei_hbm_me_cl_add(dev, (struct hbm_props_response *)req);
	if (ret)
		status = !MEI_HBMS_SUCCESS;

	return mei_hbm_add_cl_resp(dev, req->me_addr, status);
}

405
/**
406
 * mei_hbm_prop_req - request property for a single client
407 408 409
 *
 * @dev: the device structure
 *
410
 * Return: 0 on success and < 0 on failure
411
 */
412

413
static int mei_hbm_prop_req(struct mei_device *dev)
414 415
{

416
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
417 418 419
	struct hbm_props_request *prop_req;
	const size_t len = sizeof(struct hbm_props_request);
	unsigned long next_client_index;
420
	int ret;
421 422 423 424 425 426

	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 已提交
427
		dev->hbm_state = MEI_HBM_STARTED;
428 429 430 431 432
		schedule_work(&dev->init_work);

		return 0;
	}

433 434
	mei_hbm_hdr(mei_hdr, len);
	prop_req = (struct hbm_props_request *)dev->wr_msg.data;
435 436 437 438

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

	prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
439
	prop_req->me_addr = next_client_index;
440

441 442
	ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
	if (ret) {
443
		dev_err(dev->dev, "properties request write failed: ret = %d\n",
444 445
			ret);
		return ret;
446 447 448 449 450 451 452 453
	}

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

	return 0;
}

454 455 456 457 458 459
/*
 * mei_hbm_pg - sends pg command
 *
 * @dev: the device structure
 * @pg_cmd: the pg command code
 *
460
 * Return: -EIO on write failure
461
 *         -EOPNOTSUPP if the operation is not supported by the protocol
462 463 464 465 466 467 468 469
 */
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;

470 471 472
	if (!dev->hbm_f_pg_supported)
		return -EOPNOTSUPP;

473 474 475 476 477 478 479 480
	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)
481
		dev_err(dev->dev, "power gate command write failed.\n");
482 483 484 485
	return ret;
}
EXPORT_SYMBOL_GPL(mei_hbm_pg);

486
/**
T
Tomas Winkler 已提交
487
 * mei_hbm_stop_req - send stop request message
488
 *
489
 * @dev: mei device
T
Tomas Winkler 已提交
490
 *
491
 * Return: -EIO on write failure
492
 */
T
Tomas Winkler 已提交
493
static int mei_hbm_stop_req(struct mei_device *dev)
494
{
T
Tomas Winkler 已提交
495
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
496
	struct hbm_host_stop_request *req =
T
Tomas Winkler 已提交
497
			(struct hbm_host_stop_request *)dev->wr_msg.data;
498 499 500 501 502 503 504
	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 已提交
505 506

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

509
/**
510
 * mei_hbm_cl_flow_control_req - sends flow control request.
511 512
 *
 * @dev: the device structure
513
 * @cl: client info
514
 *
515
 * Return: -EIO on write failure
516
 */
517
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
518 519
{
	const size_t len = sizeof(struct hbm_flow_control);
520

521
	cl_dbg(dev, cl, "sending flow control\n");
522
	return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, len);
523 524
}

525
/**
526
 * mei_hbm_add_single_flow_creds - adds single buffer credentials.
527
 *
528
 * @dev: the device structure
529
 * @flow: flow control.
530
 *
531
 * Return: 0 on success, < 0 otherwise
532
 */
533
static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
534 535
				  struct hbm_flow_control *flow)
{
536
	struct mei_me_client *me_cl;
537
	int rets;
538

539 540
	me_cl = mei_me_cl_by_id(dev, flow->me_addr);
	if (!me_cl) {
541
		dev_err(dev->dev, "no such me client %d\n",
542
			flow->me_addr);
543
		return -ENOENT;
544 545
	}

546 547 548 549
	if (WARN_ON(me_cl->props.single_recv_buf == 0)) {
		rets = -EINVAL;
		goto out;
	}
550 551

	me_cl->mei_flow_ctrl_creds++;
552
	dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
553
	    flow->me_addr, me_cl->mei_flow_ctrl_creds);
554

555 556 557 558
	rets = 0;
out:
	mei_me_cl_put(me_cl);
	return rets;
559 560 561 562 563 564 565 566 567
}

/**
 * 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,
568
					struct hbm_flow_control *flow_control)
569
{
570
	struct mei_cl *cl;
571 572 573 574 575 576 577

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

578 579 580 581
	cl = mei_hbm_cl_find_by_cmd(dev, flow_control);
	if (cl) {
		cl->mei_flow_ctrl_creds++;
		cl_dbg(dev, cl, "flow control creds = %d.\n",
582
				cl->mei_flow_ctrl_creds);
583 584 585 586
	}
}


587
/**
588
 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
589 590
 *
 * @dev: the device structure
591
 * @cl: a client to disconnect from
592
 *
593
 * Return: -EIO on write failure
594
 */
595
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
596 597
{
	const size_t len = sizeof(struct hbm_client_connect_request);
598

599
	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, len);
600 601
}

T
Tomas Winkler 已提交
602 603 604 605 606 607
/**
 * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
 *
 * @dev: the device structure
 * @cl: a client to disconnect from
 *
608
 * Return: -EIO on write failure
T
Tomas Winkler 已提交
609 610 611 612
 */
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
{
	const size_t len = sizeof(struct hbm_client_connect_response);
613

614
	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, len);
T
Tomas Winkler 已提交
615 616
}

617
/**
618 619
 * mei_hbm_cl_disconnect_res - update the client state according
 *       disconnect response
620
 *
621
 * @dev: the device structure
622 623
 * @cl: mei host client
 * @cmd: disconnect client response host bus message
624
 */
625
static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl,
626
				      struct mei_hbm_cl_cmd *cmd)
627
{
628 629
	struct hbm_client_connect_response *rs =
		(struct hbm_client_connect_response *)cmd;
630

631
	cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status);
632

633
	if (rs->status == MEI_CL_DISCONN_SUCCESS)
634
		cl->state = MEI_FILE_DISCONNECT_REPLY;
635
	cl->status = 0;
636 637
}

638
/**
639
 * mei_hbm_cl_connect_req - send connection request to specific me client
640 641
 *
 * @dev: the device structure
642
 * @cl: a client to connect to
643
 *
644
 * Return: -EIO on write failure
645
 */
646
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
647 648
{
	const size_t len = sizeof(struct hbm_client_connect_request);
649

650
	return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, len);
651 652
}

653
/**
654 655
 * mei_hbm_cl_connect_res - update the client state according
 *        connection response
656
 *
657
 * @dev: the device structure
658 659
 * @cl: mei host client
 * @cmd: connect client response host bus message
660
 */
661
static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
662
				   struct mei_hbm_cl_cmd *cmd)
663
{
664 665
	struct hbm_client_connect_response *rs =
		(struct hbm_client_connect_response *)cmd;
666

667
	cl_dbg(dev, cl, "hbm: connect response status=%s\n",
668
			mei_cl_conn_status_str(rs->status));
669

670 671
	if (rs->status == MEI_CL_CONN_SUCCESS)
		cl->state = MEI_FILE_CONNECTED;
T
Tomas Winkler 已提交
672
	else {
673
		cl->state = MEI_FILE_DISCONNECT_REPLY;
T
Tomas Winkler 已提交
674 675 676
		if (rs->status == MEI_CL_CONN_NOT_FOUND)
			mei_me_cl_del(dev, cl->me_cl);
	}
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
	cl->status = mei_cl_conn_status_to_errno(rs->status);
}

/**
 * mei_hbm_cl_res - process hbm response received on behalf
 *         an client
 *
 * @dev: the device structure
 * @rs:  hbm client message
 * @fop_type: file operation type
 */
static void mei_hbm_cl_res(struct mei_device *dev,
			   struct mei_hbm_cl_cmd *rs,
			   enum mei_cb_file_ops fop_type)
{
	struct mei_cl *cl;
	struct mei_cl_cb *cb, *next;
694

695
	cl = NULL;
696
	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
697

698
		cl = cb->cl;
699

700
		if (cb->fop_type != fop_type)
701
			continue;
702

703
		if (mei_hbm_cl_addr_equal(cl, rs)) {
704
			list_del_init(&cb->list);
705
			break;
706 707
		}
	}
708 709 710 711

	if (!cl)
		return;

712 713
	switch (fop_type) {
	case MEI_FOP_CONNECT:
714
		mei_hbm_cl_connect_res(dev, cl, rs);
715 716
		break;
	case MEI_FOP_DISCONNECT:
717
		mei_hbm_cl_disconnect_res(dev, cl, rs);
718 719 720 721 722
		break;
	default:
		return;
	}

723
	cl->timer_count = 0;
724
	wake_up(&cl->wait);
725 726 727
}


728
/**
729 730
 * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
 *  host sends disconnect response
731 732
 *
 * @dev: the device structure.
733
 * @disconnect_req: disconnect request bus message from the me
T
Tomas Winkler 已提交
734
 *
735
 * Return: -ENOMEM on allocation failure
736
 */
T
Tomas Winkler 已提交
737
static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
738 739
		struct hbm_client_connect_request *disconnect_req)
{
740
	struct mei_cl *cl;
T
Tomas Winkler 已提交
741
	struct mei_cl_cb *cb;
742

743 744
	cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
	if (cl) {
745 746
		cl_dbg(dev, cl, "fw disconnect request received\n");
		cl->state = MEI_FILE_DISCONNECTING;
747 748
		cl->timer_count = 0;

749
		cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
750 751 752 753
		if (!cb)
			return -ENOMEM;
		cl_dbg(dev, cl, "add disconnect response as first\n");
		list_add(&cb->list, &dev->ctrl_wr_list.list);
754
	}
T
Tomas Winkler 已提交
755
	return 0;
756 757
}

758
/**
759
 * mei_hbm_config_features - check what hbm features and commands
760 761 762 763 764 765 766 767 768 769 770 771 772 773
 *        are supported by the fw
 *
 * @dev: the device structure
 */
static void mei_hbm_config_features(struct mei_device *dev)
{
	/* Power Gating Isolation Support */
	dev->hbm_f_pg_supported = 0;
	if (dev->version.major_version > HBM_MAJOR_VERSION_PGI)
		dev->hbm_f_pg_supported = 1;

	if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
	    dev->version.minor_version >= HBM_MINOR_VERSION_PGI)
		dev->hbm_f_pg_supported = 1;
T
Tomas Winkler 已提交
774 775 776

	if (dev->version.major_version >= HBM_MAJOR_VERSION_DC)
		dev->hbm_f_dc_supported = 1;
777
}
778

T
Tomas Winkler 已提交
779 780 781 782 783
/**
 * mei_hbm_version_is_supported - checks whether the driver can
 *     support the hbm version of the device
 *
 * @dev: the device structure
784
 * Return: true if driver can support hbm version of the device
T
Tomas Winkler 已提交
785 786 787 788 789 790 791 792
 */
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);
}

793 794 795 796 797
/**
 * mei_hbm_dispatch - bottom half read routine after ISR to
 * handle the read bus message cmd processing.
 *
 * @dev: the device structure
798
 * @hdr: header of bus message
799
 *
800
 * Return: 0 on success and < 0 on failure
801
 */
802
int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
803 804 805 806 807
{
	struct mei_bus_message *mei_msg;
	struct hbm_host_version_response *version_res;
	struct hbm_props_response *props_res;
	struct hbm_host_enum_response *enum_res;
T
Tomas Winkler 已提交
808 809
	struct hbm_add_client_request *add_cl_req;
	int ret;
810

811 812 813 814
	struct mei_hbm_cl_cmd *cl_cmd;
	struct hbm_client_connect_request *disconnect_req;
	struct hbm_flow_control *flow_control;

815 816 817 818
	/* 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;
819
	cl_cmd  = (struct mei_hbm_cl_cmd *)mei_msg;
820

821 822 823 824
	/* ignore spurious message and prevent reset nesting
	 * hbm is put to idle during system reset
	 */
	if (dev->hbm_state == MEI_HBM_IDLE) {
825
		dev_dbg(dev->dev, "hbm: state is idle ignore spurious messages\n");
826 827 828
		return 0;
	}

829 830
	switch (mei_msg->hbm_cmd) {
	case HOST_START_RES_CMD:
831
		dev_dbg(dev->dev, "hbm: start: response message received.\n");
832 833 834

		dev->init_clients_timer = 0;

835
		version_res = (struct hbm_host_version_response *)mei_msg;
T
Tomas Winkler 已提交
836

837
		dev_dbg(dev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
T
Tomas Winkler 已提交
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
				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)) {
853
			dev_warn(dev->dev, "hbm: start: version mismatch - stopping the driver.\n");
854

855
			dev->hbm_state = MEI_HBM_STOPPED;
T
Tomas Winkler 已提交
856
			if (mei_hbm_stop_req(dev)) {
857
				dev_err(dev->dev, "hbm: start: failed to send stop request\n");
858 859 860 861
				return -EIO;
			}
			break;
		}
T
Tomas Winkler 已提交
862

863 864
		mei_hbm_config_features(dev);

865
		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
866
		    dev->hbm_state != MEI_HBM_STARTING) {
867
			dev_err(dev->dev, "hbm: start: state mismatch, [%d, %d]\n",
868 869
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
870
		}
871

872
		if (mei_hbm_enum_clients_req(dev)) {
873
			dev_err(dev->dev, "hbm: start: failed to send enumeration request\n");
874
			return -EIO;
875 876
		}

877
		wake_up(&dev->wait_hbm_start);
878 879 880
		break;

	case CLIENT_CONNECT_RES_CMD:
881
		dev_dbg(dev->dev, "hbm: client connect response: message received.\n");
882
		mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT);
883 884 885
		break;

	case CLIENT_DISCONNECT_RES_CMD:
886
		dev_dbg(dev->dev, "hbm: client disconnect response: message received.\n");
887
		mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT);
888 889 890
		break;

	case MEI_FLOW_CONTROL_CMD:
891
		dev_dbg(dev->dev, "hbm: client flow control response: message received.\n");
892

893
		flow_control = (struct hbm_flow_control *) mei_msg;
894
		mei_hbm_cl_flow_control_res(dev, flow_control);
895 896
		break;

897
	case MEI_PG_ISOLATION_ENTRY_RES_CMD:
898
		dev_dbg(dev->dev, "power gate isolation entry response received\n");
899
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
900 901 902 903 904
		if (waitqueue_active(&dev->wait_pg))
			wake_up(&dev->wait_pg);
		break;

	case MEI_PG_ISOLATION_EXIT_REQ_CMD:
905
		dev_dbg(dev->dev, "power gate isolation exit request received\n");
906
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
907 908
		if (waitqueue_active(&dev->wait_pg))
			wake_up(&dev->wait_pg);
909 910 911 912 913 914
		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.
			*/
915
			pm_request_resume(dev->dev);
916 917
		break;

918
	case HOST_CLIENT_PROPERTIES_RES_CMD:
919
		dev_dbg(dev->dev, "hbm: properties response: message received.\n");
920 921 922

		dev->init_clients_timer = 0;

923 924
		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
		    dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
925
			dev_err(dev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
926
				dev->dev_state, dev->hbm_state);
927 928 929
			return -EPROTO;
		}

930 931
		props_res = (struct hbm_props_response *)mei_msg;

932
		if (props_res->status) {
933
			dev_err(dev->dev, "hbm: properties response: wrong status = %d %s\n",
934 935
				props_res->status,
				mei_hbm_status_str(props_res->status));
936
			return -EPROTO;
937 938
		}

939
		mei_hbm_me_cl_add(dev, props_res);
940 941 942

		dev->me_client_index++;

943
		/* request property for the next client */
944 945
		if (mei_hbm_prop_req(dev))
			return -EIO;
946 947 948 949

		break;

	case HOST_ENUM_RES_CMD:
950
		dev_dbg(dev->dev, "hbm: enumeration response: message received\n");
951 952 953

		dev->init_clients_timer = 0;

954
		enum_res = (struct hbm_host_enum_response *) mei_msg;
955 956 957
		BUILD_BUG_ON(sizeof(dev->me_clients_map)
				< sizeof(enum_res->valid_addresses));
		memcpy(dev->me_clients_map, enum_res->valid_addresses,
958
				sizeof(enum_res->valid_addresses));
959 960 961

		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
		    dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
962
			dev_err(dev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
963 964
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
965
		}
966 967 968 969 970 971 972

		dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;

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

973 974 975
		break;

	case HOST_STOP_RES_CMD:
976
		dev_dbg(dev->dev, "hbm: stop response: message received\n");
977 978 979 980

		dev->init_clients_timer = 0;

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

986
		dev->dev_state = MEI_DEV_POWER_DOWN;
987
		dev_info(dev->dev, "hbm: stop response: resetting.\n");
988 989
		/* force the reset */
		return -EPROTO;
990 991 992
		break;

	case CLIENT_DISCONNECT_REQ_CMD:
993
		dev_dbg(dev->dev, "hbm: disconnect request: message received\n");
994

995
		disconnect_req = (struct hbm_client_connect_request *)mei_msg;
996
		mei_hbm_fw_disconnect_req(dev, disconnect_req);
997 998 999
		break;

	case ME_STOP_REQ_CMD:
1000
		dev_dbg(dev->dev, "hbm: stop request: message received\n");
1001
		dev->hbm_state = MEI_HBM_STOPPED;
T
Tomas Winkler 已提交
1002
		if (mei_hbm_stop_req(dev)) {
1003
			dev_err(dev->dev, "hbm: stop request: failed to send stop request\n");
T
Tomas Winkler 已提交
1004 1005
			return -EIO;
		}
1006
		break;
T
Tomas Winkler 已提交
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029

	case MEI_HBM_ADD_CLIENT_REQ_CMD:
		dev_dbg(dev->dev, "hbm: add client request received\n");
		/*
		 * after the host receives the enum_resp
		 * message clients may be added or removed
		 */
		if (dev->hbm_state <= MEI_HBM_ENUM_CLIENTS &&
		    dev->hbm_state >= MEI_HBM_STOPPED) {
			dev_err(dev->dev, "hbm: add client: state mismatch, [%d, %d]\n",
				dev->dev_state, dev->hbm_state);
			return -EPROTO;
		}
		add_cl_req = (struct hbm_add_client_request *)mei_msg;
		ret = mei_hbm_fw_add_cl_req(dev, add_cl_req);
		if (ret) {
			dev_err(dev->dev, "hbm: add client: failed to send response %d\n",
				ret);
			return -EIO;
		}
		dev_dbg(dev->dev, "hbm: add client request processed\n");
		break;

1030 1031 1032 1033 1034
	default:
		BUG();
		break;

	}
1035
	return 0;
1036 1037
}