162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ISHTP bus layer messages handling
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2003-2016, Intel Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/export.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/sched.h>
1162306a36Sopenharmony_ci#include <linux/wait.h>
1262306a36Sopenharmony_ci#include <linux/spinlock.h>
1362306a36Sopenharmony_ci#include "ishtp-dev.h"
1462306a36Sopenharmony_ci#include "hbm.h"
1562306a36Sopenharmony_ci#include "client.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * ishtp_hbm_fw_cl_allocate() - Allocate FW clients
1962306a36Sopenharmony_ci * @dev: ISHTP device instance
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Allocates storage for fw clients
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistatic void ishtp_hbm_fw_cl_allocate(struct ishtp_device *dev)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct ishtp_fw_client *clients;
2662306a36Sopenharmony_ci	int b;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	/* count how many ISH clients we have */
2962306a36Sopenharmony_ci	for_each_set_bit(b, dev->fw_clients_map, ISHTP_CLIENTS_MAX)
3062306a36Sopenharmony_ci		dev->fw_clients_num++;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (dev->fw_clients_num <= 0)
3362306a36Sopenharmony_ci		return;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* allocate storage for fw clients representation */
3662306a36Sopenharmony_ci	clients = kcalloc(dev->fw_clients_num, sizeof(struct ishtp_fw_client),
3762306a36Sopenharmony_ci			  GFP_KERNEL);
3862306a36Sopenharmony_ci	if (!clients) {
3962306a36Sopenharmony_ci		dev->dev_state = ISHTP_DEV_RESETTING;
4062306a36Sopenharmony_ci		ish_hw_reset(dev);
4162306a36Sopenharmony_ci		return;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	dev->fw_clients = clients;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/**
4762306a36Sopenharmony_ci * ishtp_hbm_cl_hdr() - construct client hbm header
4862306a36Sopenharmony_ci * @cl: client
4962306a36Sopenharmony_ci * @hbm_cmd: host bus message command
5062306a36Sopenharmony_ci * @buf: buffer for cl header
5162306a36Sopenharmony_ci * @len: buffer length
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * Initialize HBM buffer
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_cistatic inline void ishtp_hbm_cl_hdr(struct ishtp_cl *cl, uint8_t hbm_cmd,
5662306a36Sopenharmony_ci	void *buf, size_t len)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct ishtp_hbm_cl_cmd *cmd = buf;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	memset(cmd, 0, len);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	cmd->hbm_cmd = hbm_cmd;
6362306a36Sopenharmony_ci	cmd->host_addr = cl->host_client_id;
6462306a36Sopenharmony_ci	cmd->fw_addr = cl->fw_client_id;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**
6862306a36Sopenharmony_ci * ishtp_hbm_cl_addr_equal() - Compare client address
6962306a36Sopenharmony_ci * @cl: client
7062306a36Sopenharmony_ci * @buf: Client command buffer
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Compare client address with the address in command buffer
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * Return: True if they have the same address
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic inline bool ishtp_hbm_cl_addr_equal(struct ishtp_cl *cl, void *buf)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct ishtp_hbm_cl_cmd *cmd = buf;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return cl->host_client_id == cmd->host_addr &&
8162306a36Sopenharmony_ci		cl->fw_client_id == cmd->fw_addr;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * ishtp_hbm_start_wait() - Wait for HBM start message
8662306a36Sopenharmony_ci * @dev: ISHTP device instance
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Wait for HBM start message from firmware
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Return: 0 if HBM start is/was received else timeout error
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciint ishtp_hbm_start_wait(struct ishtp_device *dev)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int ret;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (dev->hbm_state > ISHTP_HBM_START)
9762306a36Sopenharmony_ci		return 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	dev_dbg(dev->devc, "Going to wait for ishtp start. hbm_state=%08X\n",
10062306a36Sopenharmony_ci		dev->hbm_state);
10162306a36Sopenharmony_ci	ret = wait_event_interruptible_timeout(dev->wait_hbm_recvd_msg,
10262306a36Sopenharmony_ci					dev->hbm_state >= ISHTP_HBM_STARTED,
10362306a36Sopenharmony_ci					(ISHTP_INTEROP_TIMEOUT * HZ));
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	dev_dbg(dev->devc,
10662306a36Sopenharmony_ci		"Woke up from waiting for ishtp start. hbm_state=%08X\n",
10762306a36Sopenharmony_ci		dev->hbm_state);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (ret <= 0 && (dev->hbm_state <= ISHTP_HBM_START)) {
11062306a36Sopenharmony_ci		dev->hbm_state = ISHTP_HBM_IDLE;
11162306a36Sopenharmony_ci		dev_err(dev->devc,
11262306a36Sopenharmony_ci		"waiting for ishtp start failed. ret=%d hbm_state=%08X\n",
11362306a36Sopenharmony_ci			ret, dev->hbm_state);
11462306a36Sopenharmony_ci		return -ETIMEDOUT;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * ishtp_hbm_start_req() - Send HBM start message
12162306a36Sopenharmony_ci * @dev: ISHTP device instance
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * Send HBM start message to firmware
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * Return: 0 if success else error code
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ciint ishtp_hbm_start_req(struct ishtp_device *dev)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
13062306a36Sopenharmony_ci	struct hbm_host_version_request start_req = { 0 };
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, sizeof(start_req));
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* host start message */
13562306a36Sopenharmony_ci	start_req.hbm_cmd = HOST_START_REQ_CMD;
13662306a36Sopenharmony_ci	start_req.host_version.major_version = HBM_MAJOR_VERSION;
13762306a36Sopenharmony_ci	start_req.host_version.minor_version = HBM_MINOR_VERSION;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/*
14062306a36Sopenharmony_ci	 * (!) Response to HBM start may be so quick that this thread would get
14162306a36Sopenharmony_ci	 * preempted BEFORE managing to set hbm_state = ISHTP_HBM_START.
14262306a36Sopenharmony_ci	 * So set it at first, change back to ISHTP_HBM_IDLE upon failure
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	dev->hbm_state = ISHTP_HBM_START;
14562306a36Sopenharmony_ci	if (ishtp_write_message(dev, &hdr, &start_req)) {
14662306a36Sopenharmony_ci		dev_err(dev->devc, "version message send failed\n");
14762306a36Sopenharmony_ci		dev->dev_state = ISHTP_DEV_RESETTING;
14862306a36Sopenharmony_ci		dev->hbm_state = ISHTP_HBM_IDLE;
14962306a36Sopenharmony_ci		ish_hw_reset(dev);
15062306a36Sopenharmony_ci		return -ENODEV;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/**
15762306a36Sopenharmony_ci * ishtp_hbm_enum_clients_req() - Send client enum req
15862306a36Sopenharmony_ci * @dev: ISHTP device instance
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * Send enumeration client request message
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * Return: 0 if success else error code
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_civoid ishtp_hbm_enum_clients_req(struct ishtp_device *dev)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
16762306a36Sopenharmony_ci	struct hbm_host_enum_request enum_req = { 0 };
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* enumerate clients */
17062306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, sizeof(enum_req));
17162306a36Sopenharmony_ci	enum_req.hbm_cmd = HOST_ENUM_REQ_CMD;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (ishtp_write_message(dev, &hdr, &enum_req)) {
17462306a36Sopenharmony_ci		dev->dev_state = ISHTP_DEV_RESETTING;
17562306a36Sopenharmony_ci		dev_err(dev->devc, "enumeration request send failed\n");
17662306a36Sopenharmony_ci		ish_hw_reset(dev);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	dev->hbm_state = ISHTP_HBM_ENUM_CLIENTS;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * ishtp_hbm_prop_req() - Request property
18362306a36Sopenharmony_ci * @dev: ISHTP device instance
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * Request property for a single client
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * Return: 0 if success else error code
18862306a36Sopenharmony_ci */
18962306a36Sopenharmony_cistatic int ishtp_hbm_prop_req(struct ishtp_device *dev)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
19262306a36Sopenharmony_ci	struct hbm_props_request prop_req = { 0 };
19362306a36Sopenharmony_ci	unsigned long next_client_index;
19462306a36Sopenharmony_ci	uint8_t client_num;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	client_num = dev->fw_client_presentation_num;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	next_client_index = find_next_bit(dev->fw_clients_map,
19962306a36Sopenharmony_ci		ISHTP_CLIENTS_MAX, dev->fw_client_index);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* We got all client properties */
20262306a36Sopenharmony_ci	if (next_client_index == ISHTP_CLIENTS_MAX) {
20362306a36Sopenharmony_ci		dev->hbm_state = ISHTP_HBM_WORKING;
20462306a36Sopenharmony_ci		dev->dev_state = ISHTP_DEV_ENABLED;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		for (dev->fw_client_presentation_num = 1;
20762306a36Sopenharmony_ci			dev->fw_client_presentation_num < client_num + 1;
20862306a36Sopenharmony_ci				++dev->fw_client_presentation_num)
20962306a36Sopenharmony_ci			/* Add new client device */
21062306a36Sopenharmony_ci			ishtp_bus_new_client(dev);
21162306a36Sopenharmony_ci		return 0;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	dev->fw_clients[client_num].client_id = next_client_index;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, sizeof(prop_req));
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	prop_req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
21962306a36Sopenharmony_ci	prop_req.address = next_client_index;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (ishtp_write_message(dev, &hdr, &prop_req)) {
22262306a36Sopenharmony_ci		dev->dev_state = ISHTP_DEV_RESETTING;
22362306a36Sopenharmony_ci		dev_err(dev->devc, "properties request send failed\n");
22462306a36Sopenharmony_ci		ish_hw_reset(dev);
22562306a36Sopenharmony_ci		return -EIO;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	dev->fw_client_index = next_client_index;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/**
23462306a36Sopenharmony_ci * ishtp_hbm_stop_req() - Send HBM stop
23562306a36Sopenharmony_ci * @dev: ISHTP device instance
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * Send stop request message
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_cistatic void ishtp_hbm_stop_req(struct ishtp_device *dev)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
24262306a36Sopenharmony_ci	struct hbm_host_stop_request stop_req = { 0 } ;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, sizeof(stop_req));
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	stop_req.hbm_cmd = HOST_STOP_REQ_CMD;
24762306a36Sopenharmony_ci	stop_req.reason = DRIVER_STOP_REQUEST;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ishtp_write_message(dev, &hdr, &stop_req);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/**
25362306a36Sopenharmony_ci * ishtp_hbm_cl_flow_control_req() - Send flow control request
25462306a36Sopenharmony_ci * @dev: ISHTP device instance
25562306a36Sopenharmony_ci * @cl: ISHTP client instance
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * Send flow control request
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Return: 0 if success else error code
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_ciint ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
26262306a36Sopenharmony_ci				  struct ishtp_cl *cl)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
26562306a36Sopenharmony_ci	struct hbm_flow_control flow_ctrl;
26662306a36Sopenharmony_ci	const size_t len = sizeof(flow_ctrl);
26762306a36Sopenharmony_ci	int	rv;
26862306a36Sopenharmony_ci	unsigned long	flags;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	spin_lock_irqsave(&cl->fc_spinlock, flags);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, len);
27362306a36Sopenharmony_ci	ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, &flow_ctrl, len);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * Sync possible race when RB recycle and packet receive paths
27762306a36Sopenharmony_ci	 * both try to send an out FC
27862306a36Sopenharmony_ci	 */
27962306a36Sopenharmony_ci	if (cl->out_flow_ctrl_creds) {
28062306a36Sopenharmony_ci		spin_unlock_irqrestore(&cl->fc_spinlock, flags);
28162306a36Sopenharmony_ci		return	0;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	cl->recv_msg_num_frags = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	rv = ishtp_write_message(dev, &hdr, &flow_ctrl);
28762306a36Sopenharmony_ci	if (!rv) {
28862306a36Sopenharmony_ci		++cl->out_flow_ctrl_creds;
28962306a36Sopenharmony_ci		++cl->out_flow_ctrl_cnt;
29062306a36Sopenharmony_ci		cl->ts_out_fc = ktime_get();
29162306a36Sopenharmony_ci		if (cl->ts_rx) {
29262306a36Sopenharmony_ci			ktime_t ts_diff = ktime_sub(cl->ts_out_fc, cl->ts_rx);
29362306a36Sopenharmony_ci			if (ktime_after(ts_diff, cl->ts_max_fc_delay))
29462306a36Sopenharmony_ci				cl->ts_max_fc_delay = ts_diff;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci	} else {
29762306a36Sopenharmony_ci		++cl->err_send_fc;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	spin_unlock_irqrestore(&cl->fc_spinlock, flags);
30162306a36Sopenharmony_ci	return	rv;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/**
30562306a36Sopenharmony_ci * ishtp_hbm_cl_disconnect_req() - Send disconnect request
30662306a36Sopenharmony_ci * @dev: ISHTP device instance
30762306a36Sopenharmony_ci * @cl: ISHTP client instance
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Send disconnect message to fw
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * Return: 0 if success else error code
31262306a36Sopenharmony_ci */
31362306a36Sopenharmony_ciint ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
31662306a36Sopenharmony_ci	struct hbm_client_connect_request disconn_req;
31762306a36Sopenharmony_ci	const size_t len = sizeof(disconn_req);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, len);
32062306a36Sopenharmony_ci	ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, &disconn_req, len);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return ishtp_write_message(dev, &hdr, &disconn_req);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/**
32662306a36Sopenharmony_ci * ishtp_hbm_cl_disconnect_res() - Get disconnect response
32762306a36Sopenharmony_ci * @dev: ISHTP device instance
32862306a36Sopenharmony_ci * @rs: Response message
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * Received disconnect response from fw
33162306a36Sopenharmony_ci */
33262306a36Sopenharmony_cistatic void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev,
33362306a36Sopenharmony_ci	struct hbm_client_connect_response *rs)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct ishtp_cl *cl = NULL;
33662306a36Sopenharmony_ci	unsigned long	flags;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->cl_list_lock, flags);
33962306a36Sopenharmony_ci	list_for_each_entry(cl, &dev->cl_list, link) {
34062306a36Sopenharmony_ci		if (!rs->status && ishtp_hbm_cl_addr_equal(cl, rs)) {
34162306a36Sopenharmony_ci			cl->state = ISHTP_CL_DISCONNECTED;
34262306a36Sopenharmony_ci			wake_up_interruptible(&cl->wait_ctrl_res);
34362306a36Sopenharmony_ci			break;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->cl_list_lock, flags);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/**
35062306a36Sopenharmony_ci * ishtp_hbm_cl_connect_req() - Send connect request
35162306a36Sopenharmony_ci * @dev: ISHTP device instance
35262306a36Sopenharmony_ci * @cl: client device instance
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * Send connection request to specific fw client
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci * Return: 0 if success else error code
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_ciint ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
36162306a36Sopenharmony_ci	struct hbm_client_connect_request conn_req;
36262306a36Sopenharmony_ci	const size_t len = sizeof(conn_req);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, len);
36562306a36Sopenharmony_ci	ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, &conn_req, len);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return ishtp_write_message(dev, &hdr, &conn_req);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/**
37162306a36Sopenharmony_ci * ishtp_hbm_cl_connect_res() - Get connect response
37262306a36Sopenharmony_ci * @dev: ISHTP device instance
37362306a36Sopenharmony_ci * @rs: Response message
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Received connect response from fw
37662306a36Sopenharmony_ci */
37762306a36Sopenharmony_cistatic void ishtp_hbm_cl_connect_res(struct ishtp_device *dev,
37862306a36Sopenharmony_ci	struct hbm_client_connect_response *rs)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct ishtp_cl *cl = NULL;
38162306a36Sopenharmony_ci	unsigned long	flags;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	spin_lock_irqsave(&dev->cl_list_lock, flags);
38462306a36Sopenharmony_ci	list_for_each_entry(cl, &dev->cl_list, link) {
38562306a36Sopenharmony_ci		if (ishtp_hbm_cl_addr_equal(cl, rs)) {
38662306a36Sopenharmony_ci			if (!rs->status) {
38762306a36Sopenharmony_ci				cl->state = ISHTP_CL_CONNECTED;
38862306a36Sopenharmony_ci				cl->status = 0;
38962306a36Sopenharmony_ci			} else {
39062306a36Sopenharmony_ci				cl->state = ISHTP_CL_DISCONNECTED;
39162306a36Sopenharmony_ci				cl->status = -ENODEV;
39262306a36Sopenharmony_ci			}
39362306a36Sopenharmony_ci			wake_up_interruptible(&cl->wait_ctrl_res);
39462306a36Sopenharmony_ci			break;
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->cl_list_lock, flags);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/**
40162306a36Sopenharmony_ci * ishtp_hbm_fw_disconnect_req() - Receive disconnect request
40262306a36Sopenharmony_ci * @dev: ISHTP device instance
40362306a36Sopenharmony_ci * @disconnect_req: disconnect request structure
40462306a36Sopenharmony_ci *
40562306a36Sopenharmony_ci * Disconnect request bus message from the fw. Send disconnect response.
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_cistatic void ishtp_hbm_fw_disconnect_req(struct ishtp_device *dev,
40862306a36Sopenharmony_ci	struct hbm_client_connect_request *disconnect_req)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct ishtp_cl *cl;
41162306a36Sopenharmony_ci	const size_t len = sizeof(struct hbm_client_connect_response);
41262306a36Sopenharmony_ci	unsigned long	flags;
41362306a36Sopenharmony_ci	struct ishtp_msg_hdr hdr;
41462306a36Sopenharmony_ci	unsigned char data[4];	/* All HBM messages are 4 bytes */
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	spin_lock_irqsave(&dev->cl_list_lock, flags);
41762306a36Sopenharmony_ci	list_for_each_entry(cl, &dev->cl_list, link) {
41862306a36Sopenharmony_ci		if (ishtp_hbm_cl_addr_equal(cl, disconnect_req)) {
41962306a36Sopenharmony_ci			cl->state = ISHTP_CL_DISCONNECTED;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci			/* send disconnect response */
42262306a36Sopenharmony_ci			ishtp_hbm_hdr(&hdr, len);
42362306a36Sopenharmony_ci			ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, data,
42462306a36Sopenharmony_ci				len);
42562306a36Sopenharmony_ci			ishtp_write_message(dev, &hdr, data);
42662306a36Sopenharmony_ci			break;
42762306a36Sopenharmony_ci		}
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->cl_list_lock, flags);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/**
43362306a36Sopenharmony_ci * ishtp_hbm_dma_xfer_ack() - Receive transfer ACK
43462306a36Sopenharmony_ci * @dev: ISHTP device instance
43562306a36Sopenharmony_ci * @dma_xfer: HBM transfer message
43662306a36Sopenharmony_ci *
43762306a36Sopenharmony_ci * Receive ack for ISHTP-over-DMA client message
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_cistatic void ishtp_hbm_dma_xfer_ack(struct ishtp_device *dev,
44062306a36Sopenharmony_ci				   struct dma_xfer_hbm *dma_xfer)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	void	*msg;
44362306a36Sopenharmony_ci	uint64_t	offs;
44462306a36Sopenharmony_ci	struct ishtp_msg_hdr	*ishtp_hdr =
44562306a36Sopenharmony_ci		(struct ishtp_msg_hdr *)&dev->ishtp_msg_hdr;
44662306a36Sopenharmony_ci	unsigned int	msg_offs;
44762306a36Sopenharmony_ci	struct ishtp_cl *cl;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	for (msg_offs = 0; msg_offs < ishtp_hdr->length;
45062306a36Sopenharmony_ci		msg_offs += sizeof(struct dma_xfer_hbm)) {
45162306a36Sopenharmony_ci		offs = dma_xfer->msg_addr - dev->ishtp_host_dma_tx_buf_phys;
45262306a36Sopenharmony_ci		if (offs > dev->ishtp_host_dma_tx_buf_size) {
45362306a36Sopenharmony_ci			dev_err(dev->devc, "Bad DMA Tx ack message address\n");
45462306a36Sopenharmony_ci			return;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		if (dma_xfer->msg_length >
45762306a36Sopenharmony_ci				dev->ishtp_host_dma_tx_buf_size - offs) {
45862306a36Sopenharmony_ci			dev_err(dev->devc, "Bad DMA Tx ack message size\n");
45962306a36Sopenharmony_ci			return;
46062306a36Sopenharmony_ci		}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		/* logical address of the acked mem */
46362306a36Sopenharmony_ci		msg = (unsigned char *)dev->ishtp_host_dma_tx_buf + offs;
46462306a36Sopenharmony_ci		ishtp_cl_release_dma_acked_mem(dev, msg, dma_xfer->msg_length);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		list_for_each_entry(cl, &dev->cl_list, link) {
46762306a36Sopenharmony_ci			if (cl->fw_client_id == dma_xfer->fw_client_id &&
46862306a36Sopenharmony_ci			    cl->host_client_id == dma_xfer->host_client_id)
46962306a36Sopenharmony_ci				/*
47062306a36Sopenharmony_ci				 * in case that a single ack may be sent
47162306a36Sopenharmony_ci				 * over several dma transfers, and the last msg
47262306a36Sopenharmony_ci				 * addr was inside the acked memory, but not in
47362306a36Sopenharmony_ci				 * its start
47462306a36Sopenharmony_ci				 */
47562306a36Sopenharmony_ci				if (cl->last_dma_addr >=
47662306a36Sopenharmony_ci							(unsigned char *)msg &&
47762306a36Sopenharmony_ci						cl->last_dma_addr <
47862306a36Sopenharmony_ci						(unsigned char *)msg +
47962306a36Sopenharmony_ci						dma_xfer->msg_length) {
48062306a36Sopenharmony_ci					cl->last_dma_acked = 1;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci					if (!list_empty(&cl->tx_list.list) &&
48362306a36Sopenharmony_ci						cl->ishtp_flow_ctrl_creds) {
48462306a36Sopenharmony_ci						/*
48562306a36Sopenharmony_ci						 * start sending the first msg
48662306a36Sopenharmony_ci						 */
48762306a36Sopenharmony_ci						ishtp_cl_send_msg(dev, cl);
48862306a36Sopenharmony_ci					}
48962306a36Sopenharmony_ci				}
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci		++dma_xfer;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * ishtp_hbm_dma_xfer() - Receive DMA transfer message
49762306a36Sopenharmony_ci * @dev: ISHTP device instance
49862306a36Sopenharmony_ci * @dma_xfer: HBM transfer message
49962306a36Sopenharmony_ci *
50062306a36Sopenharmony_ci * Receive ISHTP-over-DMA client message
50162306a36Sopenharmony_ci */
50262306a36Sopenharmony_cistatic void ishtp_hbm_dma_xfer(struct ishtp_device *dev,
50362306a36Sopenharmony_ci			       struct dma_xfer_hbm *dma_xfer)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	void	*msg;
50662306a36Sopenharmony_ci	uint64_t	offs;
50762306a36Sopenharmony_ci	struct ishtp_msg_hdr	hdr;
50862306a36Sopenharmony_ci	struct ishtp_msg_hdr	*ishtp_hdr =
50962306a36Sopenharmony_ci		(struct ishtp_msg_hdr *) &dev->ishtp_msg_hdr;
51062306a36Sopenharmony_ci	struct dma_xfer_hbm	*prm = dma_xfer;
51162306a36Sopenharmony_ci	unsigned int	msg_offs;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	for (msg_offs = 0; msg_offs < ishtp_hdr->length;
51462306a36Sopenharmony_ci		msg_offs += sizeof(struct dma_xfer_hbm)) {
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		offs = dma_xfer->msg_addr - dev->ishtp_host_dma_rx_buf_phys;
51762306a36Sopenharmony_ci		if (offs > dev->ishtp_host_dma_rx_buf_size) {
51862306a36Sopenharmony_ci			dev_err(dev->devc, "Bad DMA Rx message address\n");
51962306a36Sopenharmony_ci			return;
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci		if (dma_xfer->msg_length >
52262306a36Sopenharmony_ci				dev->ishtp_host_dma_rx_buf_size - offs) {
52362306a36Sopenharmony_ci			dev_err(dev->devc, "Bad DMA Rx message size\n");
52462306a36Sopenharmony_ci			return;
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci		msg = dev->ishtp_host_dma_rx_buf + offs;
52762306a36Sopenharmony_ci		recv_ishtp_cl_msg_dma(dev, msg, dma_xfer);
52862306a36Sopenharmony_ci		dma_xfer->hbm = DMA_XFER_ACK;	/* Prepare for response */
52962306a36Sopenharmony_ci		++dma_xfer;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Send DMA_XFER_ACK [...] */
53362306a36Sopenharmony_ci	ishtp_hbm_hdr(&hdr, ishtp_hdr->length);
53462306a36Sopenharmony_ci	ishtp_write_message(dev, &hdr, (unsigned char *)prm);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/**
53862306a36Sopenharmony_ci * ishtp_hbm_dispatch() - HBM dispatch function
53962306a36Sopenharmony_ci * @dev: ISHTP device instance
54062306a36Sopenharmony_ci * @hdr: bus message
54162306a36Sopenharmony_ci *
54262306a36Sopenharmony_ci * Bottom half read routine after ISR to handle the read bus message cmd
54362306a36Sopenharmony_ci * processing
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_civoid ishtp_hbm_dispatch(struct ishtp_device *dev,
54662306a36Sopenharmony_ci			struct ishtp_bus_message *hdr)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct ishtp_bus_message *ishtp_msg;
54962306a36Sopenharmony_ci	struct ishtp_fw_client *fw_client;
55062306a36Sopenharmony_ci	struct hbm_host_version_response *version_res;
55162306a36Sopenharmony_ci	struct hbm_client_connect_response *connect_res;
55262306a36Sopenharmony_ci	struct hbm_client_connect_response *disconnect_res;
55362306a36Sopenharmony_ci	struct hbm_client_connect_request *disconnect_req;
55462306a36Sopenharmony_ci	struct hbm_props_response *props_res;
55562306a36Sopenharmony_ci	struct hbm_host_enum_response *enum_res;
55662306a36Sopenharmony_ci	struct ishtp_msg_hdr ishtp_hdr;
55762306a36Sopenharmony_ci	struct dma_alloc_notify	dma_alloc_notify;
55862306a36Sopenharmony_ci	struct dma_xfer_hbm	*dma_xfer;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	ishtp_msg = hdr;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	switch (ishtp_msg->hbm_cmd) {
56362306a36Sopenharmony_ci	case HOST_START_RES_CMD:
56462306a36Sopenharmony_ci		version_res = (struct hbm_host_version_response *)ishtp_msg;
56562306a36Sopenharmony_ci		if (!version_res->host_version_supported) {
56662306a36Sopenharmony_ci			dev->version = version_res->fw_max_version;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			dev->hbm_state = ISHTP_HBM_STOPPED;
56962306a36Sopenharmony_ci			ishtp_hbm_stop_req(dev);
57062306a36Sopenharmony_ci			return;
57162306a36Sopenharmony_ci		}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		dev->version.major_version = HBM_MAJOR_VERSION;
57462306a36Sopenharmony_ci		dev->version.minor_version = HBM_MINOR_VERSION;
57562306a36Sopenharmony_ci		if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS &&
57662306a36Sopenharmony_ci				dev->hbm_state == ISHTP_HBM_START) {
57762306a36Sopenharmony_ci			dev->hbm_state = ISHTP_HBM_STARTED;
57862306a36Sopenharmony_ci			ishtp_hbm_enum_clients_req(dev);
57962306a36Sopenharmony_ci		} else {
58062306a36Sopenharmony_ci			dev_err(dev->devc,
58162306a36Sopenharmony_ci				"reset: wrong host start response\n");
58262306a36Sopenharmony_ci			/* BUG: why do we arrive here? */
58362306a36Sopenharmony_ci			ish_hw_reset(dev);
58462306a36Sopenharmony_ci			return;
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		wake_up_interruptible(&dev->wait_hbm_recvd_msg);
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	case CLIENT_CONNECT_RES_CMD:
59162306a36Sopenharmony_ci		connect_res = (struct hbm_client_connect_response *)ishtp_msg;
59262306a36Sopenharmony_ci		ishtp_hbm_cl_connect_res(dev, connect_res);
59362306a36Sopenharmony_ci		break;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	case CLIENT_DISCONNECT_RES_CMD:
59662306a36Sopenharmony_ci		disconnect_res =
59762306a36Sopenharmony_ci			(struct hbm_client_connect_response *)ishtp_msg;
59862306a36Sopenharmony_ci		ishtp_hbm_cl_disconnect_res(dev, disconnect_res);
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	case HOST_CLIENT_PROPERTIES_RES_CMD:
60262306a36Sopenharmony_ci		props_res = (struct hbm_props_response *)ishtp_msg;
60362306a36Sopenharmony_ci		fw_client = &dev->fw_clients[dev->fw_client_presentation_num];
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		if (props_res->status || !dev->fw_clients) {
60662306a36Sopenharmony_ci			dev_err(dev->devc,
60762306a36Sopenharmony_ci			"reset: properties response hbm wrong status\n");
60862306a36Sopenharmony_ci			ish_hw_reset(dev);
60962306a36Sopenharmony_ci			return;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		if (fw_client->client_id != props_res->address) {
61362306a36Sopenharmony_ci			dev_err(dev->devc,
61462306a36Sopenharmony_ci				"reset: host properties response address mismatch [%02X %02X]\n",
61562306a36Sopenharmony_ci				fw_client->client_id, props_res->address);
61662306a36Sopenharmony_ci			ish_hw_reset(dev);
61762306a36Sopenharmony_ci			return;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		if (dev->dev_state != ISHTP_DEV_INIT_CLIENTS ||
62162306a36Sopenharmony_ci			dev->hbm_state != ISHTP_HBM_CLIENT_PROPERTIES) {
62262306a36Sopenharmony_ci			dev_err(dev->devc,
62362306a36Sopenharmony_ci				"reset: unexpected properties response\n");
62462306a36Sopenharmony_ci			ish_hw_reset(dev);
62562306a36Sopenharmony_ci			return;
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		fw_client->props = props_res->client_properties;
62962306a36Sopenharmony_ci		dev->fw_client_index++;
63062306a36Sopenharmony_ci		dev->fw_client_presentation_num++;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		/* request property for the next client */
63362306a36Sopenharmony_ci		ishtp_hbm_prop_req(dev);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		if (dev->dev_state != ISHTP_DEV_ENABLED)
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		if (!ishtp_use_dma_transfer())
63962306a36Sopenharmony_ci			break;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		dev_dbg(dev->devc, "Requesting to use DMA\n");
64262306a36Sopenharmony_ci		ishtp_cl_alloc_dma_buf(dev);
64362306a36Sopenharmony_ci		if (dev->ishtp_host_dma_rx_buf) {
64462306a36Sopenharmony_ci			const size_t len = sizeof(dma_alloc_notify);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci			memset(&dma_alloc_notify, 0, sizeof(dma_alloc_notify));
64762306a36Sopenharmony_ci			dma_alloc_notify.hbm = DMA_BUFFER_ALLOC_NOTIFY;
64862306a36Sopenharmony_ci			dma_alloc_notify.buf_size =
64962306a36Sopenharmony_ci					dev->ishtp_host_dma_rx_buf_size;
65062306a36Sopenharmony_ci			dma_alloc_notify.buf_address =
65162306a36Sopenharmony_ci					dev->ishtp_host_dma_rx_buf_phys;
65262306a36Sopenharmony_ci			ishtp_hbm_hdr(&ishtp_hdr, len);
65362306a36Sopenharmony_ci			ishtp_write_message(dev, &ishtp_hdr,
65462306a36Sopenharmony_ci				(unsigned char *)&dma_alloc_notify);
65562306a36Sopenharmony_ci		}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		break;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	case HOST_ENUM_RES_CMD:
66062306a36Sopenharmony_ci		enum_res = (struct hbm_host_enum_response *) ishtp_msg;
66162306a36Sopenharmony_ci		memcpy(dev->fw_clients_map, enum_res->valid_addresses, 32);
66262306a36Sopenharmony_ci		if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS &&
66362306a36Sopenharmony_ci			dev->hbm_state == ISHTP_HBM_ENUM_CLIENTS) {
66462306a36Sopenharmony_ci			dev->fw_client_presentation_num = 0;
66562306a36Sopenharmony_ci			dev->fw_client_index = 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci			ishtp_hbm_fw_cl_allocate(dev);
66862306a36Sopenharmony_ci			dev->hbm_state = ISHTP_HBM_CLIENT_PROPERTIES;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci			/* first property request */
67162306a36Sopenharmony_ci			ishtp_hbm_prop_req(dev);
67262306a36Sopenharmony_ci		} else {
67362306a36Sopenharmony_ci			dev_err(dev->devc,
67462306a36Sopenharmony_ci			      "reset: unexpected enumeration response hbm\n");
67562306a36Sopenharmony_ci			ish_hw_reset(dev);
67662306a36Sopenharmony_ci			return;
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci		break;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	case HOST_STOP_RES_CMD:
68162306a36Sopenharmony_ci		if (dev->hbm_state != ISHTP_HBM_STOPPED)
68262306a36Sopenharmony_ci			dev_err(dev->devc, "unexpected stop response\n");
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		dev->dev_state = ISHTP_DEV_DISABLED;
68562306a36Sopenharmony_ci		dev_info(dev->devc, "reset: FW stop response\n");
68662306a36Sopenharmony_ci		ish_hw_reset(dev);
68762306a36Sopenharmony_ci		break;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	case CLIENT_DISCONNECT_REQ_CMD:
69062306a36Sopenharmony_ci		/* search for client */
69162306a36Sopenharmony_ci		disconnect_req =
69262306a36Sopenharmony_ci			(struct hbm_client_connect_request *)ishtp_msg;
69362306a36Sopenharmony_ci		ishtp_hbm_fw_disconnect_req(dev, disconnect_req);
69462306a36Sopenharmony_ci		break;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	case FW_STOP_REQ_CMD:
69762306a36Sopenharmony_ci		dev->hbm_state = ISHTP_HBM_STOPPED;
69862306a36Sopenharmony_ci		break;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	case DMA_BUFFER_ALLOC_RESPONSE:
70162306a36Sopenharmony_ci		dev->ishtp_host_dma_enabled = 1;
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	case DMA_XFER:
70562306a36Sopenharmony_ci		dma_xfer = (struct dma_xfer_hbm *)ishtp_msg;
70662306a36Sopenharmony_ci		if (!dev->ishtp_host_dma_enabled) {
70762306a36Sopenharmony_ci			dev_err(dev->devc,
70862306a36Sopenharmony_ci				"DMA XFER requested but DMA is not enabled\n");
70962306a36Sopenharmony_ci			break;
71062306a36Sopenharmony_ci		}
71162306a36Sopenharmony_ci		ishtp_hbm_dma_xfer(dev, dma_xfer);
71262306a36Sopenharmony_ci		break;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	case DMA_XFER_ACK:
71562306a36Sopenharmony_ci		dma_xfer = (struct dma_xfer_hbm *)ishtp_msg;
71662306a36Sopenharmony_ci		if (!dev->ishtp_host_dma_enabled ||
71762306a36Sopenharmony_ci		    !dev->ishtp_host_dma_tx_buf) {
71862306a36Sopenharmony_ci			dev_err(dev->devc,
71962306a36Sopenharmony_ci				"DMA XFER acked but DMA Tx is not enabled\n");
72062306a36Sopenharmony_ci			break;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci		ishtp_hbm_dma_xfer_ack(dev, dma_xfer);
72362306a36Sopenharmony_ci		break;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	default:
72662306a36Sopenharmony_ci		dev_err(dev->devc, "unknown HBM: %u\n",
72762306a36Sopenharmony_ci			(unsigned int)ishtp_msg->hbm_cmd);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci/**
73462306a36Sopenharmony_ci * bh_hbm_work_fn() - HBM work function
73562306a36Sopenharmony_ci * @work: work struct
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * Bottom half processing work function (instead of thread handler)
73862306a36Sopenharmony_ci * for processing hbm messages
73962306a36Sopenharmony_ci */
74062306a36Sopenharmony_civoid	bh_hbm_work_fn(struct work_struct *work)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	unsigned long	flags;
74362306a36Sopenharmony_ci	struct ishtp_device	*dev;
74462306a36Sopenharmony_ci	unsigned char	hbm[IPC_PAYLOAD_SIZE];
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	dev = container_of(work, struct ishtp_device, bh_hbm_work);
74762306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rd_msg_spinlock, flags);
74862306a36Sopenharmony_ci	if (dev->rd_msg_fifo_head != dev->rd_msg_fifo_tail) {
74962306a36Sopenharmony_ci		memcpy(hbm, dev->rd_msg_fifo + dev->rd_msg_fifo_head,
75062306a36Sopenharmony_ci			IPC_PAYLOAD_SIZE);
75162306a36Sopenharmony_ci		dev->rd_msg_fifo_head =
75262306a36Sopenharmony_ci			(dev->rd_msg_fifo_head + IPC_PAYLOAD_SIZE) %
75362306a36Sopenharmony_ci			(RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE);
75462306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
75562306a36Sopenharmony_ci		ishtp_hbm_dispatch(dev, (struct ishtp_bus_message *)hbm);
75662306a36Sopenharmony_ci	} else {
75762306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci/**
76262306a36Sopenharmony_ci * recv_hbm() - Receive HBM message
76362306a36Sopenharmony_ci * @dev: ISHTP device instance
76462306a36Sopenharmony_ci * @ishtp_hdr: received bus message
76562306a36Sopenharmony_ci *
76662306a36Sopenharmony_ci * Receive and process ISHTP bus messages in ISR context. This will schedule
76762306a36Sopenharmony_ci * work function to process message
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_civoid	recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	uint8_t	rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE];
77262306a36Sopenharmony_ci	struct ishtp_bus_message	*ishtp_msg =
77362306a36Sopenharmony_ci		(struct ishtp_bus_message *)rd_msg_buf;
77462306a36Sopenharmony_ci	unsigned long	flags;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* Flow control - handle in place */
77962306a36Sopenharmony_ci	if (ishtp_msg->hbm_cmd == ISHTP_FLOW_CONTROL_CMD) {
78062306a36Sopenharmony_ci		struct hbm_flow_control *flow_control =
78162306a36Sopenharmony_ci			(struct hbm_flow_control *)ishtp_msg;
78262306a36Sopenharmony_ci		struct ishtp_cl *cl = NULL;
78362306a36Sopenharmony_ci		unsigned long	flags, tx_flags;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		spin_lock_irqsave(&dev->cl_list_lock, flags);
78662306a36Sopenharmony_ci		list_for_each_entry(cl, &dev->cl_list, link) {
78762306a36Sopenharmony_ci			if (cl->host_client_id == flow_control->host_addr &&
78862306a36Sopenharmony_ci					cl->fw_client_id ==
78962306a36Sopenharmony_ci					flow_control->fw_addr) {
79062306a36Sopenharmony_ci				/*
79162306a36Sopenharmony_ci				 * NOTE: It's valid only for counting
79262306a36Sopenharmony_ci				 * flow-control implementation to receive a
79362306a36Sopenharmony_ci				 * FC in the middle of sending. Meanwhile not
79462306a36Sopenharmony_ci				 * supported
79562306a36Sopenharmony_ci				 */
79662306a36Sopenharmony_ci				if (cl->ishtp_flow_ctrl_creds)
79762306a36Sopenharmony_ci					dev_err(dev->devc,
79862306a36Sopenharmony_ci					 "recv extra FC from FW client %u (host client %u) (FC count was %d)\n",
79962306a36Sopenharmony_ci					 (unsigned int)cl->fw_client_id,
80062306a36Sopenharmony_ci					 (unsigned int)cl->host_client_id,
80162306a36Sopenharmony_ci					 cl->ishtp_flow_ctrl_creds);
80262306a36Sopenharmony_ci				else {
80362306a36Sopenharmony_ci					++cl->ishtp_flow_ctrl_creds;
80462306a36Sopenharmony_ci					++cl->ishtp_flow_ctrl_cnt;
80562306a36Sopenharmony_ci					cl->last_ipc_acked = 1;
80662306a36Sopenharmony_ci					spin_lock_irqsave(
80762306a36Sopenharmony_ci							&cl->tx_list_spinlock,
80862306a36Sopenharmony_ci							tx_flags);
80962306a36Sopenharmony_ci					if (!list_empty(&cl->tx_list.list)) {
81062306a36Sopenharmony_ci						/*
81162306a36Sopenharmony_ci						 * start sending the first msg
81262306a36Sopenharmony_ci						 *	= the callback function
81362306a36Sopenharmony_ci						 */
81462306a36Sopenharmony_ci						spin_unlock_irqrestore(
81562306a36Sopenharmony_ci							&cl->tx_list_spinlock,
81662306a36Sopenharmony_ci							tx_flags);
81762306a36Sopenharmony_ci						ishtp_cl_send_msg(dev, cl);
81862306a36Sopenharmony_ci					} else {
81962306a36Sopenharmony_ci						spin_unlock_irqrestore(
82062306a36Sopenharmony_ci							&cl->tx_list_spinlock,
82162306a36Sopenharmony_ci							tx_flags);
82262306a36Sopenharmony_ci					}
82362306a36Sopenharmony_ci				}
82462306a36Sopenharmony_ci				break;
82562306a36Sopenharmony_ci			}
82662306a36Sopenharmony_ci		}
82762306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->cl_list_lock, flags);
82862306a36Sopenharmony_ci		goto	eoi;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/*
83262306a36Sopenharmony_ci	 * Some messages that are safe for ISR processing and important
83362306a36Sopenharmony_ci	 * to be done "quickly" and in-order, go here
83462306a36Sopenharmony_ci	 */
83562306a36Sopenharmony_ci	if (ishtp_msg->hbm_cmd == CLIENT_CONNECT_RES_CMD ||
83662306a36Sopenharmony_ci			ishtp_msg->hbm_cmd == CLIENT_DISCONNECT_RES_CMD ||
83762306a36Sopenharmony_ci			ishtp_msg->hbm_cmd == CLIENT_DISCONNECT_REQ_CMD ||
83862306a36Sopenharmony_ci			ishtp_msg->hbm_cmd == DMA_XFER) {
83962306a36Sopenharmony_ci		ishtp_hbm_dispatch(dev, ishtp_msg);
84062306a36Sopenharmony_ci		goto	eoi;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * All other HBMs go here.
84562306a36Sopenharmony_ci	 * We schedule HBMs for processing serially by using system wq,
84662306a36Sopenharmony_ci	 * possibly there will be multiple HBMs scheduled at the same time.
84762306a36Sopenharmony_ci	 */
84862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rd_msg_spinlock, flags);
84962306a36Sopenharmony_ci	if ((dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) %
85062306a36Sopenharmony_ci			(RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE) ==
85162306a36Sopenharmony_ci			dev->rd_msg_fifo_head) {
85262306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
85362306a36Sopenharmony_ci		dev_err(dev->devc, "BH buffer overflow, dropping HBM %u\n",
85462306a36Sopenharmony_ci			(unsigned int)ishtp_msg->hbm_cmd);
85562306a36Sopenharmony_ci		goto	eoi;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci	memcpy(dev->rd_msg_fifo + dev->rd_msg_fifo_tail, ishtp_msg,
85862306a36Sopenharmony_ci		ishtp_hdr->length);
85962306a36Sopenharmony_ci	dev->rd_msg_fifo_tail = (dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) %
86062306a36Sopenharmony_ci		(RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE);
86162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
86262306a36Sopenharmony_ci	schedule_work(&dev->bh_hbm_work);
86362306a36Sopenharmony_cieoi:
86462306a36Sopenharmony_ci	return;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/**
86862306a36Sopenharmony_ci * recv_fixed_cl_msg() - Receive fixed client message
86962306a36Sopenharmony_ci * @dev: ISHTP device instance
87062306a36Sopenharmony_ci * @ishtp_hdr: received bus message
87162306a36Sopenharmony_ci *
87262306a36Sopenharmony_ci * Receive and process ISHTP fixed client messages (address == 0)
87362306a36Sopenharmony_ci * in ISR context
87462306a36Sopenharmony_ci */
87562306a36Sopenharmony_civoid recv_fixed_cl_msg(struct ishtp_device *dev,
87662306a36Sopenharmony_ci	struct ishtp_msg_hdr *ishtp_hdr)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE];
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	dev->print_log(dev,
88162306a36Sopenharmony_ci		"%s() got fixed client msg from client #%d\n",
88262306a36Sopenharmony_ci		__func__, ishtp_hdr->fw_addr);
88362306a36Sopenharmony_ci	dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length);
88462306a36Sopenharmony_ci	if (ishtp_hdr->fw_addr == ISHTP_SYSTEM_STATE_CLIENT_ADDR) {
88562306a36Sopenharmony_ci		struct ish_system_states_header *msg_hdr =
88662306a36Sopenharmony_ci			(struct ish_system_states_header *)rd_msg_buf;
88762306a36Sopenharmony_ci		if (msg_hdr->cmd == SYSTEM_STATE_SUBSCRIBE)
88862306a36Sopenharmony_ci			ishtp_send_resume(dev);
88962306a36Sopenharmony_ci		/* if FW request arrived here, the system is not suspended */
89062306a36Sopenharmony_ci		else
89162306a36Sopenharmony_ci			dev_err(dev->devc, "unknown fixed client msg [%02X]\n",
89262306a36Sopenharmony_ci				msg_hdr->cmd);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/**
89762306a36Sopenharmony_ci * fix_cl_hdr() - Initialize fixed client header
89862306a36Sopenharmony_ci * @hdr: message header
89962306a36Sopenharmony_ci * @length: length of message
90062306a36Sopenharmony_ci * @cl_addr: Client address
90162306a36Sopenharmony_ci *
90262306a36Sopenharmony_ci * Initialize message header for fixed client
90362306a36Sopenharmony_ci */
90462306a36Sopenharmony_cistatic inline void fix_cl_hdr(struct ishtp_msg_hdr *hdr, size_t length,
90562306a36Sopenharmony_ci	uint8_t cl_addr)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	hdr->host_addr = 0;
90862306a36Sopenharmony_ci	hdr->fw_addr = cl_addr;
90962306a36Sopenharmony_ci	hdr->length = length;
91062306a36Sopenharmony_ci	hdr->msg_complete = 1;
91162306a36Sopenharmony_ci	hdr->reserved = 0;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci/*** Suspend and resume notification ***/
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic uint32_t current_state;
91762306a36Sopenharmony_cistatic uint32_t supported_states = SUSPEND_STATE_BIT | CONNECTED_STANDBY_STATE_BIT;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci/**
92062306a36Sopenharmony_ci * ishtp_send_suspend() - Send suspend message to FW
92162306a36Sopenharmony_ci * @dev: ISHTP device instance
92262306a36Sopenharmony_ci *
92362306a36Sopenharmony_ci * Send suspend message to FW. This is useful for system freeze (non S3) case
92462306a36Sopenharmony_ci */
92562306a36Sopenharmony_civoid ishtp_send_suspend(struct ishtp_device *dev)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	struct ishtp_msg_hdr	ishtp_hdr;
92862306a36Sopenharmony_ci	struct ish_system_states_status state_status_msg;
92962306a36Sopenharmony_ci	const size_t len = sizeof(struct ish_system_states_status);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	memset(&state_status_msg, 0, len);
93462306a36Sopenharmony_ci	state_status_msg.hdr.cmd = SYSTEM_STATE_STATUS;
93562306a36Sopenharmony_ci	state_status_msg.supported_states = supported_states;
93662306a36Sopenharmony_ci	current_state |= (SUSPEND_STATE_BIT | CONNECTED_STANDBY_STATE_BIT);
93762306a36Sopenharmony_ci	dev->print_log(dev, "%s() sends SUSPEND notification\n", __func__);
93862306a36Sopenharmony_ci	state_status_msg.states_status = current_state;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	ishtp_write_message(dev, &ishtp_hdr,
94162306a36Sopenharmony_ci		(unsigned char *)&state_status_msg);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_send_suspend);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/**
94662306a36Sopenharmony_ci * ishtp_send_resume() - Send resume message to FW
94762306a36Sopenharmony_ci * @dev: ISHTP device instance
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * Send resume message to FW. This is useful for system freeze (non S3) case
95062306a36Sopenharmony_ci */
95162306a36Sopenharmony_civoid ishtp_send_resume(struct ishtp_device *dev)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	struct ishtp_msg_hdr	ishtp_hdr;
95462306a36Sopenharmony_ci	struct ish_system_states_status state_status_msg;
95562306a36Sopenharmony_ci	const size_t len = sizeof(struct ish_system_states_status);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	memset(&state_status_msg, 0, len);
96062306a36Sopenharmony_ci	state_status_msg.hdr.cmd = SYSTEM_STATE_STATUS;
96162306a36Sopenharmony_ci	state_status_msg.supported_states = supported_states;
96262306a36Sopenharmony_ci	current_state &= ~(CONNECTED_STANDBY_STATE_BIT | SUSPEND_STATE_BIT);
96362306a36Sopenharmony_ci	dev->print_log(dev, "%s() sends RESUME notification\n", __func__);
96462306a36Sopenharmony_ci	state_status_msg.states_status = current_state;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	ishtp_write_message(dev, &ishtp_hdr,
96762306a36Sopenharmony_ci		(unsigned char *)&state_status_msg);
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_send_resume);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci/**
97262306a36Sopenharmony_ci * ishtp_query_subscribers() - Send query subscribers message
97362306a36Sopenharmony_ci * @dev: ISHTP device instance
97462306a36Sopenharmony_ci *
97562306a36Sopenharmony_ci * Send message to query subscribers
97662306a36Sopenharmony_ci */
97762306a36Sopenharmony_civoid ishtp_query_subscribers(struct ishtp_device *dev)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct ishtp_msg_hdr	ishtp_hdr;
98062306a36Sopenharmony_ci	struct ish_system_states_query_subscribers query_subscribers_msg;
98162306a36Sopenharmony_ci	const size_t len = sizeof(struct ish_system_states_query_subscribers);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	memset(&query_subscribers_msg, 0, len);
98662306a36Sopenharmony_ci	query_subscribers_msg.hdr.cmd = SYSTEM_STATE_QUERY_SUBSCRIBERS;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	ishtp_write_message(dev, &ishtp_hdr,
98962306a36Sopenharmony_ci		(unsigned char *)&query_subscribers_msg);
99062306a36Sopenharmony_ci}
991