162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2003-2022, Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/export.h>
762306a36Sopenharmony_ci#include <linux/sched.h>
862306a36Sopenharmony_ci#include <linux/wait.h>
962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/mei.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "mei_dev.h"
1562306a36Sopenharmony_ci#include "hbm.h"
1662306a36Sopenharmony_ci#include "client.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic const char *mei_hbm_status_str(enum mei_hbm_status status)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci#define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status
2162306a36Sopenharmony_ci	switch (status) {
2262306a36Sopenharmony_ci	MEI_HBM_STATUS(SUCCESS);
2362306a36Sopenharmony_ci	MEI_HBM_STATUS(CLIENT_NOT_FOUND);
2462306a36Sopenharmony_ci	MEI_HBM_STATUS(ALREADY_EXISTS);
2562306a36Sopenharmony_ci	MEI_HBM_STATUS(REJECTED);
2662306a36Sopenharmony_ci	MEI_HBM_STATUS(INVALID_PARAMETER);
2762306a36Sopenharmony_ci	MEI_HBM_STATUS(NOT_ALLOWED);
2862306a36Sopenharmony_ci	MEI_HBM_STATUS(ALREADY_STARTED);
2962306a36Sopenharmony_ci	MEI_HBM_STATUS(NOT_STARTED);
3062306a36Sopenharmony_ci	default: return "unknown";
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci#undef MEI_HBM_STATUS
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
3862306a36Sopenharmony_ci	switch (status) {
3962306a36Sopenharmony_ci	MEI_CL_CS(SUCCESS);
4062306a36Sopenharmony_ci	MEI_CL_CS(NOT_FOUND);
4162306a36Sopenharmony_ci	MEI_CL_CS(ALREADY_STARTED);
4262306a36Sopenharmony_ci	MEI_CL_CS(OUT_OF_RESOURCES);
4362306a36Sopenharmony_ci	MEI_CL_CS(MESSAGE_SMALL);
4462306a36Sopenharmony_ci	MEI_CL_CS(NOT_ALLOWED);
4562306a36Sopenharmony_ci	default: return "unknown";
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci#undef MEI_CL_CCS
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciconst char *mei_hbm_state_str(enum mei_hbm_state state)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci#define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state
5362306a36Sopenharmony_ci	switch (state) {
5462306a36Sopenharmony_ci	MEI_HBM_STATE(IDLE);
5562306a36Sopenharmony_ci	MEI_HBM_STATE(STARTING);
5662306a36Sopenharmony_ci	MEI_HBM_STATE(STARTED);
5762306a36Sopenharmony_ci	MEI_HBM_STATE(DR_SETUP);
5862306a36Sopenharmony_ci	MEI_HBM_STATE(ENUM_CLIENTS);
5962306a36Sopenharmony_ci	MEI_HBM_STATE(CLIENT_PROPERTIES);
6062306a36Sopenharmony_ci	MEI_HBM_STATE(STOPPED);
6162306a36Sopenharmony_ci	default:
6262306a36Sopenharmony_ci		return "unknown";
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci#undef MEI_HBM_STATE
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**
6862306a36Sopenharmony_ci * mei_cl_conn_status_to_errno - convert client connect response
6962306a36Sopenharmony_ci * status to error code
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * @status: client connect response status
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Return: corresponding error code
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cistatic int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	switch (status) {
7862306a36Sopenharmony_ci	case MEI_CL_CONN_SUCCESS:          return 0;
7962306a36Sopenharmony_ci	case MEI_CL_CONN_NOT_FOUND:        return -ENOTTY;
8062306a36Sopenharmony_ci	case MEI_CL_CONN_ALREADY_STARTED:  return -EBUSY;
8162306a36Sopenharmony_ci	case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
8262306a36Sopenharmony_ci	case MEI_CL_CONN_MESSAGE_SMALL:    return -EINVAL;
8362306a36Sopenharmony_ci	case MEI_CL_CONN_NOT_ALLOWED:      return -EBUSY;
8462306a36Sopenharmony_ci	default:                           return -EINVAL;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/**
8962306a36Sopenharmony_ci * mei_hbm_write_message - wrapper for sending hbm messages.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * @dev: mei device
9262306a36Sopenharmony_ci * @hdr: mei header
9362306a36Sopenharmony_ci * @data: payload
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_cistatic inline int mei_hbm_write_message(struct mei_device *dev,
9662306a36Sopenharmony_ci					struct mei_msg_hdr *hdr,
9762306a36Sopenharmony_ci					const void *data)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return mei_write_message(dev, hdr, sizeof(*hdr), data, hdr->length);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**
10362306a36Sopenharmony_ci * mei_hbm_idle - set hbm to idle state
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * @dev: the device structure
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_civoid mei_hbm_idle(struct mei_device *dev)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	dev->init_clients_timer = 0;
11062306a36Sopenharmony_ci	dev->hbm_state = MEI_HBM_IDLE;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/**
11462306a36Sopenharmony_ci * mei_hbm_reset - reset hbm counters and book keeping data structurs
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * @dev: the device structure
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_civoid mei_hbm_reset(struct mei_device *dev)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	mei_me_cl_rm_all(dev);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	mei_hbm_idle(dev);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/**
12662306a36Sopenharmony_ci * mei_hbm_hdr - construct hbm header
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * @mei_hdr: hbm header
12962306a36Sopenharmony_ci * @length: payload length
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic inline void mei_hbm_hdr(struct mei_msg_hdr *mei_hdr, size_t length)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	memset(mei_hdr, 0, sizeof(*mei_hdr));
13562306a36Sopenharmony_ci	mei_hdr->length = length;
13662306a36Sopenharmony_ci	mei_hdr->msg_complete = 1;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/**
14062306a36Sopenharmony_ci * mei_hbm_cl_hdr - construct client hbm header
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * @cl: client
14362306a36Sopenharmony_ci * @hbm_cmd: host bus message command
14462306a36Sopenharmony_ci * @buf: buffer for cl header
14562306a36Sopenharmony_ci * @len: buffer length
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic inline
14862306a36Sopenharmony_civoid mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct mei_hbm_cl_cmd *cmd = buf;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	memset(cmd, 0, len);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	cmd->hbm_cmd = hbm_cmd;
15562306a36Sopenharmony_ci	cmd->host_addr = mei_cl_host_addr(cl);
15662306a36Sopenharmony_ci	cmd->me_addr = mei_cl_me_id(cl);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/**
16062306a36Sopenharmony_ci * mei_hbm_cl_write - write simple hbm client message
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * @dev: the device structure
16362306a36Sopenharmony_ci * @cl: client
16462306a36Sopenharmony_ci * @hbm_cmd: host bus message command
16562306a36Sopenharmony_ci * @buf: message buffer
16662306a36Sopenharmony_ci * @len: buffer length
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci * Return: 0 on success, <0 on failure.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic inline int mei_hbm_cl_write(struct mei_device *dev, struct mei_cl *cl,
17162306a36Sopenharmony_ci				   u8 hbm_cmd, void *buf, size_t len)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, len);
17662306a36Sopenharmony_ci	mei_hbm_cl_hdr(cl, hbm_cmd, buf, len);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return mei_hbm_write_message(dev, &mei_hdr, buf);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * mei_hbm_cl_addr_equal - check if the client's and
18362306a36Sopenharmony_ci *	the message address match
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * @cl: client
18662306a36Sopenharmony_ci * @cmd: hbm client message
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * Return: true if addresses are the same
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_cistatic inline
19162306a36Sopenharmony_cibool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	return  mei_cl_host_addr(cl) == cmd->host_addr &&
19462306a36Sopenharmony_ci		mei_cl_me_id(cl) == cmd->me_addr;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * mei_hbm_cl_find_by_cmd - find recipient client
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * @dev: the device structure
20162306a36Sopenharmony_ci * @buf: a buffer with hbm cl command
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * Return: the recipient client or NULL if not found
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic inline
20662306a36Sopenharmony_cistruct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf;
20962306a36Sopenharmony_ci	struct mei_cl *cl;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	list_for_each_entry(cl, &dev->file_list, link)
21262306a36Sopenharmony_ci		if (mei_hbm_cl_addr_equal(cl, cmd))
21362306a36Sopenharmony_ci			return cl;
21462306a36Sopenharmony_ci	return NULL;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/**
21962306a36Sopenharmony_ci * mei_hbm_start_wait - wait for start response message.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * @dev: the device structure
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_ciint mei_hbm_start_wait(struct mei_device *dev)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	int ret;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (dev->hbm_state > MEI_HBM_STARTING)
23062306a36Sopenharmony_ci		return 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
23362306a36Sopenharmony_ci	ret = wait_event_timeout(dev->wait_hbm_start,
23462306a36Sopenharmony_ci			dev->hbm_state != MEI_HBM_STARTING,
23562306a36Sopenharmony_ci			dev->timeouts.hbm);
23662306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) {
23962306a36Sopenharmony_ci		dev->hbm_state = MEI_HBM_IDLE;
24062306a36Sopenharmony_ci		dev_err(dev->dev, "waiting for mei start failed\n");
24162306a36Sopenharmony_ci		return -ETIME;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/**
24762306a36Sopenharmony_ci * mei_hbm_start_req - sends start request message.
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * @dev: the device structure
25062306a36Sopenharmony_ci *
25162306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_ciint mei_hbm_start_req(struct mei_device *dev)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
25662306a36Sopenharmony_ci	struct hbm_host_version_request req;
25762306a36Sopenharmony_ci	int ret;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	mei_hbm_reset(dev);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* host start message */
26462306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
26562306a36Sopenharmony_ci	req.hbm_cmd = HOST_START_REQ_CMD;
26662306a36Sopenharmony_ci	req.host_version.major_version = HBM_MAJOR_VERSION;
26762306a36Sopenharmony_ci	req.host_version.minor_version = HBM_MINOR_VERSION;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	dev->hbm_state = MEI_HBM_IDLE;
27062306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
27162306a36Sopenharmony_ci	if (ret) {
27262306a36Sopenharmony_ci		dev_err(dev->dev, "version message write failed: ret = %d\n",
27362306a36Sopenharmony_ci			ret);
27462306a36Sopenharmony_ci		return ret;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	dev->hbm_state = MEI_HBM_STARTING;
27862306a36Sopenharmony_ci	dev->init_clients_timer = dev->timeouts.client_init;
27962306a36Sopenharmony_ci	mei_schedule_stall_timer(dev);
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/**
28462306a36Sopenharmony_ci * mei_hbm_dma_setup_req() - setup DMA request
28562306a36Sopenharmony_ci * @dev: the device structure
28662306a36Sopenharmony_ci *
28762306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_cistatic int mei_hbm_dma_setup_req(struct mei_device *dev)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
29262306a36Sopenharmony_ci	struct hbm_dma_setup_request req;
29362306a36Sopenharmony_ci	unsigned int i;
29462306a36Sopenharmony_ci	int ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
29962306a36Sopenharmony_ci	req.hbm_cmd = MEI_HBM_DMA_SETUP_REQ_CMD;
30062306a36Sopenharmony_ci	for (i = 0; i < DMA_DSCR_NUM; i++) {
30162306a36Sopenharmony_ci		phys_addr_t paddr;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		paddr = dev->dr_dscr[i].daddr;
30462306a36Sopenharmony_ci		req.dma_dscr[i].addr_hi = upper_32_bits(paddr);
30562306a36Sopenharmony_ci		req.dma_dscr[i].addr_lo = lower_32_bits(paddr);
30662306a36Sopenharmony_ci		req.dma_dscr[i].size = dev->dr_dscr[i].size;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	mei_dma_ring_reset(dev);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
31262306a36Sopenharmony_ci	if (ret) {
31362306a36Sopenharmony_ci		dev_err(dev->dev, "dma setup request write failed: ret = %d.\n",
31462306a36Sopenharmony_ci			ret);
31562306a36Sopenharmony_ci		return ret;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	dev->hbm_state = MEI_HBM_DR_SETUP;
31962306a36Sopenharmony_ci	dev->init_clients_timer = dev->timeouts.client_init;
32062306a36Sopenharmony_ci	mei_schedule_stall_timer(dev);
32162306a36Sopenharmony_ci	return 0;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/**
32562306a36Sopenharmony_ci * mei_hbm_capabilities_req - request capabilities
32662306a36Sopenharmony_ci *
32762306a36Sopenharmony_ci * @dev: the device structure
32862306a36Sopenharmony_ci *
32962306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic int mei_hbm_capabilities_req(struct mei_device *dev)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
33462306a36Sopenharmony_ci	struct hbm_capability_request req;
33562306a36Sopenharmony_ci	int ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
34062306a36Sopenharmony_ci	req.hbm_cmd = MEI_HBM_CAPABILITIES_REQ_CMD;
34162306a36Sopenharmony_ci	if (dev->hbm_f_vt_supported)
34262306a36Sopenharmony_ci		req.capability_requested[0] |= HBM_CAP_VT;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (dev->hbm_f_cd_supported)
34562306a36Sopenharmony_ci		req.capability_requested[0] |= HBM_CAP_CD;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (dev->hbm_f_gsc_supported)
34862306a36Sopenharmony_ci		req.capability_requested[0] |= HBM_CAP_GSC;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
35162306a36Sopenharmony_ci	if (ret) {
35262306a36Sopenharmony_ci		dev_err(dev->dev,
35362306a36Sopenharmony_ci			"capabilities request write failed: ret = %d.\n", ret);
35462306a36Sopenharmony_ci		return ret;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	dev->hbm_state = MEI_HBM_CAP_SETUP;
35862306a36Sopenharmony_ci	dev->init_clients_timer = dev->timeouts.client_init;
35962306a36Sopenharmony_ci	mei_schedule_stall_timer(dev);
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/**
36462306a36Sopenharmony_ci * mei_hbm_enum_clients_req - sends enumeration client request message.
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci * @dev: the device structure
36762306a36Sopenharmony_ci *
36862306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_cistatic int mei_hbm_enum_clients_req(struct mei_device *dev)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
37362306a36Sopenharmony_ci	struct hbm_host_enum_request req;
37462306a36Sopenharmony_ci	int ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* enumerate clients */
37762306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
38062306a36Sopenharmony_ci	req.hbm_cmd = HOST_ENUM_REQ_CMD;
38162306a36Sopenharmony_ci	req.flags |= dev->hbm_f_dc_supported ? MEI_HBM_ENUM_F_ALLOW_ADD : 0;
38262306a36Sopenharmony_ci	req.flags |= dev->hbm_f_ie_supported ?
38362306a36Sopenharmony_ci			  MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
38662306a36Sopenharmony_ci	if (ret) {
38762306a36Sopenharmony_ci		dev_err(dev->dev, "enumeration request write failed: ret = %d.\n",
38862306a36Sopenharmony_ci			ret);
38962306a36Sopenharmony_ci		return ret;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
39262306a36Sopenharmony_ci	dev->init_clients_timer = dev->timeouts.client_init;
39362306a36Sopenharmony_ci	mei_schedule_stall_timer(dev);
39462306a36Sopenharmony_ci	return 0;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/**
39862306a36Sopenharmony_ci * mei_hbm_me_cl_add - add new me client to the list
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * @dev: the device structure
40162306a36Sopenharmony_ci * @res: hbm property response
40262306a36Sopenharmony_ci *
40362306a36Sopenharmony_ci * Return: 0 on success and -ENOMEM on allocation failure
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int mei_hbm_me_cl_add(struct mei_device *dev,
40762306a36Sopenharmony_ci			     struct hbm_props_response *res)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct mei_me_client *me_cl;
41062306a36Sopenharmony_ci	const uuid_le *uuid = &res->client_properties.protocol_name;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	mei_me_cl_rm_by_uuid(dev, uuid);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	me_cl = kzalloc(sizeof(*me_cl), GFP_KERNEL);
41562306a36Sopenharmony_ci	if (!me_cl)
41662306a36Sopenharmony_ci		return -ENOMEM;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	mei_me_cl_init(me_cl);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	me_cl->props = res->client_properties;
42162306a36Sopenharmony_ci	me_cl->client_id = res->me_addr;
42262306a36Sopenharmony_ci	me_cl->tx_flow_ctrl_creds = 0;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	mei_me_cl_add(dev, me_cl);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return 0;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/**
43062306a36Sopenharmony_ci * mei_hbm_add_cl_resp - send response to fw on client add request
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * @dev: the device structure
43362306a36Sopenharmony_ci * @addr: me address
43462306a36Sopenharmony_ci * @status: response status
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
43762306a36Sopenharmony_ci */
43862306a36Sopenharmony_cistatic int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
44162306a36Sopenharmony_ci	struct hbm_add_client_response resp;
44262306a36Sopenharmony_ci	int ret;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	dev_dbg(dev->dev, "adding client response\n");
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(resp));
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	memset(&resp, 0, sizeof(resp));
44962306a36Sopenharmony_ci	resp.hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD;
45062306a36Sopenharmony_ci	resp.me_addr = addr;
45162306a36Sopenharmony_ci	resp.status  = status;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &resp);
45462306a36Sopenharmony_ci	if (ret)
45562306a36Sopenharmony_ci		dev_err(dev->dev, "add client response write failed: ret = %d\n",
45662306a36Sopenharmony_ci			ret);
45762306a36Sopenharmony_ci	return ret;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/**
46162306a36Sopenharmony_ci * mei_hbm_fw_add_cl_req - request from the fw to add a client
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * @dev: the device structure
46462306a36Sopenharmony_ci * @req: add client request
46562306a36Sopenharmony_ci *
46662306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
46762306a36Sopenharmony_ci */
46862306a36Sopenharmony_cistatic int mei_hbm_fw_add_cl_req(struct mei_device *dev,
46962306a36Sopenharmony_ci			      struct hbm_add_client_request *req)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	int ret;
47262306a36Sopenharmony_ci	u8 status = MEI_HBMS_SUCCESS;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct hbm_add_client_request) !=
47562306a36Sopenharmony_ci			sizeof(struct hbm_props_response));
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	ret = mei_hbm_me_cl_add(dev, (struct hbm_props_response *)req);
47862306a36Sopenharmony_ci	if (ret)
47962306a36Sopenharmony_ci		status = !MEI_HBMS_SUCCESS;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (dev->dev_state == MEI_DEV_ENABLED)
48262306a36Sopenharmony_ci		schedule_work(&dev->bus_rescan_work);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return mei_hbm_add_cl_resp(dev, req->me_addr, status);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/**
48862306a36Sopenharmony_ci * mei_hbm_cl_notify_req - send notification request
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci * @dev: the device structure
49162306a36Sopenharmony_ci * @cl: a client to disconnect from
49262306a36Sopenharmony_ci * @start: true for start false for stop
49362306a36Sopenharmony_ci *
49462306a36Sopenharmony_ci * Return: 0 on success and -EIO on write failure
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_ciint mei_hbm_cl_notify_req(struct mei_device *dev,
49762306a36Sopenharmony_ci			  struct mei_cl *cl, u8 start)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
50162306a36Sopenharmony_ci	struct hbm_notification_request req;
50262306a36Sopenharmony_ci	int ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
50562306a36Sopenharmony_ci	mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, &req, sizeof(req));
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	req.start = start;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
51062306a36Sopenharmony_ci	if (ret)
51162306a36Sopenharmony_ci		dev_err(dev->dev, "notify request failed: ret = %d\n", ret);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return ret;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci/**
51762306a36Sopenharmony_ci *  notify_res_to_fop - convert notification response to the proper
51862306a36Sopenharmony_ci *      notification FOP
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci * @cmd: client notification start response command
52162306a36Sopenharmony_ci *
52262306a36Sopenharmony_ci * Return:  MEI_FOP_NOTIFY_START or MEI_FOP_NOTIFY_STOP;
52362306a36Sopenharmony_ci */
52462306a36Sopenharmony_cistatic inline enum mei_cb_file_ops notify_res_to_fop(struct mei_hbm_cl_cmd *cmd)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct hbm_notification_response *rs =
52762306a36Sopenharmony_ci		(struct hbm_notification_response *)cmd;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return mei_cl_notify_req2fop(rs->start);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/**
53362306a36Sopenharmony_ci * mei_hbm_cl_notify_start_res - update the client state according
53462306a36Sopenharmony_ci *       notify start response
53562306a36Sopenharmony_ci *
53662306a36Sopenharmony_ci * @dev: the device structure
53762306a36Sopenharmony_ci * @cl: mei host client
53862306a36Sopenharmony_ci * @cmd: client notification start response command
53962306a36Sopenharmony_ci */
54062306a36Sopenharmony_cistatic void mei_hbm_cl_notify_start_res(struct mei_device *dev,
54162306a36Sopenharmony_ci					struct mei_cl *cl,
54262306a36Sopenharmony_ci					struct mei_hbm_cl_cmd *cmd)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct hbm_notification_response *rs =
54562306a36Sopenharmony_ci		(struct hbm_notification_response *)cmd;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	cl_dbg(dev, cl, "hbm: notify start response status=%d\n", rs->status);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (rs->status == MEI_HBMS_SUCCESS ||
55062306a36Sopenharmony_ci	    rs->status == MEI_HBMS_ALREADY_STARTED) {
55162306a36Sopenharmony_ci		cl->notify_en = true;
55262306a36Sopenharmony_ci		cl->status = 0;
55362306a36Sopenharmony_ci	} else {
55462306a36Sopenharmony_ci		cl->status = -EINVAL;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/**
55962306a36Sopenharmony_ci * mei_hbm_cl_notify_stop_res - update the client state according
56062306a36Sopenharmony_ci *       notify stop response
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * @dev: the device structure
56362306a36Sopenharmony_ci * @cl: mei host client
56462306a36Sopenharmony_ci * @cmd: client notification stop response command
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_cistatic void mei_hbm_cl_notify_stop_res(struct mei_device *dev,
56762306a36Sopenharmony_ci				       struct mei_cl *cl,
56862306a36Sopenharmony_ci				       struct mei_hbm_cl_cmd *cmd)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct hbm_notification_response *rs =
57162306a36Sopenharmony_ci		(struct hbm_notification_response *)cmd;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	cl_dbg(dev, cl, "hbm: notify stop response status=%d\n", rs->status);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (rs->status == MEI_HBMS_SUCCESS ||
57662306a36Sopenharmony_ci	    rs->status == MEI_HBMS_NOT_STARTED) {
57762306a36Sopenharmony_ci		cl->notify_en = false;
57862306a36Sopenharmony_ci		cl->status = 0;
57962306a36Sopenharmony_ci	} else {
58062306a36Sopenharmony_ci		/* TODO: spec is not clear yet about other possible issues */
58162306a36Sopenharmony_ci		cl->status = -EINVAL;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci * mei_hbm_cl_notify - signal notification event
58762306a36Sopenharmony_ci *
58862306a36Sopenharmony_ci * @dev: the device structure
58962306a36Sopenharmony_ci * @cmd: notification client message
59062306a36Sopenharmony_ci */
59162306a36Sopenharmony_cistatic void mei_hbm_cl_notify(struct mei_device *dev,
59262306a36Sopenharmony_ci			      struct mei_hbm_cl_cmd *cmd)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct mei_cl *cl;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	cl = mei_hbm_cl_find_by_cmd(dev, cmd);
59762306a36Sopenharmony_ci	if (cl)
59862306a36Sopenharmony_ci		mei_cl_notify(cl);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci/**
60262306a36Sopenharmony_ci * mei_hbm_cl_dma_map_req - send client dma map request
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * @dev: the device structure
60562306a36Sopenharmony_ci * @cl: mei host client
60662306a36Sopenharmony_ci *
60762306a36Sopenharmony_ci * Return: 0 on success and -EIO on write failure
60862306a36Sopenharmony_ci */
60962306a36Sopenharmony_ciint mei_hbm_cl_dma_map_req(struct mei_device *dev, struct mei_cl *cl)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
61262306a36Sopenharmony_ci	struct hbm_client_dma_map_request req;
61362306a36Sopenharmony_ci	int ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	req.hbm_cmd = MEI_HBM_CLIENT_DMA_MAP_REQ_CMD;
62062306a36Sopenharmony_ci	req.client_buffer_id = cl->dma.buffer_id;
62162306a36Sopenharmony_ci	req.address_lsb = lower_32_bits(cl->dma.daddr);
62262306a36Sopenharmony_ci	req.address_msb = upper_32_bits(cl->dma.daddr);
62362306a36Sopenharmony_ci	req.size = cl->dma.size;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
62662306a36Sopenharmony_ci	if (ret)
62762306a36Sopenharmony_ci		dev_err(dev->dev, "dma map request failed: ret = %d\n", ret);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return ret;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci/**
63362306a36Sopenharmony_ci * mei_hbm_cl_dma_unmap_req - send client dma unmap request
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * @dev: the device structure
63662306a36Sopenharmony_ci * @cl: mei host client
63762306a36Sopenharmony_ci *
63862306a36Sopenharmony_ci * Return: 0 on success and -EIO on write failure
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_ciint mei_hbm_cl_dma_unmap_req(struct mei_device *dev, struct mei_cl *cl)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
64362306a36Sopenharmony_ci	struct hbm_client_dma_unmap_request req;
64462306a36Sopenharmony_ci	int ret;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	req.hbm_cmd = MEI_HBM_CLIENT_DMA_UNMAP_REQ_CMD;
65162306a36Sopenharmony_ci	req.client_buffer_id = cl->dma.buffer_id;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
65462306a36Sopenharmony_ci	if (ret)
65562306a36Sopenharmony_ci		dev_err(dev->dev, "dma unmap request failed: ret = %d\n", ret);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return ret;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic void mei_hbm_cl_dma_map_res(struct mei_device *dev,
66162306a36Sopenharmony_ci				   struct hbm_client_dma_response *res)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct mei_cl *cl;
66462306a36Sopenharmony_ci	struct mei_cl_cb *cb, *next;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	cl = NULL;
66762306a36Sopenharmony_ci	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) {
66862306a36Sopenharmony_ci		if (cb->fop_type != MEI_FOP_DMA_MAP)
66962306a36Sopenharmony_ci			continue;
67062306a36Sopenharmony_ci		if (!cb->cl->dma.buffer_id || cb->cl->dma_mapped)
67162306a36Sopenharmony_ci			continue;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		cl = cb->cl;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	if (!cl)
67762306a36Sopenharmony_ci		return;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (res->status) {
68062306a36Sopenharmony_ci		dev_err(dev->dev, "cl dma map failed %d\n", res->status);
68162306a36Sopenharmony_ci		cl->status = -EFAULT;
68262306a36Sopenharmony_ci	} else {
68362306a36Sopenharmony_ci		dev_dbg(dev->dev, "cl dma map succeeded\n");
68462306a36Sopenharmony_ci		cl->dma_mapped = 1;
68562306a36Sopenharmony_ci		cl->status = 0;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci	wake_up(&cl->wait);
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic void mei_hbm_cl_dma_unmap_res(struct mei_device *dev,
69162306a36Sopenharmony_ci				     struct hbm_client_dma_response *res)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct mei_cl *cl;
69462306a36Sopenharmony_ci	struct mei_cl_cb *cb, *next;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	cl = NULL;
69762306a36Sopenharmony_ci	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) {
69862306a36Sopenharmony_ci		if (cb->fop_type != MEI_FOP_DMA_UNMAP)
69962306a36Sopenharmony_ci			continue;
70062306a36Sopenharmony_ci		if (!cb->cl->dma.buffer_id || !cb->cl->dma_mapped)
70162306a36Sopenharmony_ci			continue;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		cl = cb->cl;
70462306a36Sopenharmony_ci		break;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci	if (!cl)
70762306a36Sopenharmony_ci		return;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (res->status) {
71062306a36Sopenharmony_ci		dev_err(dev->dev, "cl dma unmap failed %d\n", res->status);
71162306a36Sopenharmony_ci		cl->status = -EFAULT;
71262306a36Sopenharmony_ci	} else {
71362306a36Sopenharmony_ci		dev_dbg(dev->dev, "cl dma unmap succeeded\n");
71462306a36Sopenharmony_ci		cl->dma_mapped = 0;
71562306a36Sopenharmony_ci		cl->status = 0;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	wake_up(&cl->wait);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/**
72162306a36Sopenharmony_ci * mei_hbm_prop_req - request property for a single client
72262306a36Sopenharmony_ci *
72362306a36Sopenharmony_ci * @dev: the device structure
72462306a36Sopenharmony_ci * @start_idx: client index to start search
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
72762306a36Sopenharmony_ci */
72862306a36Sopenharmony_cistatic int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
73162306a36Sopenharmony_ci	struct hbm_props_request req;
73262306a36Sopenharmony_ci	unsigned long addr;
73362306a36Sopenharmony_ci	int ret;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	addr = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, start_idx);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* We got all client properties */
73862306a36Sopenharmony_ci	if (addr == MEI_CLIENTS_MAX) {
73962306a36Sopenharmony_ci		dev->hbm_state = MEI_HBM_STARTED;
74062306a36Sopenharmony_ci		mei_host_client_init(dev);
74162306a36Sopenharmony_ci		return 0;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
74962306a36Sopenharmony_ci	req.me_addr = addr;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
75262306a36Sopenharmony_ci	if (ret) {
75362306a36Sopenharmony_ci		dev_err(dev->dev, "properties request write failed: ret = %d\n",
75462306a36Sopenharmony_ci			ret);
75562306a36Sopenharmony_ci		return ret;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	dev->init_clients_timer = dev->timeouts.client_init;
75962306a36Sopenharmony_ci	mei_schedule_stall_timer(dev);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	return 0;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci/**
76562306a36Sopenharmony_ci * mei_hbm_pg - sends pg command
76662306a36Sopenharmony_ci *
76762306a36Sopenharmony_ci * @dev: the device structure
76862306a36Sopenharmony_ci * @pg_cmd: the pg command code
76962306a36Sopenharmony_ci *
77062306a36Sopenharmony_ci * Return: -EIO on write failure
77162306a36Sopenharmony_ci *         -EOPNOTSUPP if the operation is not supported by the protocol
77262306a36Sopenharmony_ci */
77362306a36Sopenharmony_ciint mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
77662306a36Sopenharmony_ci	struct hbm_power_gate req;
77762306a36Sopenharmony_ci	int ret;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (!dev->hbm_f_pg_supported)
78062306a36Sopenharmony_ci		return -EOPNOTSUPP;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
78562306a36Sopenharmony_ci	req.hbm_cmd = pg_cmd;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
78862306a36Sopenharmony_ci	if (ret)
78962306a36Sopenharmony_ci		dev_err(dev->dev, "power gate command write failed.\n");
79062306a36Sopenharmony_ci	return ret;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_hbm_pg);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/**
79562306a36Sopenharmony_ci * mei_hbm_stop_req - send stop request message
79662306a36Sopenharmony_ci *
79762306a36Sopenharmony_ci * @dev: mei device
79862306a36Sopenharmony_ci *
79962306a36Sopenharmony_ci * Return: -EIO on write failure
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_cistatic int mei_hbm_stop_req(struct mei_device *dev)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct mei_msg_hdr mei_hdr;
80462306a36Sopenharmony_ci	struct hbm_host_stop_request req;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	mei_hbm_hdr(&mei_hdr, sizeof(req));
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
80962306a36Sopenharmony_ci	req.hbm_cmd = HOST_STOP_REQ_CMD;
81062306a36Sopenharmony_ci	req.reason = DRIVER_STOP_REQUEST;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return mei_hbm_write_message(dev, &mei_hdr, &req);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci/**
81662306a36Sopenharmony_ci * mei_hbm_cl_flow_control_req - sends flow control request.
81762306a36Sopenharmony_ci *
81862306a36Sopenharmony_ci * @dev: the device structure
81962306a36Sopenharmony_ci * @cl: client info
82062306a36Sopenharmony_ci *
82162306a36Sopenharmony_ci * Return: -EIO on write failure
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_ciint mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct hbm_flow_control req;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	cl_dbg(dev, cl, "sending flow control\n");
82862306a36Sopenharmony_ci	return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD,
82962306a36Sopenharmony_ci				&req, sizeof(req));
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/**
83362306a36Sopenharmony_ci * mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials.
83462306a36Sopenharmony_ci *
83562306a36Sopenharmony_ci * @dev: the device structure
83662306a36Sopenharmony_ci * @fctrl: flow control response bus message
83762306a36Sopenharmony_ci *
83862306a36Sopenharmony_ci * Return: 0 on success, < 0 otherwise
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_cistatic int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev,
84162306a36Sopenharmony_ci						 struct hbm_flow_control *fctrl)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct mei_me_client *me_cl;
84462306a36Sopenharmony_ci	int rets;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	me_cl = mei_me_cl_by_id(dev, fctrl->me_addr);
84762306a36Sopenharmony_ci	if (!me_cl) {
84862306a36Sopenharmony_ci		dev_err(dev->dev, "no such me client %d\n", fctrl->me_addr);
84962306a36Sopenharmony_ci		return -ENOENT;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (WARN_ON(me_cl->props.single_recv_buf == 0)) {
85362306a36Sopenharmony_ci		rets = -EINVAL;
85462306a36Sopenharmony_ci		goto out;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	me_cl->tx_flow_ctrl_creds++;
85862306a36Sopenharmony_ci	dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
85962306a36Sopenharmony_ci		fctrl->me_addr, me_cl->tx_flow_ctrl_creds);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	rets = 0;
86262306a36Sopenharmony_ciout:
86362306a36Sopenharmony_ci	mei_me_cl_put(me_cl);
86462306a36Sopenharmony_ci	return rets;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/**
86862306a36Sopenharmony_ci * mei_hbm_cl_tx_flow_ctrl_creds_res - flow control response from me
86962306a36Sopenharmony_ci *
87062306a36Sopenharmony_ci * @dev: the device structure
87162306a36Sopenharmony_ci * @fctrl: flow control response bus message
87262306a36Sopenharmony_ci */
87362306a36Sopenharmony_cistatic void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev,
87462306a36Sopenharmony_ci					       struct hbm_flow_control *fctrl)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	struct mei_cl *cl;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (!fctrl->host_addr) {
87962306a36Sopenharmony_ci		/* single receive buffer */
88062306a36Sopenharmony_ci		mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl);
88162306a36Sopenharmony_ci		return;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	cl = mei_hbm_cl_find_by_cmd(dev, fctrl);
88562306a36Sopenharmony_ci	if (cl) {
88662306a36Sopenharmony_ci		cl->tx_flow_ctrl_creds++;
88762306a36Sopenharmony_ci		cl_dbg(dev, cl, "flow control creds = %d.\n",
88862306a36Sopenharmony_ci				cl->tx_flow_ctrl_creds);
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci/**
89462306a36Sopenharmony_ci * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
89562306a36Sopenharmony_ci *
89662306a36Sopenharmony_ci * @dev: the device structure
89762306a36Sopenharmony_ci * @cl: a client to disconnect from
89862306a36Sopenharmony_ci *
89962306a36Sopenharmony_ci * Return: -EIO on write failure
90062306a36Sopenharmony_ci */
90162306a36Sopenharmony_ciint mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	struct hbm_client_connect_request req;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD,
90662306a36Sopenharmony_ci				&req, sizeof(req));
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/**
91062306a36Sopenharmony_ci * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
91162306a36Sopenharmony_ci *
91262306a36Sopenharmony_ci * @dev: the device structure
91362306a36Sopenharmony_ci * @cl: a client to disconnect from
91462306a36Sopenharmony_ci *
91562306a36Sopenharmony_ci * Return: -EIO on write failure
91662306a36Sopenharmony_ci */
91762306a36Sopenharmony_ciint mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct hbm_client_connect_response resp;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD,
92262306a36Sopenharmony_ci				&resp, sizeof(resp));
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci/**
92662306a36Sopenharmony_ci * mei_hbm_cl_disconnect_res - update the client state according
92762306a36Sopenharmony_ci *       disconnect response
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * @dev: the device structure
93062306a36Sopenharmony_ci * @cl: mei host client
93162306a36Sopenharmony_ci * @cmd: disconnect client response host bus message
93262306a36Sopenharmony_ci */
93362306a36Sopenharmony_cistatic void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl,
93462306a36Sopenharmony_ci				      struct mei_hbm_cl_cmd *cmd)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct hbm_client_connect_response *rs =
93762306a36Sopenharmony_ci		(struct hbm_client_connect_response *)cmd;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (rs->status == MEI_CL_DISCONN_SUCCESS)
94262306a36Sopenharmony_ci		cl->state = MEI_FILE_DISCONNECT_REPLY;
94362306a36Sopenharmony_ci	cl->status = 0;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci/**
94762306a36Sopenharmony_ci * mei_hbm_cl_connect_req - send connection request to specific me client
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * @dev: the device structure
95062306a36Sopenharmony_ci * @cl: a client to connect to
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * Return: -EIO on write failure
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_ciint mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct hbm_client_connect_request req;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD,
95962306a36Sopenharmony_ci				&req, sizeof(req));
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/**
96362306a36Sopenharmony_ci * mei_hbm_cl_connect_res - update the client state according
96462306a36Sopenharmony_ci *        connection response
96562306a36Sopenharmony_ci *
96662306a36Sopenharmony_ci * @dev: the device structure
96762306a36Sopenharmony_ci * @cl: mei host client
96862306a36Sopenharmony_ci * @cmd: connect client response host bus message
96962306a36Sopenharmony_ci */
97062306a36Sopenharmony_cistatic void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
97162306a36Sopenharmony_ci				   struct mei_hbm_cl_cmd *cmd)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	struct hbm_client_connect_response *rs =
97462306a36Sopenharmony_ci		(struct hbm_client_connect_response *)cmd;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	cl_dbg(dev, cl, "hbm: connect response status=%s\n",
97762306a36Sopenharmony_ci			mei_cl_conn_status_str(rs->status));
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (rs->status == MEI_CL_CONN_SUCCESS)
98062306a36Sopenharmony_ci		cl->state = MEI_FILE_CONNECTED;
98162306a36Sopenharmony_ci	else {
98262306a36Sopenharmony_ci		cl->state = MEI_FILE_DISCONNECT_REPLY;
98362306a36Sopenharmony_ci		if (rs->status == MEI_CL_CONN_NOT_FOUND) {
98462306a36Sopenharmony_ci			mei_me_cl_del(dev, cl->me_cl);
98562306a36Sopenharmony_ci			if (dev->dev_state == MEI_DEV_ENABLED)
98662306a36Sopenharmony_ci				schedule_work(&dev->bus_rescan_work);
98762306a36Sopenharmony_ci		}
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci	cl->status = mei_cl_conn_status_to_errno(rs->status);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci/**
99362306a36Sopenharmony_ci * mei_hbm_cl_res - process hbm response received on behalf
99462306a36Sopenharmony_ci *         an client
99562306a36Sopenharmony_ci *
99662306a36Sopenharmony_ci * @dev: the device structure
99762306a36Sopenharmony_ci * @rs:  hbm client message
99862306a36Sopenharmony_ci * @fop_type: file operation type
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_cistatic void mei_hbm_cl_res(struct mei_device *dev,
100162306a36Sopenharmony_ci			   struct mei_hbm_cl_cmd *rs,
100262306a36Sopenharmony_ci			   enum mei_cb_file_ops fop_type)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	struct mei_cl *cl;
100562306a36Sopenharmony_ci	struct mei_cl_cb *cb, *next;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	cl = NULL;
100862306a36Sopenharmony_ci	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) {
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		cl = cb->cl;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		if (cb->fop_type != fop_type)
101362306a36Sopenharmony_ci			continue;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		if (mei_hbm_cl_addr_equal(cl, rs)) {
101662306a36Sopenharmony_ci			list_del_init(&cb->list);
101762306a36Sopenharmony_ci			break;
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (!cl)
102262306a36Sopenharmony_ci		return;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	switch (fop_type) {
102562306a36Sopenharmony_ci	case MEI_FOP_CONNECT:
102662306a36Sopenharmony_ci		mei_hbm_cl_connect_res(dev, cl, rs);
102762306a36Sopenharmony_ci		break;
102862306a36Sopenharmony_ci	case MEI_FOP_DISCONNECT:
102962306a36Sopenharmony_ci		mei_hbm_cl_disconnect_res(dev, cl, rs);
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	case MEI_FOP_NOTIFY_START:
103262306a36Sopenharmony_ci		mei_hbm_cl_notify_start_res(dev, cl, rs);
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	case MEI_FOP_NOTIFY_STOP:
103562306a36Sopenharmony_ci		mei_hbm_cl_notify_stop_res(dev, cl, rs);
103662306a36Sopenharmony_ci		break;
103762306a36Sopenharmony_ci	default:
103862306a36Sopenharmony_ci		return;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	cl->timer_count = 0;
104262306a36Sopenharmony_ci	wake_up(&cl->wait);
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci/**
104762306a36Sopenharmony_ci * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
104862306a36Sopenharmony_ci *  host sends disconnect response
104962306a36Sopenharmony_ci *
105062306a36Sopenharmony_ci * @dev: the device structure.
105162306a36Sopenharmony_ci * @disconnect_req: disconnect request bus message from the me
105262306a36Sopenharmony_ci *
105362306a36Sopenharmony_ci * Return: -ENOMEM on allocation failure
105462306a36Sopenharmony_ci */
105562306a36Sopenharmony_cistatic int mei_hbm_fw_disconnect_req(struct mei_device *dev,
105662306a36Sopenharmony_ci		struct hbm_client_connect_request *disconnect_req)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	struct mei_cl *cl;
105962306a36Sopenharmony_ci	struct mei_cl_cb *cb;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
106262306a36Sopenharmony_ci	if (cl) {
106362306a36Sopenharmony_ci		cl_warn(dev, cl, "fw disconnect request received\n");
106462306a36Sopenharmony_ci		cl->state = MEI_FILE_DISCONNECTING;
106562306a36Sopenharmony_ci		cl->timer_count = 0;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT_RSP,
106862306a36Sopenharmony_ci					       NULL);
106962306a36Sopenharmony_ci		if (!cb)
107062306a36Sopenharmony_ci			return -ENOMEM;
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci	return 0;
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci/**
107662306a36Sopenharmony_ci * mei_hbm_pg_enter_res - PG enter response received
107762306a36Sopenharmony_ci *
107862306a36Sopenharmony_ci * @dev: the device structure.
107962306a36Sopenharmony_ci *
108062306a36Sopenharmony_ci * Return: 0 on success, -EPROTO on state mismatch
108162306a36Sopenharmony_ci */
108262306a36Sopenharmony_cistatic int mei_hbm_pg_enter_res(struct mei_device *dev)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	if (mei_pg_state(dev) != MEI_PG_OFF ||
108562306a36Sopenharmony_ci	    dev->pg_event != MEI_PG_EVENT_WAIT) {
108662306a36Sopenharmony_ci		dev_err(dev->dev, "hbm: pg entry response: state mismatch [%s, %d]\n",
108762306a36Sopenharmony_ci			mei_pg_state_str(mei_pg_state(dev)), dev->pg_event);
108862306a36Sopenharmony_ci		return -EPROTO;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_RECEIVED;
109262306a36Sopenharmony_ci	wake_up(&dev->wait_pg);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	return 0;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci/**
109862306a36Sopenharmony_ci * mei_hbm_pg_resume - process with PG resume
109962306a36Sopenharmony_ci *
110062306a36Sopenharmony_ci * @dev: the device structure.
110162306a36Sopenharmony_ci */
110262306a36Sopenharmony_civoid mei_hbm_pg_resume(struct mei_device *dev)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	pm_request_resume(dev->dev);
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_hbm_pg_resume);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/**
110962306a36Sopenharmony_ci * mei_hbm_pg_exit_res - PG exit response received
111062306a36Sopenharmony_ci *
111162306a36Sopenharmony_ci * @dev: the device structure.
111262306a36Sopenharmony_ci *
111362306a36Sopenharmony_ci * Return: 0 on success, -EPROTO on state mismatch
111462306a36Sopenharmony_ci */
111562306a36Sopenharmony_cistatic int mei_hbm_pg_exit_res(struct mei_device *dev)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	if (mei_pg_state(dev) != MEI_PG_ON ||
111862306a36Sopenharmony_ci	    (dev->pg_event != MEI_PG_EVENT_WAIT &&
111962306a36Sopenharmony_ci	     dev->pg_event != MEI_PG_EVENT_IDLE)) {
112062306a36Sopenharmony_ci		dev_err(dev->dev, "hbm: pg exit response: state mismatch [%s, %d]\n",
112162306a36Sopenharmony_ci			mei_pg_state_str(mei_pg_state(dev)), dev->pg_event);
112262306a36Sopenharmony_ci		return -EPROTO;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	switch (dev->pg_event) {
112662306a36Sopenharmony_ci	case MEI_PG_EVENT_WAIT:
112762306a36Sopenharmony_ci		dev->pg_event = MEI_PG_EVENT_RECEIVED;
112862306a36Sopenharmony_ci		wake_up(&dev->wait_pg);
112962306a36Sopenharmony_ci		break;
113062306a36Sopenharmony_ci	case MEI_PG_EVENT_IDLE:
113162306a36Sopenharmony_ci		/*
113262306a36Sopenharmony_ci		* If the driver is not waiting on this then
113362306a36Sopenharmony_ci		* this is HW initiated exit from PG.
113462306a36Sopenharmony_ci		* Start runtime pm resume sequence to exit from PG.
113562306a36Sopenharmony_ci		*/
113662306a36Sopenharmony_ci		dev->pg_event = MEI_PG_EVENT_RECEIVED;
113762306a36Sopenharmony_ci		mei_hbm_pg_resume(dev);
113862306a36Sopenharmony_ci		break;
113962306a36Sopenharmony_ci	default:
114062306a36Sopenharmony_ci		WARN(1, "hbm: pg exit response: unexpected pg event = %d\n",
114162306a36Sopenharmony_ci		     dev->pg_event);
114262306a36Sopenharmony_ci		return -EPROTO;
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	return 0;
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci/**
114962306a36Sopenharmony_ci * mei_hbm_config_features - check what hbm features and commands
115062306a36Sopenharmony_ci *        are supported by the fw
115162306a36Sopenharmony_ci *
115262306a36Sopenharmony_ci * @dev: the device structure
115362306a36Sopenharmony_ci */
115462306a36Sopenharmony_cistatic void mei_hbm_config_features(struct mei_device *dev)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	/* Power Gating Isolation Support */
115762306a36Sopenharmony_ci	dev->hbm_f_pg_supported = 0;
115862306a36Sopenharmony_ci	if (dev->version.major_version > HBM_MAJOR_VERSION_PGI)
115962306a36Sopenharmony_ci		dev->hbm_f_pg_supported = 1;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
116262306a36Sopenharmony_ci	    dev->version.minor_version >= HBM_MINOR_VERSION_PGI)
116362306a36Sopenharmony_ci		dev->hbm_f_pg_supported = 1;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	dev->hbm_f_dc_supported = 0;
116662306a36Sopenharmony_ci	if (dev->version.major_version >= HBM_MAJOR_VERSION_DC)
116762306a36Sopenharmony_ci		dev->hbm_f_dc_supported = 1;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	dev->hbm_f_ie_supported = 0;
117062306a36Sopenharmony_ci	if (dev->version.major_version >= HBM_MAJOR_VERSION_IE)
117162306a36Sopenharmony_ci		dev->hbm_f_ie_supported = 1;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	/* disconnect on connect timeout instead of link reset */
117462306a36Sopenharmony_ci	dev->hbm_f_dot_supported = 0;
117562306a36Sopenharmony_ci	if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT)
117662306a36Sopenharmony_ci		dev->hbm_f_dot_supported = 1;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* Notification Event Support */
117962306a36Sopenharmony_ci	dev->hbm_f_ev_supported = 0;
118062306a36Sopenharmony_ci	if (dev->version.major_version >= HBM_MAJOR_VERSION_EV)
118162306a36Sopenharmony_ci		dev->hbm_f_ev_supported = 1;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/* Fixed Address Client Support */
118462306a36Sopenharmony_ci	dev->hbm_f_fa_supported = 0;
118562306a36Sopenharmony_ci	if (dev->version.major_version >= HBM_MAJOR_VERSION_FA)
118662306a36Sopenharmony_ci		dev->hbm_f_fa_supported = 1;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/* OS ver message Support */
118962306a36Sopenharmony_ci	dev->hbm_f_os_supported = 0;
119062306a36Sopenharmony_ci	if (dev->version.major_version >= HBM_MAJOR_VERSION_OS)
119162306a36Sopenharmony_ci		dev->hbm_f_os_supported = 1;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/* DMA Ring Support */
119462306a36Sopenharmony_ci	dev->hbm_f_dr_supported = 0;
119562306a36Sopenharmony_ci	if (dev->version.major_version > HBM_MAJOR_VERSION_DR ||
119662306a36Sopenharmony_ci	    (dev->version.major_version == HBM_MAJOR_VERSION_DR &&
119762306a36Sopenharmony_ci	     dev->version.minor_version >= HBM_MINOR_VERSION_DR))
119862306a36Sopenharmony_ci		dev->hbm_f_dr_supported = 1;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/* VTag Support */
120162306a36Sopenharmony_ci	dev->hbm_f_vt_supported = 0;
120262306a36Sopenharmony_ci	if (dev->version.major_version > HBM_MAJOR_VERSION_VT ||
120362306a36Sopenharmony_ci	    (dev->version.major_version == HBM_MAJOR_VERSION_VT &&
120462306a36Sopenharmony_ci	     dev->version.minor_version >= HBM_MINOR_VERSION_VT))
120562306a36Sopenharmony_ci		dev->hbm_f_vt_supported = 1;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/* GSC support */
120862306a36Sopenharmony_ci	if (dev->version.major_version > HBM_MAJOR_VERSION_GSC ||
120962306a36Sopenharmony_ci	    (dev->version.major_version == HBM_MAJOR_VERSION_GSC &&
121062306a36Sopenharmony_ci	     dev->version.minor_version >= HBM_MINOR_VERSION_GSC))
121162306a36Sopenharmony_ci		dev->hbm_f_gsc_supported = 1;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	/* Capability message Support */
121462306a36Sopenharmony_ci	dev->hbm_f_cap_supported = 0;
121562306a36Sopenharmony_ci	if (dev->version.major_version > HBM_MAJOR_VERSION_CAP ||
121662306a36Sopenharmony_ci	    (dev->version.major_version == HBM_MAJOR_VERSION_CAP &&
121762306a36Sopenharmony_ci	     dev->version.minor_version >= HBM_MINOR_VERSION_CAP))
121862306a36Sopenharmony_ci		dev->hbm_f_cap_supported = 1;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	/* Client DMA Support */
122162306a36Sopenharmony_ci	dev->hbm_f_cd_supported = 0;
122262306a36Sopenharmony_ci	if (dev->version.major_version > HBM_MAJOR_VERSION_CD ||
122362306a36Sopenharmony_ci	    (dev->version.major_version == HBM_MAJOR_VERSION_CD &&
122462306a36Sopenharmony_ci	     dev->version.minor_version >= HBM_MINOR_VERSION_CD))
122562306a36Sopenharmony_ci		dev->hbm_f_cd_supported = 1;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci/**
122962306a36Sopenharmony_ci * mei_hbm_version_is_supported - checks whether the driver can
123062306a36Sopenharmony_ci *     support the hbm version of the device
123162306a36Sopenharmony_ci *
123262306a36Sopenharmony_ci * @dev: the device structure
123362306a36Sopenharmony_ci * Return: true if driver can support hbm version of the device
123462306a36Sopenharmony_ci */
123562306a36Sopenharmony_cibool mei_hbm_version_is_supported(struct mei_device *dev)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	return	(dev->version.major_version < HBM_MAJOR_VERSION) ||
123862306a36Sopenharmony_ci		(dev->version.major_version == HBM_MAJOR_VERSION &&
123962306a36Sopenharmony_ci		 dev->version.minor_version <= HBM_MINOR_VERSION);
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci/**
124362306a36Sopenharmony_ci * mei_hbm_dispatch - bottom half read routine after ISR to
124462306a36Sopenharmony_ci * handle the read bus message cmd processing.
124562306a36Sopenharmony_ci *
124662306a36Sopenharmony_ci * @dev: the device structure
124762306a36Sopenharmony_ci * @hdr: header of bus message
124862306a36Sopenharmony_ci *
124962306a36Sopenharmony_ci * Return: 0 on success and < 0 on failure
125062306a36Sopenharmony_ci */
125162306a36Sopenharmony_ciint mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct mei_bus_message *mei_msg;
125462306a36Sopenharmony_ci	struct hbm_host_version_response *version_res;
125562306a36Sopenharmony_ci	struct hbm_props_response *props_res;
125662306a36Sopenharmony_ci	struct hbm_host_enum_response *enum_res;
125762306a36Sopenharmony_ci	struct hbm_dma_setup_response *dma_setup_res;
125862306a36Sopenharmony_ci	struct hbm_add_client_request *add_cl_req;
125962306a36Sopenharmony_ci	struct hbm_capability_response *capability_res;
126062306a36Sopenharmony_ci	int ret;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	struct mei_hbm_cl_cmd *cl_cmd;
126362306a36Sopenharmony_ci	struct hbm_client_connect_request *disconnect_req;
126462306a36Sopenharmony_ci	struct hbm_flow_control *fctrl;
126562306a36Sopenharmony_ci	struct hbm_client_dma_response *client_dma_res;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/* read the message to our buffer */
126862306a36Sopenharmony_ci	BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
126962306a36Sopenharmony_ci	mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
127062306a36Sopenharmony_ci	mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
127162306a36Sopenharmony_ci	cl_cmd  = (struct mei_hbm_cl_cmd *)mei_msg;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	/* ignore spurious message and prevent reset nesting
127462306a36Sopenharmony_ci	 * hbm is put to idle during system reset
127562306a36Sopenharmony_ci	 */
127662306a36Sopenharmony_ci	if (dev->hbm_state == MEI_HBM_IDLE) {
127762306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: state is idle ignore spurious messages\n");
127862306a36Sopenharmony_ci		return 0;
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	switch (mei_msg->hbm_cmd) {
128262306a36Sopenharmony_ci	case HOST_START_RES_CMD:
128362306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: start: response message received.\n");
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		dev->init_clients_timer = 0;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		version_res = (struct hbm_host_version_response *)mei_msg;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		dev_dbg(dev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
129062306a36Sopenharmony_ci				HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
129162306a36Sopenharmony_ci				version_res->me_max_version.major_version,
129262306a36Sopenharmony_ci				version_res->me_max_version.minor_version);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		if (version_res->host_version_supported) {
129562306a36Sopenharmony_ci			dev->version.major_version = HBM_MAJOR_VERSION;
129662306a36Sopenharmony_ci			dev->version.minor_version = HBM_MINOR_VERSION;
129762306a36Sopenharmony_ci		} else {
129862306a36Sopenharmony_ci			dev->version.major_version =
129962306a36Sopenharmony_ci				version_res->me_max_version.major_version;
130062306a36Sopenharmony_ci			dev->version.minor_version =
130162306a36Sopenharmony_ci				version_res->me_max_version.minor_version;
130262306a36Sopenharmony_ci		}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		if (!mei_hbm_version_is_supported(dev)) {
130562306a36Sopenharmony_ci			dev_warn(dev->dev, "hbm: start: version mismatch - stopping the driver.\n");
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci			dev->hbm_state = MEI_HBM_STOPPED;
130862306a36Sopenharmony_ci			if (mei_hbm_stop_req(dev)) {
130962306a36Sopenharmony_ci				dev_err(dev->dev, "hbm: start: failed to send stop request\n");
131062306a36Sopenharmony_ci				return -EIO;
131162306a36Sopenharmony_ci			}
131262306a36Sopenharmony_ci			break;
131362306a36Sopenharmony_ci		}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci		mei_hbm_config_features(dev);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
131862306a36Sopenharmony_ci		    dev->hbm_state != MEI_HBM_STARTING) {
131962306a36Sopenharmony_ci			if (dev->dev_state == MEI_DEV_POWER_DOWN ||
132062306a36Sopenharmony_ci			    dev->dev_state == MEI_DEV_POWERING_DOWN) {
132162306a36Sopenharmony_ci				dev_dbg(dev->dev, "hbm: start: on shutdown, ignoring\n");
132262306a36Sopenharmony_ci				return 0;
132362306a36Sopenharmony_ci			}
132462306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: start: state mismatch, [%d, %d]\n",
132562306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
132662306a36Sopenharmony_ci			return -EPROTO;
132762306a36Sopenharmony_ci		}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci		if (dev->hbm_f_cap_supported) {
133062306a36Sopenharmony_ci			if (mei_hbm_capabilities_req(dev))
133162306a36Sopenharmony_ci				return -EIO;
133262306a36Sopenharmony_ci			wake_up(&dev->wait_hbm_start);
133362306a36Sopenharmony_ci			break;
133462306a36Sopenharmony_ci		}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci		if (dev->hbm_f_dr_supported) {
133762306a36Sopenharmony_ci			if (mei_dmam_ring_alloc(dev))
133862306a36Sopenharmony_ci				dev_info(dev->dev, "running w/o dma ring\n");
133962306a36Sopenharmony_ci			if (mei_dma_ring_is_allocated(dev)) {
134062306a36Sopenharmony_ci				if (mei_hbm_dma_setup_req(dev))
134162306a36Sopenharmony_ci					return -EIO;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci				wake_up(&dev->wait_hbm_start);
134462306a36Sopenharmony_ci				break;
134562306a36Sopenharmony_ci			}
134662306a36Sopenharmony_ci		}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		dev->hbm_f_dr_supported = 0;
134962306a36Sopenharmony_ci		mei_dmam_ring_free(dev);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci		if (mei_hbm_enum_clients_req(dev))
135262306a36Sopenharmony_ci			return -EIO;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		wake_up(&dev->wait_hbm_start);
135562306a36Sopenharmony_ci		break;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	case MEI_HBM_CAPABILITIES_RES_CMD:
135862306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: capabilities response: message received.\n");
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		dev->init_clients_timer = 0;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
136362306a36Sopenharmony_ci		    dev->hbm_state != MEI_HBM_CAP_SETUP) {
136462306a36Sopenharmony_ci			if (dev->dev_state == MEI_DEV_POWER_DOWN ||
136562306a36Sopenharmony_ci			    dev->dev_state == MEI_DEV_POWERING_DOWN) {
136662306a36Sopenharmony_ci				dev_dbg(dev->dev, "hbm: capabilities response: on shutdown, ignoring\n");
136762306a36Sopenharmony_ci				return 0;
136862306a36Sopenharmony_ci			}
136962306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: capabilities response: state mismatch, [%d, %d]\n",
137062306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
137162306a36Sopenharmony_ci			return -EPROTO;
137262306a36Sopenharmony_ci		}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci		capability_res = (struct hbm_capability_response *)mei_msg;
137562306a36Sopenharmony_ci		if (!(capability_res->capability_granted[0] & HBM_CAP_VT))
137662306a36Sopenharmony_ci			dev->hbm_f_vt_supported = 0;
137762306a36Sopenharmony_ci		if (!(capability_res->capability_granted[0] & HBM_CAP_CD))
137862306a36Sopenharmony_ci			dev->hbm_f_cd_supported = 0;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		if (!(capability_res->capability_granted[0] & HBM_CAP_GSC))
138162306a36Sopenharmony_ci			dev->hbm_f_gsc_supported = 0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci		if (dev->hbm_f_dr_supported) {
138462306a36Sopenharmony_ci			if (mei_dmam_ring_alloc(dev))
138562306a36Sopenharmony_ci				dev_info(dev->dev, "running w/o dma ring\n");
138662306a36Sopenharmony_ci			if (mei_dma_ring_is_allocated(dev)) {
138762306a36Sopenharmony_ci				if (mei_hbm_dma_setup_req(dev))
138862306a36Sopenharmony_ci					return -EIO;
138962306a36Sopenharmony_ci				break;
139062306a36Sopenharmony_ci			}
139162306a36Sopenharmony_ci		}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		dev->hbm_f_dr_supported = 0;
139462306a36Sopenharmony_ci		mei_dmam_ring_free(dev);
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci		if (mei_hbm_enum_clients_req(dev))
139762306a36Sopenharmony_ci			return -EIO;
139862306a36Sopenharmony_ci		break;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	case MEI_HBM_DMA_SETUP_RES_CMD:
140162306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: dma setup response: message received.\n");
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		dev->init_clients_timer = 0;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
140662306a36Sopenharmony_ci		    dev->hbm_state != MEI_HBM_DR_SETUP) {
140762306a36Sopenharmony_ci			if (dev->dev_state == MEI_DEV_POWER_DOWN ||
140862306a36Sopenharmony_ci			    dev->dev_state == MEI_DEV_POWERING_DOWN) {
140962306a36Sopenharmony_ci				dev_dbg(dev->dev, "hbm: dma setup response: on shutdown, ignoring\n");
141062306a36Sopenharmony_ci				return 0;
141162306a36Sopenharmony_ci			}
141262306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: dma setup response: state mismatch, [%d, %d]\n",
141362306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
141462306a36Sopenharmony_ci			return -EPROTO;
141562306a36Sopenharmony_ci		}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		dma_setup_res = (struct hbm_dma_setup_response *)mei_msg;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci		if (dma_setup_res->status) {
142062306a36Sopenharmony_ci			u8 status = dma_setup_res->status;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci			if (status == MEI_HBMS_NOT_ALLOWED) {
142362306a36Sopenharmony_ci				dev_dbg(dev->dev, "hbm: dma setup not allowed\n");
142462306a36Sopenharmony_ci			} else {
142562306a36Sopenharmony_ci				dev_info(dev->dev, "hbm: dma setup response: failure = %d %s\n",
142662306a36Sopenharmony_ci					 status,
142762306a36Sopenharmony_ci					 mei_hbm_status_str(status));
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci			dev->hbm_f_dr_supported = 0;
143062306a36Sopenharmony_ci			mei_dmam_ring_free(dev);
143162306a36Sopenharmony_ci		}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci		if (mei_hbm_enum_clients_req(dev))
143462306a36Sopenharmony_ci			return -EIO;
143562306a36Sopenharmony_ci		break;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	case CLIENT_CONNECT_RES_CMD:
143862306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: client connect response: message received.\n");
143962306a36Sopenharmony_ci		mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT);
144062306a36Sopenharmony_ci		break;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	case CLIENT_DISCONNECT_RES_CMD:
144362306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: client disconnect response: message received.\n");
144462306a36Sopenharmony_ci		mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT);
144562306a36Sopenharmony_ci		break;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	case MEI_FLOW_CONTROL_CMD:
144862306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: client flow control response: message received.\n");
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci		fctrl = (struct hbm_flow_control *)mei_msg;
145162306a36Sopenharmony_ci		mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl);
145262306a36Sopenharmony_ci		break;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	case MEI_PG_ISOLATION_ENTRY_RES_CMD:
145562306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: power gate isolation entry response received\n");
145662306a36Sopenharmony_ci		ret = mei_hbm_pg_enter_res(dev);
145762306a36Sopenharmony_ci		if (ret)
145862306a36Sopenharmony_ci			return ret;
145962306a36Sopenharmony_ci		break;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	case MEI_PG_ISOLATION_EXIT_REQ_CMD:
146262306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: power gate isolation exit request received\n");
146362306a36Sopenharmony_ci		ret = mei_hbm_pg_exit_res(dev);
146462306a36Sopenharmony_ci		if (ret)
146562306a36Sopenharmony_ci			return ret;
146662306a36Sopenharmony_ci		break;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	case HOST_CLIENT_PROPERTIES_RES_CMD:
146962306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: properties response: message received.\n");
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci		dev->init_clients_timer = 0;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
147462306a36Sopenharmony_ci		    dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
147562306a36Sopenharmony_ci			if (dev->dev_state == MEI_DEV_POWER_DOWN ||
147662306a36Sopenharmony_ci			    dev->dev_state == MEI_DEV_POWERING_DOWN) {
147762306a36Sopenharmony_ci				dev_dbg(dev->dev, "hbm: properties response: on shutdown, ignoring\n");
147862306a36Sopenharmony_ci				return 0;
147962306a36Sopenharmony_ci			}
148062306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
148162306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
148262306a36Sopenharmony_ci			return -EPROTO;
148362306a36Sopenharmony_ci		}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci		props_res = (struct hbm_props_response *)mei_msg;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		if (props_res->status == MEI_HBMS_CLIENT_NOT_FOUND) {
148862306a36Sopenharmony_ci			dev_dbg(dev->dev, "hbm: properties response: %d CLIENT_NOT_FOUND\n",
148962306a36Sopenharmony_ci				props_res->me_addr);
149062306a36Sopenharmony_ci		} else if (props_res->status) {
149162306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: properties response: wrong status = %d %s\n",
149262306a36Sopenharmony_ci				props_res->status,
149362306a36Sopenharmony_ci				mei_hbm_status_str(props_res->status));
149462306a36Sopenharmony_ci			return -EPROTO;
149562306a36Sopenharmony_ci		} else {
149662306a36Sopenharmony_ci			mei_hbm_me_cl_add(dev, props_res);
149762306a36Sopenharmony_ci		}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci		/* request property for the next client */
150062306a36Sopenharmony_ci		if (mei_hbm_prop_req(dev, props_res->me_addr + 1))
150162306a36Sopenharmony_ci			return -EIO;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci		break;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	case HOST_ENUM_RES_CMD:
150662306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: enumeration response: message received\n");
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci		dev->init_clients_timer = 0;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci		enum_res = (struct hbm_host_enum_response *) mei_msg;
151162306a36Sopenharmony_ci		BUILD_BUG_ON(sizeof(dev->me_clients_map)
151262306a36Sopenharmony_ci				< sizeof(enum_res->valid_addresses));
151362306a36Sopenharmony_ci		memcpy(dev->me_clients_map, enum_res->valid_addresses,
151462306a36Sopenharmony_ci				sizeof(enum_res->valid_addresses));
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
151762306a36Sopenharmony_ci		    dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
151862306a36Sopenharmony_ci			if (dev->dev_state == MEI_DEV_POWER_DOWN ||
151962306a36Sopenharmony_ci			    dev->dev_state == MEI_DEV_POWERING_DOWN) {
152062306a36Sopenharmony_ci				dev_dbg(dev->dev, "hbm: enumeration response: on shutdown, ignoring\n");
152162306a36Sopenharmony_ci				return 0;
152262306a36Sopenharmony_ci			}
152362306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
152462306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
152562306a36Sopenharmony_ci			return -EPROTO;
152662306a36Sopenharmony_ci		}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci		dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		/* first property request */
153162306a36Sopenharmony_ci		if (mei_hbm_prop_req(dev, 0))
153262306a36Sopenharmony_ci			return -EIO;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		break;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	case HOST_STOP_RES_CMD:
153762306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: stop response: message received\n");
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		dev->init_clients_timer = 0;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci		if (dev->hbm_state != MEI_HBM_STOPPED) {
154262306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
154362306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
154462306a36Sopenharmony_ci			return -EPROTO;
154562306a36Sopenharmony_ci		}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci		mei_set_devstate(dev, MEI_DEV_POWER_DOWN);
154862306a36Sopenharmony_ci		dev_info(dev->dev, "hbm: stop response: resetting.\n");
154962306a36Sopenharmony_ci		/* force the reset */
155062306a36Sopenharmony_ci		return -EPROTO;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	case CLIENT_DISCONNECT_REQ_CMD:
155362306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: disconnect request: message received\n");
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		disconnect_req = (struct hbm_client_connect_request *)mei_msg;
155662306a36Sopenharmony_ci		mei_hbm_fw_disconnect_req(dev, disconnect_req);
155762306a36Sopenharmony_ci		break;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	case ME_STOP_REQ_CMD:
156062306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: stop request: message received\n");
156162306a36Sopenharmony_ci		dev->hbm_state = MEI_HBM_STOPPED;
156262306a36Sopenharmony_ci		if (mei_hbm_stop_req(dev)) {
156362306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: stop request: failed to send stop request\n");
156462306a36Sopenharmony_ci			return -EIO;
156562306a36Sopenharmony_ci		}
156662306a36Sopenharmony_ci		break;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	case MEI_HBM_ADD_CLIENT_REQ_CMD:
156962306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: add client request received\n");
157062306a36Sopenharmony_ci		/*
157162306a36Sopenharmony_ci		 * after the host receives the enum_resp
157262306a36Sopenharmony_ci		 * message clients may be added or removed
157362306a36Sopenharmony_ci		 */
157462306a36Sopenharmony_ci		if (dev->hbm_state <= MEI_HBM_ENUM_CLIENTS ||
157562306a36Sopenharmony_ci		    dev->hbm_state >= MEI_HBM_STOPPED) {
157662306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: add client: state mismatch, [%d, %d]\n",
157762306a36Sopenharmony_ci				dev->dev_state, dev->hbm_state);
157862306a36Sopenharmony_ci			return -EPROTO;
157962306a36Sopenharmony_ci		}
158062306a36Sopenharmony_ci		add_cl_req = (struct hbm_add_client_request *)mei_msg;
158162306a36Sopenharmony_ci		ret = mei_hbm_fw_add_cl_req(dev, add_cl_req);
158262306a36Sopenharmony_ci		if (ret) {
158362306a36Sopenharmony_ci			dev_err(dev->dev, "hbm: add client: failed to send response %d\n",
158462306a36Sopenharmony_ci				ret);
158562306a36Sopenharmony_ci			return -EIO;
158662306a36Sopenharmony_ci		}
158762306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: add client request processed\n");
158862306a36Sopenharmony_ci		break;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	case MEI_HBM_NOTIFY_RES_CMD:
159162306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: notify response received\n");
159262306a36Sopenharmony_ci		mei_hbm_cl_res(dev, cl_cmd, notify_res_to_fop(cl_cmd));
159362306a36Sopenharmony_ci		break;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	case MEI_HBM_NOTIFICATION_CMD:
159662306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: notification\n");
159762306a36Sopenharmony_ci		mei_hbm_cl_notify(dev, cl_cmd);
159862306a36Sopenharmony_ci		break;
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	case MEI_HBM_CLIENT_DMA_MAP_RES_CMD:
160162306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: client dma map response: message received.\n");
160262306a36Sopenharmony_ci		client_dma_res = (struct hbm_client_dma_response *)mei_msg;
160362306a36Sopenharmony_ci		mei_hbm_cl_dma_map_res(dev, client_dma_res);
160462306a36Sopenharmony_ci		break;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	case MEI_HBM_CLIENT_DMA_UNMAP_RES_CMD:
160762306a36Sopenharmony_ci		dev_dbg(dev->dev, "hbm: client dma unmap response: message received.\n");
160862306a36Sopenharmony_ci		client_dma_res = (struct hbm_client_dma_response *)mei_msg;
160962306a36Sopenharmony_ci		mei_hbm_cl_dma_unmap_res(dev, client_dma_res);
161062306a36Sopenharmony_ci		break;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	default:
161362306a36Sopenharmony_ci		WARN(1, "hbm: wrong command %d\n", mei_msg->hbm_cmd);
161462306a36Sopenharmony_ci		return -EPROTO;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci	return 0;
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
1620