xref: /OK3568_Linux_fs/kernel/drivers/misc/mei/hbm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2003-2020, Intel Corporation. All rights reserved.
4*4882a593Smuzhiyun  * Intel Management Engine Interface (Intel MEI) Linux driver
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun #include <linux/export.h>
7*4882a593Smuzhiyun #include <linux/sched.h>
8*4882a593Smuzhiyun #include <linux/wait.h>
9*4882a593Smuzhiyun #include <linux/pm_runtime.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/mei.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "mei_dev.h"
15*4882a593Smuzhiyun #include "hbm.h"
16*4882a593Smuzhiyun #include "client.h"
17*4882a593Smuzhiyun 
mei_hbm_status_str(enum mei_hbm_status status)18*4882a593Smuzhiyun static const char *mei_hbm_status_str(enum mei_hbm_status status)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun #define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status
21*4882a593Smuzhiyun 	switch (status) {
22*4882a593Smuzhiyun 	MEI_HBM_STATUS(SUCCESS);
23*4882a593Smuzhiyun 	MEI_HBM_STATUS(CLIENT_NOT_FOUND);
24*4882a593Smuzhiyun 	MEI_HBM_STATUS(ALREADY_EXISTS);
25*4882a593Smuzhiyun 	MEI_HBM_STATUS(REJECTED);
26*4882a593Smuzhiyun 	MEI_HBM_STATUS(INVALID_PARAMETER);
27*4882a593Smuzhiyun 	MEI_HBM_STATUS(NOT_ALLOWED);
28*4882a593Smuzhiyun 	MEI_HBM_STATUS(ALREADY_STARTED);
29*4882a593Smuzhiyun 	MEI_HBM_STATUS(NOT_STARTED);
30*4882a593Smuzhiyun 	default: return "unknown";
31*4882a593Smuzhiyun 	}
32*4882a593Smuzhiyun #undef MEI_HBM_STATUS
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun 
mei_cl_conn_status_str(enum mei_cl_connect_status status)35*4882a593Smuzhiyun static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun #define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
38*4882a593Smuzhiyun 	switch (status) {
39*4882a593Smuzhiyun 	MEI_CL_CS(SUCCESS);
40*4882a593Smuzhiyun 	MEI_CL_CS(NOT_FOUND);
41*4882a593Smuzhiyun 	MEI_CL_CS(ALREADY_STARTED);
42*4882a593Smuzhiyun 	MEI_CL_CS(OUT_OF_RESOURCES);
43*4882a593Smuzhiyun 	MEI_CL_CS(MESSAGE_SMALL);
44*4882a593Smuzhiyun 	MEI_CL_CS(NOT_ALLOWED);
45*4882a593Smuzhiyun 	default: return "unknown";
46*4882a593Smuzhiyun 	}
47*4882a593Smuzhiyun #undef MEI_CL_CCS
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun 
mei_hbm_state_str(enum mei_hbm_state state)50*4882a593Smuzhiyun const char *mei_hbm_state_str(enum mei_hbm_state state)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun #define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state
53*4882a593Smuzhiyun 	switch (state) {
54*4882a593Smuzhiyun 	MEI_HBM_STATE(IDLE);
55*4882a593Smuzhiyun 	MEI_HBM_STATE(STARTING);
56*4882a593Smuzhiyun 	MEI_HBM_STATE(STARTED);
57*4882a593Smuzhiyun 	MEI_HBM_STATE(DR_SETUP);
58*4882a593Smuzhiyun 	MEI_HBM_STATE(ENUM_CLIENTS);
59*4882a593Smuzhiyun 	MEI_HBM_STATE(CLIENT_PROPERTIES);
60*4882a593Smuzhiyun 	MEI_HBM_STATE(STOPPED);
61*4882a593Smuzhiyun 	default:
62*4882a593Smuzhiyun 		return "unknown";
63*4882a593Smuzhiyun 	}
64*4882a593Smuzhiyun #undef MEI_HBM_STATE
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun /**
68*4882a593Smuzhiyun  * mei_cl_conn_status_to_errno - convert client connect response
69*4882a593Smuzhiyun  * status to error code
70*4882a593Smuzhiyun  *
71*4882a593Smuzhiyun  * @status: client connect response status
72*4882a593Smuzhiyun  *
73*4882a593Smuzhiyun  * Return: corresponding error code
74*4882a593Smuzhiyun  */
mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)75*4882a593Smuzhiyun static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	switch (status) {
78*4882a593Smuzhiyun 	case MEI_CL_CONN_SUCCESS:          return 0;
79*4882a593Smuzhiyun 	case MEI_CL_CONN_NOT_FOUND:        return -ENOTTY;
80*4882a593Smuzhiyun 	case MEI_CL_CONN_ALREADY_STARTED:  return -EBUSY;
81*4882a593Smuzhiyun 	case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
82*4882a593Smuzhiyun 	case MEI_CL_CONN_MESSAGE_SMALL:    return -EINVAL;
83*4882a593Smuzhiyun 	case MEI_CL_CONN_NOT_ALLOWED:      return -EBUSY;
84*4882a593Smuzhiyun 	default:                           return -EINVAL;
85*4882a593Smuzhiyun 	}
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun /**
89*4882a593Smuzhiyun  * mei_hbm_write_message - wrapper for sending hbm messages.
90*4882a593Smuzhiyun  *
91*4882a593Smuzhiyun  * @dev: mei device
92*4882a593Smuzhiyun  * @hdr: mei header
93*4882a593Smuzhiyun  * @data: payload
94*4882a593Smuzhiyun  */
mei_hbm_write_message(struct mei_device * dev,struct mei_msg_hdr * hdr,const void * data)95*4882a593Smuzhiyun static inline int mei_hbm_write_message(struct mei_device *dev,
96*4882a593Smuzhiyun 					struct mei_msg_hdr *hdr,
97*4882a593Smuzhiyun 					const void *data)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	return mei_write_message(dev, hdr, sizeof(*hdr), data, hdr->length);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun /**
103*4882a593Smuzhiyun  * mei_hbm_idle - set hbm to idle state
104*4882a593Smuzhiyun  *
105*4882a593Smuzhiyun  * @dev: the device structure
106*4882a593Smuzhiyun  */
mei_hbm_idle(struct mei_device * dev)107*4882a593Smuzhiyun void mei_hbm_idle(struct mei_device *dev)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	dev->init_clients_timer = 0;
110*4882a593Smuzhiyun 	dev->hbm_state = MEI_HBM_IDLE;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun /**
114*4882a593Smuzhiyun  * mei_hbm_reset - reset hbm counters and book keeping data structurs
115*4882a593Smuzhiyun  *
116*4882a593Smuzhiyun  * @dev: the device structure
117*4882a593Smuzhiyun  */
mei_hbm_reset(struct mei_device * dev)118*4882a593Smuzhiyun void mei_hbm_reset(struct mei_device *dev)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	mei_me_cl_rm_all(dev);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	mei_hbm_idle(dev);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun /**
126*4882a593Smuzhiyun  * mei_hbm_hdr - construct hbm header
127*4882a593Smuzhiyun  *
128*4882a593Smuzhiyun  * @mei_hdr: hbm header
129*4882a593Smuzhiyun  * @length: payload length
130*4882a593Smuzhiyun  */
131*4882a593Smuzhiyun 
mei_hbm_hdr(struct mei_msg_hdr * mei_hdr,size_t length)132*4882a593Smuzhiyun static inline void mei_hbm_hdr(struct mei_msg_hdr *mei_hdr, size_t length)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun 	memset(mei_hdr, 0, sizeof(*mei_hdr));
135*4882a593Smuzhiyun 	mei_hdr->length = length;
136*4882a593Smuzhiyun 	mei_hdr->msg_complete = 1;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun /**
140*4882a593Smuzhiyun  * mei_hbm_cl_hdr - construct client hbm header
141*4882a593Smuzhiyun  *
142*4882a593Smuzhiyun  * @cl: client
143*4882a593Smuzhiyun  * @hbm_cmd: host bus message command
144*4882a593Smuzhiyun  * @buf: buffer for cl header
145*4882a593Smuzhiyun  * @len: buffer length
146*4882a593Smuzhiyun  */
147*4882a593Smuzhiyun static inline
mei_hbm_cl_hdr(struct mei_cl * cl,u8 hbm_cmd,void * buf,size_t len)148*4882a593Smuzhiyun void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	struct mei_hbm_cl_cmd *cmd = buf;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	memset(cmd, 0, len);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	cmd->hbm_cmd = hbm_cmd;
155*4882a593Smuzhiyun 	cmd->host_addr = mei_cl_host_addr(cl);
156*4882a593Smuzhiyun 	cmd->me_addr = mei_cl_me_id(cl);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /**
160*4882a593Smuzhiyun  * mei_hbm_cl_write - write simple hbm client message
161*4882a593Smuzhiyun  *
162*4882a593Smuzhiyun  * @dev: the device structure
163*4882a593Smuzhiyun  * @cl: client
164*4882a593Smuzhiyun  * @hbm_cmd: host bus message command
165*4882a593Smuzhiyun  * @buf: message buffer
166*4882a593Smuzhiyun  * @len: buffer length
167*4882a593Smuzhiyun  *
168*4882a593Smuzhiyun  * Return: 0 on success, <0 on failure.
169*4882a593Smuzhiyun  */
mei_hbm_cl_write(struct mei_device * dev,struct mei_cl * cl,u8 hbm_cmd,void * buf,size_t len)170*4882a593Smuzhiyun static inline int mei_hbm_cl_write(struct mei_device *dev, struct mei_cl *cl,
171*4882a593Smuzhiyun 				   u8 hbm_cmd, void *buf, size_t len)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, len);
176*4882a593Smuzhiyun 	mei_hbm_cl_hdr(cl, hbm_cmd, buf, len);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	return mei_hbm_write_message(dev, &mei_hdr, buf);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun /**
182*4882a593Smuzhiyun  * mei_hbm_cl_addr_equal - check if the client's and
183*4882a593Smuzhiyun  *	the message address match
184*4882a593Smuzhiyun  *
185*4882a593Smuzhiyun  * @cl: client
186*4882a593Smuzhiyun  * @cmd: hbm client message
187*4882a593Smuzhiyun  *
188*4882a593Smuzhiyun  * Return: true if addresses are the same
189*4882a593Smuzhiyun  */
190*4882a593Smuzhiyun static inline
mei_hbm_cl_addr_equal(struct mei_cl * cl,struct mei_hbm_cl_cmd * cmd)191*4882a593Smuzhiyun bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	return  mei_cl_host_addr(cl) == cmd->host_addr &&
194*4882a593Smuzhiyun 		mei_cl_me_id(cl) == cmd->me_addr;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /**
198*4882a593Smuzhiyun  * mei_hbm_cl_find_by_cmd - find recipient client
199*4882a593Smuzhiyun  *
200*4882a593Smuzhiyun  * @dev: the device structure
201*4882a593Smuzhiyun  * @buf: a buffer with hbm cl command
202*4882a593Smuzhiyun  *
203*4882a593Smuzhiyun  * Return: the recipient client or NULL if not found
204*4882a593Smuzhiyun  */
205*4882a593Smuzhiyun static inline
mei_hbm_cl_find_by_cmd(struct mei_device * dev,void * buf)206*4882a593Smuzhiyun struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf;
209*4882a593Smuzhiyun 	struct mei_cl *cl;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	list_for_each_entry(cl, &dev->file_list, link)
212*4882a593Smuzhiyun 		if (mei_hbm_cl_addr_equal(cl, cmd))
213*4882a593Smuzhiyun 			return cl;
214*4882a593Smuzhiyun 	return NULL;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun /**
219*4882a593Smuzhiyun  * mei_hbm_start_wait - wait for start response message.
220*4882a593Smuzhiyun  *
221*4882a593Smuzhiyun  * @dev: the device structure
222*4882a593Smuzhiyun  *
223*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
224*4882a593Smuzhiyun  */
mei_hbm_start_wait(struct mei_device * dev)225*4882a593Smuzhiyun int mei_hbm_start_wait(struct mei_device *dev)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun 	int ret;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	if (dev->hbm_state > MEI_HBM_STARTING)
230*4882a593Smuzhiyun 		return 0;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	mutex_unlock(&dev->device_lock);
233*4882a593Smuzhiyun 	ret = wait_event_timeout(dev->wait_hbm_start,
234*4882a593Smuzhiyun 			dev->hbm_state != MEI_HBM_STARTING,
235*4882a593Smuzhiyun 			mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
236*4882a593Smuzhiyun 	mutex_lock(&dev->device_lock);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) {
239*4882a593Smuzhiyun 		dev->hbm_state = MEI_HBM_IDLE;
240*4882a593Smuzhiyun 		dev_err(dev->dev, "waiting for mei start failed\n");
241*4882a593Smuzhiyun 		return -ETIME;
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 	return 0;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun /**
247*4882a593Smuzhiyun  * mei_hbm_start_req - sends start request message.
248*4882a593Smuzhiyun  *
249*4882a593Smuzhiyun  * @dev: the device structure
250*4882a593Smuzhiyun  *
251*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
252*4882a593Smuzhiyun  */
mei_hbm_start_req(struct mei_device * dev)253*4882a593Smuzhiyun int mei_hbm_start_req(struct mei_device *dev)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
256*4882a593Smuzhiyun 	struct hbm_host_version_request req;
257*4882a593Smuzhiyun 	int ret;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	mei_hbm_reset(dev);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	/* host start message */
264*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
265*4882a593Smuzhiyun 	req.hbm_cmd = HOST_START_REQ_CMD;
266*4882a593Smuzhiyun 	req.host_version.major_version = HBM_MAJOR_VERSION;
267*4882a593Smuzhiyun 	req.host_version.minor_version = HBM_MINOR_VERSION;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	dev->hbm_state = MEI_HBM_IDLE;
270*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
271*4882a593Smuzhiyun 	if (ret) {
272*4882a593Smuzhiyun 		dev_err(dev->dev, "version message write failed: ret = %d\n",
273*4882a593Smuzhiyun 			ret);
274*4882a593Smuzhiyun 		return ret;
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	dev->hbm_state = MEI_HBM_STARTING;
278*4882a593Smuzhiyun 	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
279*4882a593Smuzhiyun 	mei_schedule_stall_timer(dev);
280*4882a593Smuzhiyun 	return 0;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun /**
284*4882a593Smuzhiyun  * mei_hbm_dma_setup_req() - setup DMA request
285*4882a593Smuzhiyun  * @dev: the device structure
286*4882a593Smuzhiyun  *
287*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
288*4882a593Smuzhiyun  */
mei_hbm_dma_setup_req(struct mei_device * dev)289*4882a593Smuzhiyun static int mei_hbm_dma_setup_req(struct mei_device *dev)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
292*4882a593Smuzhiyun 	struct hbm_dma_setup_request req;
293*4882a593Smuzhiyun 	unsigned int i;
294*4882a593Smuzhiyun 	int ret;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
299*4882a593Smuzhiyun 	req.hbm_cmd = MEI_HBM_DMA_SETUP_REQ_CMD;
300*4882a593Smuzhiyun 	for (i = 0; i < DMA_DSCR_NUM; i++) {
301*4882a593Smuzhiyun 		phys_addr_t paddr;
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 		paddr = dev->dr_dscr[i].daddr;
304*4882a593Smuzhiyun 		req.dma_dscr[i].addr_hi = upper_32_bits(paddr);
305*4882a593Smuzhiyun 		req.dma_dscr[i].addr_lo = lower_32_bits(paddr);
306*4882a593Smuzhiyun 		req.dma_dscr[i].size = dev->dr_dscr[i].size;
307*4882a593Smuzhiyun 	}
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	mei_dma_ring_reset(dev);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
312*4882a593Smuzhiyun 	if (ret) {
313*4882a593Smuzhiyun 		dev_err(dev->dev, "dma setup request write failed: ret = %d.\n",
314*4882a593Smuzhiyun 			ret);
315*4882a593Smuzhiyun 		return ret;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	dev->hbm_state = MEI_HBM_DR_SETUP;
319*4882a593Smuzhiyun 	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
320*4882a593Smuzhiyun 	mei_schedule_stall_timer(dev);
321*4882a593Smuzhiyun 	return 0;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun /**
325*4882a593Smuzhiyun  * mei_hbm_capabilities_req - request capabilities
326*4882a593Smuzhiyun  *
327*4882a593Smuzhiyun  * @dev: the device structure
328*4882a593Smuzhiyun  *
329*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
330*4882a593Smuzhiyun  */
mei_hbm_capabilities_req(struct mei_device * dev)331*4882a593Smuzhiyun static int mei_hbm_capabilities_req(struct mei_device *dev)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
334*4882a593Smuzhiyun 	struct hbm_capability_request req;
335*4882a593Smuzhiyun 	int ret;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
340*4882a593Smuzhiyun 	req.hbm_cmd = MEI_HBM_CAPABILITIES_REQ_CMD;
341*4882a593Smuzhiyun 	if (dev->hbm_f_vt_supported)
342*4882a593Smuzhiyun 		req.capability_requested[0] = HBM_CAP_VT;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
345*4882a593Smuzhiyun 	if (ret) {
346*4882a593Smuzhiyun 		dev_err(dev->dev,
347*4882a593Smuzhiyun 			"capabilities request write failed: ret = %d.\n", ret);
348*4882a593Smuzhiyun 		return ret;
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	dev->hbm_state = MEI_HBM_CAP_SETUP;
352*4882a593Smuzhiyun 	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
353*4882a593Smuzhiyun 	mei_schedule_stall_timer(dev);
354*4882a593Smuzhiyun 	return 0;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun /**
358*4882a593Smuzhiyun  * mei_hbm_enum_clients_req - sends enumeration client request message.
359*4882a593Smuzhiyun  *
360*4882a593Smuzhiyun  * @dev: the device structure
361*4882a593Smuzhiyun  *
362*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
363*4882a593Smuzhiyun  */
mei_hbm_enum_clients_req(struct mei_device * dev)364*4882a593Smuzhiyun static int mei_hbm_enum_clients_req(struct mei_device *dev)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
367*4882a593Smuzhiyun 	struct hbm_host_enum_request req;
368*4882a593Smuzhiyun 	int ret;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	/* enumerate clients */
371*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
374*4882a593Smuzhiyun 	req.hbm_cmd = HOST_ENUM_REQ_CMD;
375*4882a593Smuzhiyun 	req.flags |= dev->hbm_f_dc_supported ? MEI_HBM_ENUM_F_ALLOW_ADD : 0;
376*4882a593Smuzhiyun 	req.flags |= dev->hbm_f_ie_supported ?
377*4882a593Smuzhiyun 			  MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
380*4882a593Smuzhiyun 	if (ret) {
381*4882a593Smuzhiyun 		dev_err(dev->dev, "enumeration request write failed: ret = %d.\n",
382*4882a593Smuzhiyun 			ret);
383*4882a593Smuzhiyun 		return ret;
384*4882a593Smuzhiyun 	}
385*4882a593Smuzhiyun 	dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
386*4882a593Smuzhiyun 	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
387*4882a593Smuzhiyun 	mei_schedule_stall_timer(dev);
388*4882a593Smuzhiyun 	return 0;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun /**
392*4882a593Smuzhiyun  * mei_hbm_me_cl_add - add new me client to the list
393*4882a593Smuzhiyun  *
394*4882a593Smuzhiyun  * @dev: the device structure
395*4882a593Smuzhiyun  * @res: hbm property response
396*4882a593Smuzhiyun  *
397*4882a593Smuzhiyun  * Return: 0 on success and -ENOMEM on allocation failure
398*4882a593Smuzhiyun  */
399*4882a593Smuzhiyun 
mei_hbm_me_cl_add(struct mei_device * dev,struct hbm_props_response * res)400*4882a593Smuzhiyun static int mei_hbm_me_cl_add(struct mei_device *dev,
401*4882a593Smuzhiyun 			     struct hbm_props_response *res)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	struct mei_me_client *me_cl;
404*4882a593Smuzhiyun 	const uuid_le *uuid = &res->client_properties.protocol_name;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	mei_me_cl_rm_by_uuid(dev, uuid);
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	me_cl = kzalloc(sizeof(*me_cl), GFP_KERNEL);
409*4882a593Smuzhiyun 	if (!me_cl)
410*4882a593Smuzhiyun 		return -ENOMEM;
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	mei_me_cl_init(me_cl);
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	me_cl->props = res->client_properties;
415*4882a593Smuzhiyun 	me_cl->client_id = res->me_addr;
416*4882a593Smuzhiyun 	me_cl->tx_flow_ctrl_creds = 0;
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	mei_me_cl_add(dev, me_cl);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	return 0;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun /**
424*4882a593Smuzhiyun  * mei_hbm_add_cl_resp - send response to fw on client add request
425*4882a593Smuzhiyun  *
426*4882a593Smuzhiyun  * @dev: the device structure
427*4882a593Smuzhiyun  * @addr: me address
428*4882a593Smuzhiyun  * @status: response status
429*4882a593Smuzhiyun  *
430*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
431*4882a593Smuzhiyun  */
mei_hbm_add_cl_resp(struct mei_device * dev,u8 addr,u8 status)432*4882a593Smuzhiyun static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
435*4882a593Smuzhiyun 	struct hbm_add_client_response resp;
436*4882a593Smuzhiyun 	int ret;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	dev_dbg(dev->dev, "adding client response\n");
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(resp));
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	memset(&resp, 0, sizeof(resp));
443*4882a593Smuzhiyun 	resp.hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD;
444*4882a593Smuzhiyun 	resp.me_addr = addr;
445*4882a593Smuzhiyun 	resp.status  = status;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &resp);
448*4882a593Smuzhiyun 	if (ret)
449*4882a593Smuzhiyun 		dev_err(dev->dev, "add client response write failed: ret = %d\n",
450*4882a593Smuzhiyun 			ret);
451*4882a593Smuzhiyun 	return ret;
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun /**
455*4882a593Smuzhiyun  * mei_hbm_fw_add_cl_req - request from the fw to add a client
456*4882a593Smuzhiyun  *
457*4882a593Smuzhiyun  * @dev: the device structure
458*4882a593Smuzhiyun  * @req: add client request
459*4882a593Smuzhiyun  *
460*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
461*4882a593Smuzhiyun  */
mei_hbm_fw_add_cl_req(struct mei_device * dev,struct hbm_add_client_request * req)462*4882a593Smuzhiyun static int mei_hbm_fw_add_cl_req(struct mei_device *dev,
463*4882a593Smuzhiyun 			      struct hbm_add_client_request *req)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun 	int ret;
466*4882a593Smuzhiyun 	u8 status = MEI_HBMS_SUCCESS;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	BUILD_BUG_ON(sizeof(struct hbm_add_client_request) !=
469*4882a593Smuzhiyun 			sizeof(struct hbm_props_response));
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	ret = mei_hbm_me_cl_add(dev, (struct hbm_props_response *)req);
472*4882a593Smuzhiyun 	if (ret)
473*4882a593Smuzhiyun 		status = !MEI_HBMS_SUCCESS;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	if (dev->dev_state == MEI_DEV_ENABLED)
476*4882a593Smuzhiyun 		schedule_work(&dev->bus_rescan_work);
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	return mei_hbm_add_cl_resp(dev, req->me_addr, status);
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun /**
482*4882a593Smuzhiyun  * mei_hbm_cl_notify_req - send notification request
483*4882a593Smuzhiyun  *
484*4882a593Smuzhiyun  * @dev: the device structure
485*4882a593Smuzhiyun  * @cl: a client to disconnect from
486*4882a593Smuzhiyun  * @start: true for start false for stop
487*4882a593Smuzhiyun  *
488*4882a593Smuzhiyun  * Return: 0 on success and -EIO on write failure
489*4882a593Smuzhiyun  */
mei_hbm_cl_notify_req(struct mei_device * dev,struct mei_cl * cl,u8 start)490*4882a593Smuzhiyun int mei_hbm_cl_notify_req(struct mei_device *dev,
491*4882a593Smuzhiyun 			  struct mei_cl *cl, u8 start)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
495*4882a593Smuzhiyun 	struct hbm_notification_request req;
496*4882a593Smuzhiyun 	int ret;
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
499*4882a593Smuzhiyun 	mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, &req, sizeof(req));
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	req.start = start;
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
504*4882a593Smuzhiyun 	if (ret)
505*4882a593Smuzhiyun 		dev_err(dev->dev, "notify request failed: ret = %d\n", ret);
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	return ret;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun /**
511*4882a593Smuzhiyun  *  notify_res_to_fop - convert notification response to the proper
512*4882a593Smuzhiyun  *      notification FOP
513*4882a593Smuzhiyun  *
514*4882a593Smuzhiyun  * @cmd: client notification start response command
515*4882a593Smuzhiyun  *
516*4882a593Smuzhiyun  * Return:  MEI_FOP_NOTIFY_START or MEI_FOP_NOTIFY_STOP;
517*4882a593Smuzhiyun  */
notify_res_to_fop(struct mei_hbm_cl_cmd * cmd)518*4882a593Smuzhiyun static inline enum mei_cb_file_ops notify_res_to_fop(struct mei_hbm_cl_cmd *cmd)
519*4882a593Smuzhiyun {
520*4882a593Smuzhiyun 	struct hbm_notification_response *rs =
521*4882a593Smuzhiyun 		(struct hbm_notification_response *)cmd;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	return mei_cl_notify_req2fop(rs->start);
524*4882a593Smuzhiyun }
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun /**
527*4882a593Smuzhiyun  * mei_hbm_cl_notify_start_res - update the client state according
528*4882a593Smuzhiyun  *       notify start response
529*4882a593Smuzhiyun  *
530*4882a593Smuzhiyun  * @dev: the device structure
531*4882a593Smuzhiyun  * @cl: mei host client
532*4882a593Smuzhiyun  * @cmd: client notification start response command
533*4882a593Smuzhiyun  */
mei_hbm_cl_notify_start_res(struct mei_device * dev,struct mei_cl * cl,struct mei_hbm_cl_cmd * cmd)534*4882a593Smuzhiyun static void mei_hbm_cl_notify_start_res(struct mei_device *dev,
535*4882a593Smuzhiyun 					struct mei_cl *cl,
536*4882a593Smuzhiyun 					struct mei_hbm_cl_cmd *cmd)
537*4882a593Smuzhiyun {
538*4882a593Smuzhiyun 	struct hbm_notification_response *rs =
539*4882a593Smuzhiyun 		(struct hbm_notification_response *)cmd;
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	cl_dbg(dev, cl, "hbm: notify start response status=%d\n", rs->status);
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	if (rs->status == MEI_HBMS_SUCCESS ||
544*4882a593Smuzhiyun 	    rs->status == MEI_HBMS_ALREADY_STARTED) {
545*4882a593Smuzhiyun 		cl->notify_en = true;
546*4882a593Smuzhiyun 		cl->status = 0;
547*4882a593Smuzhiyun 	} else {
548*4882a593Smuzhiyun 		cl->status = -EINVAL;
549*4882a593Smuzhiyun 	}
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun /**
553*4882a593Smuzhiyun  * mei_hbm_cl_notify_stop_res - update the client state according
554*4882a593Smuzhiyun  *       notify stop response
555*4882a593Smuzhiyun  *
556*4882a593Smuzhiyun  * @dev: the device structure
557*4882a593Smuzhiyun  * @cl: mei host client
558*4882a593Smuzhiyun  * @cmd: client notification stop response command
559*4882a593Smuzhiyun  */
mei_hbm_cl_notify_stop_res(struct mei_device * dev,struct mei_cl * cl,struct mei_hbm_cl_cmd * cmd)560*4882a593Smuzhiyun static void mei_hbm_cl_notify_stop_res(struct mei_device *dev,
561*4882a593Smuzhiyun 				       struct mei_cl *cl,
562*4882a593Smuzhiyun 				       struct mei_hbm_cl_cmd *cmd)
563*4882a593Smuzhiyun {
564*4882a593Smuzhiyun 	struct hbm_notification_response *rs =
565*4882a593Smuzhiyun 		(struct hbm_notification_response *)cmd;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	cl_dbg(dev, cl, "hbm: notify stop response status=%d\n", rs->status);
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	if (rs->status == MEI_HBMS_SUCCESS ||
570*4882a593Smuzhiyun 	    rs->status == MEI_HBMS_NOT_STARTED) {
571*4882a593Smuzhiyun 		cl->notify_en = false;
572*4882a593Smuzhiyun 		cl->status = 0;
573*4882a593Smuzhiyun 	} else {
574*4882a593Smuzhiyun 		/* TODO: spec is not clear yet about other possible issues */
575*4882a593Smuzhiyun 		cl->status = -EINVAL;
576*4882a593Smuzhiyun 	}
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun /**
580*4882a593Smuzhiyun  * mei_hbm_cl_notify - signal notification event
581*4882a593Smuzhiyun  *
582*4882a593Smuzhiyun  * @dev: the device structure
583*4882a593Smuzhiyun  * @cmd: notification client message
584*4882a593Smuzhiyun  */
mei_hbm_cl_notify(struct mei_device * dev,struct mei_hbm_cl_cmd * cmd)585*4882a593Smuzhiyun static void mei_hbm_cl_notify(struct mei_device *dev,
586*4882a593Smuzhiyun 			      struct mei_hbm_cl_cmd *cmd)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun 	struct mei_cl *cl;
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	cl = mei_hbm_cl_find_by_cmd(dev, cmd);
591*4882a593Smuzhiyun 	if (cl)
592*4882a593Smuzhiyun 		mei_cl_notify(cl);
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun /**
596*4882a593Smuzhiyun  * mei_hbm_prop_req - request property for a single client
597*4882a593Smuzhiyun  *
598*4882a593Smuzhiyun  * @dev: the device structure
599*4882a593Smuzhiyun  * @start_idx: client index to start search
600*4882a593Smuzhiyun  *
601*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
602*4882a593Smuzhiyun  */
mei_hbm_prop_req(struct mei_device * dev,unsigned long start_idx)603*4882a593Smuzhiyun static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
606*4882a593Smuzhiyun 	struct hbm_props_request req;
607*4882a593Smuzhiyun 	unsigned long addr;
608*4882a593Smuzhiyun 	int ret;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	addr = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, start_idx);
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	/* We got all client properties */
613*4882a593Smuzhiyun 	if (addr == MEI_CLIENTS_MAX) {
614*4882a593Smuzhiyun 		dev->hbm_state = MEI_HBM_STARTED;
615*4882a593Smuzhiyun 		mei_host_client_init(dev);
616*4882a593Smuzhiyun 		return 0;
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
624*4882a593Smuzhiyun 	req.me_addr = addr;
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
627*4882a593Smuzhiyun 	if (ret) {
628*4882a593Smuzhiyun 		dev_err(dev->dev, "properties request write failed: ret = %d\n",
629*4882a593Smuzhiyun 			ret);
630*4882a593Smuzhiyun 		return ret;
631*4882a593Smuzhiyun 	}
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
634*4882a593Smuzhiyun 	mei_schedule_stall_timer(dev);
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	return 0;
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun /**
640*4882a593Smuzhiyun  * mei_hbm_pg - sends pg command
641*4882a593Smuzhiyun  *
642*4882a593Smuzhiyun  * @dev: the device structure
643*4882a593Smuzhiyun  * @pg_cmd: the pg command code
644*4882a593Smuzhiyun  *
645*4882a593Smuzhiyun  * Return: -EIO on write failure
646*4882a593Smuzhiyun  *         -EOPNOTSUPP if the operation is not supported by the protocol
647*4882a593Smuzhiyun  */
mei_hbm_pg(struct mei_device * dev,u8 pg_cmd)648*4882a593Smuzhiyun int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
649*4882a593Smuzhiyun {
650*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
651*4882a593Smuzhiyun 	struct hbm_power_gate req;
652*4882a593Smuzhiyun 	int ret;
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	if (!dev->hbm_f_pg_supported)
655*4882a593Smuzhiyun 		return -EOPNOTSUPP;
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
660*4882a593Smuzhiyun 	req.hbm_cmd = pg_cmd;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	ret = mei_hbm_write_message(dev, &mei_hdr, &req);
663*4882a593Smuzhiyun 	if (ret)
664*4882a593Smuzhiyun 		dev_err(dev->dev, "power gate command write failed.\n");
665*4882a593Smuzhiyun 	return ret;
666*4882a593Smuzhiyun }
667*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mei_hbm_pg);
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun /**
670*4882a593Smuzhiyun  * mei_hbm_stop_req - send stop request message
671*4882a593Smuzhiyun  *
672*4882a593Smuzhiyun  * @dev: mei device
673*4882a593Smuzhiyun  *
674*4882a593Smuzhiyun  * Return: -EIO on write failure
675*4882a593Smuzhiyun  */
mei_hbm_stop_req(struct mei_device * dev)676*4882a593Smuzhiyun static int mei_hbm_stop_req(struct mei_device *dev)
677*4882a593Smuzhiyun {
678*4882a593Smuzhiyun 	struct mei_msg_hdr mei_hdr;
679*4882a593Smuzhiyun 	struct hbm_host_stop_request req;
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 	mei_hbm_hdr(&mei_hdr, sizeof(req));
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
684*4882a593Smuzhiyun 	req.hbm_cmd = HOST_STOP_REQ_CMD;
685*4882a593Smuzhiyun 	req.reason = DRIVER_STOP_REQUEST;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 	return mei_hbm_write_message(dev, &mei_hdr, &req);
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun /**
691*4882a593Smuzhiyun  * mei_hbm_cl_flow_control_req - sends flow control request.
692*4882a593Smuzhiyun  *
693*4882a593Smuzhiyun  * @dev: the device structure
694*4882a593Smuzhiyun  * @cl: client info
695*4882a593Smuzhiyun  *
696*4882a593Smuzhiyun  * Return: -EIO on write failure
697*4882a593Smuzhiyun  */
mei_hbm_cl_flow_control_req(struct mei_device * dev,struct mei_cl * cl)698*4882a593Smuzhiyun int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
699*4882a593Smuzhiyun {
700*4882a593Smuzhiyun 	struct hbm_flow_control req;
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	cl_dbg(dev, cl, "sending flow control\n");
703*4882a593Smuzhiyun 	return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD,
704*4882a593Smuzhiyun 				&req, sizeof(req));
705*4882a593Smuzhiyun }
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun /**
708*4882a593Smuzhiyun  * mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials.
709*4882a593Smuzhiyun  *
710*4882a593Smuzhiyun  * @dev: the device structure
711*4882a593Smuzhiyun  * @fctrl: flow control response bus message
712*4882a593Smuzhiyun  *
713*4882a593Smuzhiyun  * Return: 0 on success, < 0 otherwise
714*4882a593Smuzhiyun  */
mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device * dev,struct hbm_flow_control * fctrl)715*4882a593Smuzhiyun static int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev,
716*4882a593Smuzhiyun 						 struct hbm_flow_control *fctrl)
717*4882a593Smuzhiyun {
718*4882a593Smuzhiyun 	struct mei_me_client *me_cl;
719*4882a593Smuzhiyun 	int rets;
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	me_cl = mei_me_cl_by_id(dev, fctrl->me_addr);
722*4882a593Smuzhiyun 	if (!me_cl) {
723*4882a593Smuzhiyun 		dev_err(dev->dev, "no such me client %d\n", fctrl->me_addr);
724*4882a593Smuzhiyun 		return -ENOENT;
725*4882a593Smuzhiyun 	}
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 	if (WARN_ON(me_cl->props.single_recv_buf == 0)) {
728*4882a593Smuzhiyun 		rets = -EINVAL;
729*4882a593Smuzhiyun 		goto out;
730*4882a593Smuzhiyun 	}
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	me_cl->tx_flow_ctrl_creds++;
733*4882a593Smuzhiyun 	dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
734*4882a593Smuzhiyun 		fctrl->me_addr, me_cl->tx_flow_ctrl_creds);
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	rets = 0;
737*4882a593Smuzhiyun out:
738*4882a593Smuzhiyun 	mei_me_cl_put(me_cl);
739*4882a593Smuzhiyun 	return rets;
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun /**
743*4882a593Smuzhiyun  * mei_hbm_cl_flow_control_res - flow control response from me
744*4882a593Smuzhiyun  *
745*4882a593Smuzhiyun  * @dev: the device structure
746*4882a593Smuzhiyun  * @fctrl: flow control response bus message
747*4882a593Smuzhiyun  */
mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device * dev,struct hbm_flow_control * fctrl)748*4882a593Smuzhiyun static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev,
749*4882a593Smuzhiyun 					       struct hbm_flow_control *fctrl)
750*4882a593Smuzhiyun {
751*4882a593Smuzhiyun 	struct mei_cl *cl;
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	if (!fctrl->host_addr) {
754*4882a593Smuzhiyun 		/* single receive buffer */
755*4882a593Smuzhiyun 		mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl);
756*4882a593Smuzhiyun 		return;
757*4882a593Smuzhiyun 	}
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	cl = mei_hbm_cl_find_by_cmd(dev, fctrl);
760*4882a593Smuzhiyun 	if (cl) {
761*4882a593Smuzhiyun 		cl->tx_flow_ctrl_creds++;
762*4882a593Smuzhiyun 		cl_dbg(dev, cl, "flow control creds = %d.\n",
763*4882a593Smuzhiyun 				cl->tx_flow_ctrl_creds);
764*4882a593Smuzhiyun 	}
765*4882a593Smuzhiyun }
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun /**
769*4882a593Smuzhiyun  * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
770*4882a593Smuzhiyun  *
771*4882a593Smuzhiyun  * @dev: the device structure
772*4882a593Smuzhiyun  * @cl: a client to disconnect from
773*4882a593Smuzhiyun  *
774*4882a593Smuzhiyun  * Return: -EIO on write failure
775*4882a593Smuzhiyun  */
mei_hbm_cl_disconnect_req(struct mei_device * dev,struct mei_cl * cl)776*4882a593Smuzhiyun int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
777*4882a593Smuzhiyun {
778*4882a593Smuzhiyun 	struct hbm_client_connect_request req;
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD,
781*4882a593Smuzhiyun 				&req, sizeof(req));
782*4882a593Smuzhiyun }
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun /**
785*4882a593Smuzhiyun  * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
786*4882a593Smuzhiyun  *
787*4882a593Smuzhiyun  * @dev: the device structure
788*4882a593Smuzhiyun  * @cl: a client to disconnect from
789*4882a593Smuzhiyun  *
790*4882a593Smuzhiyun  * Return: -EIO on write failure
791*4882a593Smuzhiyun  */
mei_hbm_cl_disconnect_rsp(struct mei_device * dev,struct mei_cl * cl)792*4882a593Smuzhiyun int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
793*4882a593Smuzhiyun {
794*4882a593Smuzhiyun 	struct hbm_client_connect_response resp;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD,
797*4882a593Smuzhiyun 				&resp, sizeof(resp));
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun /**
801*4882a593Smuzhiyun  * mei_hbm_cl_disconnect_res - update the client state according
802*4882a593Smuzhiyun  *       disconnect response
803*4882a593Smuzhiyun  *
804*4882a593Smuzhiyun  * @dev: the device structure
805*4882a593Smuzhiyun  * @cl: mei host client
806*4882a593Smuzhiyun  * @cmd: disconnect client response host bus message
807*4882a593Smuzhiyun  */
mei_hbm_cl_disconnect_res(struct mei_device * dev,struct mei_cl * cl,struct mei_hbm_cl_cmd * cmd)808*4882a593Smuzhiyun static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl,
809*4882a593Smuzhiyun 				      struct mei_hbm_cl_cmd *cmd)
810*4882a593Smuzhiyun {
811*4882a593Smuzhiyun 	struct hbm_client_connect_response *rs =
812*4882a593Smuzhiyun 		(struct hbm_client_connect_response *)cmd;
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status);
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun 	if (rs->status == MEI_CL_DISCONN_SUCCESS)
817*4882a593Smuzhiyun 		cl->state = MEI_FILE_DISCONNECT_REPLY;
818*4882a593Smuzhiyun 	cl->status = 0;
819*4882a593Smuzhiyun }
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun /**
822*4882a593Smuzhiyun  * mei_hbm_cl_connect_req - send connection request to specific me client
823*4882a593Smuzhiyun  *
824*4882a593Smuzhiyun  * @dev: the device structure
825*4882a593Smuzhiyun  * @cl: a client to connect to
826*4882a593Smuzhiyun  *
827*4882a593Smuzhiyun  * Return: -EIO on write failure
828*4882a593Smuzhiyun  */
mei_hbm_cl_connect_req(struct mei_device * dev,struct mei_cl * cl)829*4882a593Smuzhiyun int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
830*4882a593Smuzhiyun {
831*4882a593Smuzhiyun 	struct hbm_client_connect_request req;
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD,
834*4882a593Smuzhiyun 				&req, sizeof(req));
835*4882a593Smuzhiyun }
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun /**
838*4882a593Smuzhiyun  * mei_hbm_cl_connect_res - update the client state according
839*4882a593Smuzhiyun  *        connection response
840*4882a593Smuzhiyun  *
841*4882a593Smuzhiyun  * @dev: the device structure
842*4882a593Smuzhiyun  * @cl: mei host client
843*4882a593Smuzhiyun  * @cmd: connect client response host bus message
844*4882a593Smuzhiyun  */
mei_hbm_cl_connect_res(struct mei_device * dev,struct mei_cl * cl,struct mei_hbm_cl_cmd * cmd)845*4882a593Smuzhiyun static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
846*4882a593Smuzhiyun 				   struct mei_hbm_cl_cmd *cmd)
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun 	struct hbm_client_connect_response *rs =
849*4882a593Smuzhiyun 		(struct hbm_client_connect_response *)cmd;
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	cl_dbg(dev, cl, "hbm: connect response status=%s\n",
852*4882a593Smuzhiyun 			mei_cl_conn_status_str(rs->status));
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	if (rs->status == MEI_CL_CONN_SUCCESS)
855*4882a593Smuzhiyun 		cl->state = MEI_FILE_CONNECTED;
856*4882a593Smuzhiyun 	else {
857*4882a593Smuzhiyun 		cl->state = MEI_FILE_DISCONNECT_REPLY;
858*4882a593Smuzhiyun 		if (rs->status == MEI_CL_CONN_NOT_FOUND) {
859*4882a593Smuzhiyun 			mei_me_cl_del(dev, cl->me_cl);
860*4882a593Smuzhiyun 			if (dev->dev_state == MEI_DEV_ENABLED)
861*4882a593Smuzhiyun 				schedule_work(&dev->bus_rescan_work);
862*4882a593Smuzhiyun 		}
863*4882a593Smuzhiyun 	}
864*4882a593Smuzhiyun 	cl->status = mei_cl_conn_status_to_errno(rs->status);
865*4882a593Smuzhiyun }
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun /**
868*4882a593Smuzhiyun  * mei_hbm_cl_res - process hbm response received on behalf
869*4882a593Smuzhiyun  *         an client
870*4882a593Smuzhiyun  *
871*4882a593Smuzhiyun  * @dev: the device structure
872*4882a593Smuzhiyun  * @rs:  hbm client message
873*4882a593Smuzhiyun  * @fop_type: file operation type
874*4882a593Smuzhiyun  */
mei_hbm_cl_res(struct mei_device * dev,struct mei_hbm_cl_cmd * rs,enum mei_cb_file_ops fop_type)875*4882a593Smuzhiyun static void mei_hbm_cl_res(struct mei_device *dev,
876*4882a593Smuzhiyun 			   struct mei_hbm_cl_cmd *rs,
877*4882a593Smuzhiyun 			   enum mei_cb_file_ops fop_type)
878*4882a593Smuzhiyun {
879*4882a593Smuzhiyun 	struct mei_cl *cl;
880*4882a593Smuzhiyun 	struct mei_cl_cb *cb, *next;
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	cl = NULL;
883*4882a593Smuzhiyun 	list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) {
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun 		cl = cb->cl;
886*4882a593Smuzhiyun 
887*4882a593Smuzhiyun 		if (cb->fop_type != fop_type)
888*4882a593Smuzhiyun 			continue;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 		if (mei_hbm_cl_addr_equal(cl, rs)) {
891*4882a593Smuzhiyun 			list_del_init(&cb->list);
892*4882a593Smuzhiyun 			break;
893*4882a593Smuzhiyun 		}
894*4882a593Smuzhiyun 	}
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	if (!cl)
897*4882a593Smuzhiyun 		return;
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	switch (fop_type) {
900*4882a593Smuzhiyun 	case MEI_FOP_CONNECT:
901*4882a593Smuzhiyun 		mei_hbm_cl_connect_res(dev, cl, rs);
902*4882a593Smuzhiyun 		break;
903*4882a593Smuzhiyun 	case MEI_FOP_DISCONNECT:
904*4882a593Smuzhiyun 		mei_hbm_cl_disconnect_res(dev, cl, rs);
905*4882a593Smuzhiyun 		break;
906*4882a593Smuzhiyun 	case MEI_FOP_NOTIFY_START:
907*4882a593Smuzhiyun 		mei_hbm_cl_notify_start_res(dev, cl, rs);
908*4882a593Smuzhiyun 		break;
909*4882a593Smuzhiyun 	case MEI_FOP_NOTIFY_STOP:
910*4882a593Smuzhiyun 		mei_hbm_cl_notify_stop_res(dev, cl, rs);
911*4882a593Smuzhiyun 		break;
912*4882a593Smuzhiyun 	default:
913*4882a593Smuzhiyun 		return;
914*4882a593Smuzhiyun 	}
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	cl->timer_count = 0;
917*4882a593Smuzhiyun 	wake_up(&cl->wait);
918*4882a593Smuzhiyun }
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun /**
922*4882a593Smuzhiyun  * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
923*4882a593Smuzhiyun  *  host sends disconnect response
924*4882a593Smuzhiyun  *
925*4882a593Smuzhiyun  * @dev: the device structure.
926*4882a593Smuzhiyun  * @disconnect_req: disconnect request bus message from the me
927*4882a593Smuzhiyun  *
928*4882a593Smuzhiyun  * Return: -ENOMEM on allocation failure
929*4882a593Smuzhiyun  */
mei_hbm_fw_disconnect_req(struct mei_device * dev,struct hbm_client_connect_request * disconnect_req)930*4882a593Smuzhiyun static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
931*4882a593Smuzhiyun 		struct hbm_client_connect_request *disconnect_req)
932*4882a593Smuzhiyun {
933*4882a593Smuzhiyun 	struct mei_cl *cl;
934*4882a593Smuzhiyun 	struct mei_cl_cb *cb;
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 	cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
937*4882a593Smuzhiyun 	if (cl) {
938*4882a593Smuzhiyun 		cl_warn(dev, cl, "fw disconnect request received\n");
939*4882a593Smuzhiyun 		cl->state = MEI_FILE_DISCONNECTING;
940*4882a593Smuzhiyun 		cl->timer_count = 0;
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun 		cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT_RSP,
943*4882a593Smuzhiyun 					       NULL);
944*4882a593Smuzhiyun 		if (!cb)
945*4882a593Smuzhiyun 			return -ENOMEM;
946*4882a593Smuzhiyun 	}
947*4882a593Smuzhiyun 	return 0;
948*4882a593Smuzhiyun }
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun /**
951*4882a593Smuzhiyun  * mei_hbm_pg_enter_res - PG enter response received
952*4882a593Smuzhiyun  *
953*4882a593Smuzhiyun  * @dev: the device structure.
954*4882a593Smuzhiyun  *
955*4882a593Smuzhiyun  * Return: 0 on success, -EPROTO on state mismatch
956*4882a593Smuzhiyun  */
mei_hbm_pg_enter_res(struct mei_device * dev)957*4882a593Smuzhiyun static int mei_hbm_pg_enter_res(struct mei_device *dev)
958*4882a593Smuzhiyun {
959*4882a593Smuzhiyun 	if (mei_pg_state(dev) != MEI_PG_OFF ||
960*4882a593Smuzhiyun 	    dev->pg_event != MEI_PG_EVENT_WAIT) {
961*4882a593Smuzhiyun 		dev_err(dev->dev, "hbm: pg entry response: state mismatch [%s, %d]\n",
962*4882a593Smuzhiyun 			mei_pg_state_str(mei_pg_state(dev)), dev->pg_event);
963*4882a593Smuzhiyun 		return -EPROTO;
964*4882a593Smuzhiyun 	}
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun 	dev->pg_event = MEI_PG_EVENT_RECEIVED;
967*4882a593Smuzhiyun 	wake_up(&dev->wait_pg);
968*4882a593Smuzhiyun 
969*4882a593Smuzhiyun 	return 0;
970*4882a593Smuzhiyun }
971*4882a593Smuzhiyun 
972*4882a593Smuzhiyun /**
973*4882a593Smuzhiyun  * mei_hbm_pg_resume - process with PG resume
974*4882a593Smuzhiyun  *
975*4882a593Smuzhiyun  * @dev: the device structure.
976*4882a593Smuzhiyun  */
mei_hbm_pg_resume(struct mei_device * dev)977*4882a593Smuzhiyun void mei_hbm_pg_resume(struct mei_device *dev)
978*4882a593Smuzhiyun {
979*4882a593Smuzhiyun 	pm_request_resume(dev->dev);
980*4882a593Smuzhiyun }
981*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mei_hbm_pg_resume);
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun /**
984*4882a593Smuzhiyun  * mei_hbm_pg_exit_res - PG exit response received
985*4882a593Smuzhiyun  *
986*4882a593Smuzhiyun  * @dev: the device structure.
987*4882a593Smuzhiyun  *
988*4882a593Smuzhiyun  * Return: 0 on success, -EPROTO on state mismatch
989*4882a593Smuzhiyun  */
mei_hbm_pg_exit_res(struct mei_device * dev)990*4882a593Smuzhiyun static int mei_hbm_pg_exit_res(struct mei_device *dev)
991*4882a593Smuzhiyun {
992*4882a593Smuzhiyun 	if (mei_pg_state(dev) != MEI_PG_ON ||
993*4882a593Smuzhiyun 	    (dev->pg_event != MEI_PG_EVENT_WAIT &&
994*4882a593Smuzhiyun 	     dev->pg_event != MEI_PG_EVENT_IDLE)) {
995*4882a593Smuzhiyun 		dev_err(dev->dev, "hbm: pg exit response: state mismatch [%s, %d]\n",
996*4882a593Smuzhiyun 			mei_pg_state_str(mei_pg_state(dev)), dev->pg_event);
997*4882a593Smuzhiyun 		return -EPROTO;
998*4882a593Smuzhiyun 	}
999*4882a593Smuzhiyun 
1000*4882a593Smuzhiyun 	switch (dev->pg_event) {
1001*4882a593Smuzhiyun 	case MEI_PG_EVENT_WAIT:
1002*4882a593Smuzhiyun 		dev->pg_event = MEI_PG_EVENT_RECEIVED;
1003*4882a593Smuzhiyun 		wake_up(&dev->wait_pg);
1004*4882a593Smuzhiyun 		break;
1005*4882a593Smuzhiyun 	case MEI_PG_EVENT_IDLE:
1006*4882a593Smuzhiyun 		/*
1007*4882a593Smuzhiyun 		* If the driver is not waiting on this then
1008*4882a593Smuzhiyun 		* this is HW initiated exit from PG.
1009*4882a593Smuzhiyun 		* Start runtime pm resume sequence to exit from PG.
1010*4882a593Smuzhiyun 		*/
1011*4882a593Smuzhiyun 		dev->pg_event = MEI_PG_EVENT_RECEIVED;
1012*4882a593Smuzhiyun 		mei_hbm_pg_resume(dev);
1013*4882a593Smuzhiyun 		break;
1014*4882a593Smuzhiyun 	default:
1015*4882a593Smuzhiyun 		WARN(1, "hbm: pg exit response: unexpected pg event = %d\n",
1016*4882a593Smuzhiyun 		     dev->pg_event);
1017*4882a593Smuzhiyun 		return -EPROTO;
1018*4882a593Smuzhiyun 	}
1019*4882a593Smuzhiyun 
1020*4882a593Smuzhiyun 	return 0;
1021*4882a593Smuzhiyun }
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun /**
1024*4882a593Smuzhiyun  * mei_hbm_config_features - check what hbm features and commands
1025*4882a593Smuzhiyun  *        are supported by the fw
1026*4882a593Smuzhiyun  *
1027*4882a593Smuzhiyun  * @dev: the device structure
1028*4882a593Smuzhiyun  */
mei_hbm_config_features(struct mei_device * dev)1029*4882a593Smuzhiyun static void mei_hbm_config_features(struct mei_device *dev)
1030*4882a593Smuzhiyun {
1031*4882a593Smuzhiyun 	/* Power Gating Isolation Support */
1032*4882a593Smuzhiyun 	dev->hbm_f_pg_supported = 0;
1033*4882a593Smuzhiyun 	if (dev->version.major_version > HBM_MAJOR_VERSION_PGI)
1034*4882a593Smuzhiyun 		dev->hbm_f_pg_supported = 1;
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
1037*4882a593Smuzhiyun 	    dev->version.minor_version >= HBM_MINOR_VERSION_PGI)
1038*4882a593Smuzhiyun 		dev->hbm_f_pg_supported = 1;
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun 	dev->hbm_f_dc_supported = 0;
1041*4882a593Smuzhiyun 	if (dev->version.major_version >= HBM_MAJOR_VERSION_DC)
1042*4882a593Smuzhiyun 		dev->hbm_f_dc_supported = 1;
1043*4882a593Smuzhiyun 
1044*4882a593Smuzhiyun 	dev->hbm_f_ie_supported = 0;
1045*4882a593Smuzhiyun 	if (dev->version.major_version >= HBM_MAJOR_VERSION_IE)
1046*4882a593Smuzhiyun 		dev->hbm_f_ie_supported = 1;
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun 	/* disconnect on connect timeout instead of link reset */
1049*4882a593Smuzhiyun 	dev->hbm_f_dot_supported = 0;
1050*4882a593Smuzhiyun 	if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT)
1051*4882a593Smuzhiyun 		dev->hbm_f_dot_supported = 1;
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun 	/* Notification Event Support */
1054*4882a593Smuzhiyun 	dev->hbm_f_ev_supported = 0;
1055*4882a593Smuzhiyun 	if (dev->version.major_version >= HBM_MAJOR_VERSION_EV)
1056*4882a593Smuzhiyun 		dev->hbm_f_ev_supported = 1;
1057*4882a593Smuzhiyun 
1058*4882a593Smuzhiyun 	/* Fixed Address Client Support */
1059*4882a593Smuzhiyun 	dev->hbm_f_fa_supported = 0;
1060*4882a593Smuzhiyun 	if (dev->version.major_version >= HBM_MAJOR_VERSION_FA)
1061*4882a593Smuzhiyun 		dev->hbm_f_fa_supported = 1;
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun 	/* OS ver message Support */
1064*4882a593Smuzhiyun 	dev->hbm_f_os_supported = 0;
1065*4882a593Smuzhiyun 	if (dev->version.major_version >= HBM_MAJOR_VERSION_OS)
1066*4882a593Smuzhiyun 		dev->hbm_f_os_supported = 1;
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun 	/* DMA Ring Support */
1069*4882a593Smuzhiyun 	dev->hbm_f_dr_supported = 0;
1070*4882a593Smuzhiyun 	if (dev->version.major_version > HBM_MAJOR_VERSION_DR ||
1071*4882a593Smuzhiyun 	    (dev->version.major_version == HBM_MAJOR_VERSION_DR &&
1072*4882a593Smuzhiyun 	     dev->version.minor_version >= HBM_MINOR_VERSION_DR))
1073*4882a593Smuzhiyun 		dev->hbm_f_dr_supported = 1;
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun 	/* VTag Support */
1076*4882a593Smuzhiyun 	dev->hbm_f_vt_supported = 0;
1077*4882a593Smuzhiyun 	if (dev->version.major_version > HBM_MAJOR_VERSION_VT ||
1078*4882a593Smuzhiyun 	    (dev->version.major_version == HBM_MAJOR_VERSION_VT &&
1079*4882a593Smuzhiyun 	     dev->version.minor_version >= HBM_MINOR_VERSION_VT))
1080*4882a593Smuzhiyun 		dev->hbm_f_vt_supported = 1;
1081*4882a593Smuzhiyun 
1082*4882a593Smuzhiyun 	/* Capability message Support */
1083*4882a593Smuzhiyun 	dev->hbm_f_cap_supported = 0;
1084*4882a593Smuzhiyun 	if (dev->version.major_version > HBM_MAJOR_VERSION_CAP ||
1085*4882a593Smuzhiyun 	    (dev->version.major_version == HBM_MAJOR_VERSION_CAP &&
1086*4882a593Smuzhiyun 	     dev->version.minor_version >= HBM_MINOR_VERSION_CAP))
1087*4882a593Smuzhiyun 		dev->hbm_f_cap_supported = 1;
1088*4882a593Smuzhiyun }
1089*4882a593Smuzhiyun 
1090*4882a593Smuzhiyun /**
1091*4882a593Smuzhiyun  * mei_hbm_version_is_supported - checks whether the driver can
1092*4882a593Smuzhiyun  *     support the hbm version of the device
1093*4882a593Smuzhiyun  *
1094*4882a593Smuzhiyun  * @dev: the device structure
1095*4882a593Smuzhiyun  * Return: true if driver can support hbm version of the device
1096*4882a593Smuzhiyun  */
mei_hbm_version_is_supported(struct mei_device * dev)1097*4882a593Smuzhiyun bool mei_hbm_version_is_supported(struct mei_device *dev)
1098*4882a593Smuzhiyun {
1099*4882a593Smuzhiyun 	return	(dev->version.major_version < HBM_MAJOR_VERSION) ||
1100*4882a593Smuzhiyun 		(dev->version.major_version == HBM_MAJOR_VERSION &&
1101*4882a593Smuzhiyun 		 dev->version.minor_version <= HBM_MINOR_VERSION);
1102*4882a593Smuzhiyun }
1103*4882a593Smuzhiyun 
1104*4882a593Smuzhiyun /**
1105*4882a593Smuzhiyun  * mei_hbm_dispatch - bottom half read routine after ISR to
1106*4882a593Smuzhiyun  * handle the read bus message cmd processing.
1107*4882a593Smuzhiyun  *
1108*4882a593Smuzhiyun  * @dev: the device structure
1109*4882a593Smuzhiyun  * @hdr: header of bus message
1110*4882a593Smuzhiyun  *
1111*4882a593Smuzhiyun  * Return: 0 on success and < 0 on failure
1112*4882a593Smuzhiyun  */
mei_hbm_dispatch(struct mei_device * dev,struct mei_msg_hdr * hdr)1113*4882a593Smuzhiyun int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
1114*4882a593Smuzhiyun {
1115*4882a593Smuzhiyun 	struct mei_bus_message *mei_msg;
1116*4882a593Smuzhiyun 	struct hbm_host_version_response *version_res;
1117*4882a593Smuzhiyun 	struct hbm_props_response *props_res;
1118*4882a593Smuzhiyun 	struct hbm_host_enum_response *enum_res;
1119*4882a593Smuzhiyun 	struct hbm_dma_setup_response *dma_setup_res;
1120*4882a593Smuzhiyun 	struct hbm_add_client_request *add_cl_req;
1121*4882a593Smuzhiyun 	struct hbm_capability_response *capability_res;
1122*4882a593Smuzhiyun 	int ret;
1123*4882a593Smuzhiyun 
1124*4882a593Smuzhiyun 	struct mei_hbm_cl_cmd *cl_cmd;
1125*4882a593Smuzhiyun 	struct hbm_client_connect_request *disconnect_req;
1126*4882a593Smuzhiyun 	struct hbm_flow_control *fctrl;
1127*4882a593Smuzhiyun 
1128*4882a593Smuzhiyun 	/* read the message to our buffer */
1129*4882a593Smuzhiyun 	BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
1130*4882a593Smuzhiyun 	mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
1131*4882a593Smuzhiyun 	mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
1132*4882a593Smuzhiyun 	cl_cmd  = (struct mei_hbm_cl_cmd *)mei_msg;
1133*4882a593Smuzhiyun 
1134*4882a593Smuzhiyun 	/* ignore spurious message and prevent reset nesting
1135*4882a593Smuzhiyun 	 * hbm is put to idle during system reset
1136*4882a593Smuzhiyun 	 */
1137*4882a593Smuzhiyun 	if (dev->hbm_state == MEI_HBM_IDLE) {
1138*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: state is idle ignore spurious messages\n");
1139*4882a593Smuzhiyun 		return 0;
1140*4882a593Smuzhiyun 	}
1141*4882a593Smuzhiyun 
1142*4882a593Smuzhiyun 	switch (mei_msg->hbm_cmd) {
1143*4882a593Smuzhiyun 	case HOST_START_RES_CMD:
1144*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: start: response message received.\n");
1145*4882a593Smuzhiyun 
1146*4882a593Smuzhiyun 		dev->init_clients_timer = 0;
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun 		version_res = (struct hbm_host_version_response *)mei_msg;
1149*4882a593Smuzhiyun 
1150*4882a593Smuzhiyun 		dev_dbg(dev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
1151*4882a593Smuzhiyun 				HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
1152*4882a593Smuzhiyun 				version_res->me_max_version.major_version,
1153*4882a593Smuzhiyun 				version_res->me_max_version.minor_version);
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun 		if (version_res->host_version_supported) {
1156*4882a593Smuzhiyun 			dev->version.major_version = HBM_MAJOR_VERSION;
1157*4882a593Smuzhiyun 			dev->version.minor_version = HBM_MINOR_VERSION;
1158*4882a593Smuzhiyun 		} else {
1159*4882a593Smuzhiyun 			dev->version.major_version =
1160*4882a593Smuzhiyun 				version_res->me_max_version.major_version;
1161*4882a593Smuzhiyun 			dev->version.minor_version =
1162*4882a593Smuzhiyun 				version_res->me_max_version.minor_version;
1163*4882a593Smuzhiyun 		}
1164*4882a593Smuzhiyun 
1165*4882a593Smuzhiyun 		if (!mei_hbm_version_is_supported(dev)) {
1166*4882a593Smuzhiyun 			dev_warn(dev->dev, "hbm: start: version mismatch - stopping the driver.\n");
1167*4882a593Smuzhiyun 
1168*4882a593Smuzhiyun 			dev->hbm_state = MEI_HBM_STOPPED;
1169*4882a593Smuzhiyun 			if (mei_hbm_stop_req(dev)) {
1170*4882a593Smuzhiyun 				dev_err(dev->dev, "hbm: start: failed to send stop request\n");
1171*4882a593Smuzhiyun 				return -EIO;
1172*4882a593Smuzhiyun 			}
1173*4882a593Smuzhiyun 			break;
1174*4882a593Smuzhiyun 		}
1175*4882a593Smuzhiyun 
1176*4882a593Smuzhiyun 		mei_hbm_config_features(dev);
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
1179*4882a593Smuzhiyun 		    dev->hbm_state != MEI_HBM_STARTING) {
1180*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: start: state mismatch, [%d, %d]\n",
1181*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1182*4882a593Smuzhiyun 			return -EPROTO;
1183*4882a593Smuzhiyun 		}
1184*4882a593Smuzhiyun 
1185*4882a593Smuzhiyun 		if (dev->hbm_f_cap_supported) {
1186*4882a593Smuzhiyun 			if (mei_hbm_capabilities_req(dev))
1187*4882a593Smuzhiyun 				return -EIO;
1188*4882a593Smuzhiyun 			wake_up(&dev->wait_hbm_start);
1189*4882a593Smuzhiyun 			break;
1190*4882a593Smuzhiyun 		}
1191*4882a593Smuzhiyun 
1192*4882a593Smuzhiyun 		if (dev->hbm_f_dr_supported) {
1193*4882a593Smuzhiyun 			if (mei_dmam_ring_alloc(dev))
1194*4882a593Smuzhiyun 				dev_info(dev->dev, "running w/o dma ring\n");
1195*4882a593Smuzhiyun 			if (mei_dma_ring_is_allocated(dev)) {
1196*4882a593Smuzhiyun 				if (mei_hbm_dma_setup_req(dev))
1197*4882a593Smuzhiyun 					return -EIO;
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 				wake_up(&dev->wait_hbm_start);
1200*4882a593Smuzhiyun 				break;
1201*4882a593Smuzhiyun 			}
1202*4882a593Smuzhiyun 		}
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun 		dev->hbm_f_dr_supported = 0;
1205*4882a593Smuzhiyun 		mei_dmam_ring_free(dev);
1206*4882a593Smuzhiyun 
1207*4882a593Smuzhiyun 		if (mei_hbm_enum_clients_req(dev))
1208*4882a593Smuzhiyun 			return -EIO;
1209*4882a593Smuzhiyun 
1210*4882a593Smuzhiyun 		wake_up(&dev->wait_hbm_start);
1211*4882a593Smuzhiyun 		break;
1212*4882a593Smuzhiyun 
1213*4882a593Smuzhiyun 	case MEI_HBM_CAPABILITIES_RES_CMD:
1214*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: capabilities response: message received.\n");
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 		dev->init_clients_timer = 0;
1217*4882a593Smuzhiyun 
1218*4882a593Smuzhiyun 		if (dev->hbm_state != MEI_HBM_CAP_SETUP) {
1219*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: capabilities response: state mismatch, [%d, %d]\n",
1220*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1221*4882a593Smuzhiyun 			return -EPROTO;
1222*4882a593Smuzhiyun 		}
1223*4882a593Smuzhiyun 
1224*4882a593Smuzhiyun 		capability_res = (struct hbm_capability_response *)mei_msg;
1225*4882a593Smuzhiyun 		if (!(capability_res->capability_granted[0] & HBM_CAP_VT))
1226*4882a593Smuzhiyun 			dev->hbm_f_vt_supported = 0;
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 		if (dev->hbm_f_dr_supported) {
1229*4882a593Smuzhiyun 			if (mei_dmam_ring_alloc(dev))
1230*4882a593Smuzhiyun 				dev_info(dev->dev, "running w/o dma ring\n");
1231*4882a593Smuzhiyun 			if (mei_dma_ring_is_allocated(dev)) {
1232*4882a593Smuzhiyun 				if (mei_hbm_dma_setup_req(dev))
1233*4882a593Smuzhiyun 					return -EIO;
1234*4882a593Smuzhiyun 				break;
1235*4882a593Smuzhiyun 			}
1236*4882a593Smuzhiyun 		}
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun 		dev->hbm_f_dr_supported = 0;
1239*4882a593Smuzhiyun 		mei_dmam_ring_free(dev);
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 		if (mei_hbm_enum_clients_req(dev))
1242*4882a593Smuzhiyun 			return -EIO;
1243*4882a593Smuzhiyun 		break;
1244*4882a593Smuzhiyun 
1245*4882a593Smuzhiyun 	case MEI_HBM_DMA_SETUP_RES_CMD:
1246*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: dma setup response: message received.\n");
1247*4882a593Smuzhiyun 
1248*4882a593Smuzhiyun 		dev->init_clients_timer = 0;
1249*4882a593Smuzhiyun 
1250*4882a593Smuzhiyun 		if (dev->hbm_state != MEI_HBM_DR_SETUP) {
1251*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: dma setup response: state mismatch, [%d, %d]\n",
1252*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1253*4882a593Smuzhiyun 			return -EPROTO;
1254*4882a593Smuzhiyun 		}
1255*4882a593Smuzhiyun 
1256*4882a593Smuzhiyun 		dma_setup_res = (struct hbm_dma_setup_response *)mei_msg;
1257*4882a593Smuzhiyun 
1258*4882a593Smuzhiyun 		if (dma_setup_res->status) {
1259*4882a593Smuzhiyun 			u8 status = dma_setup_res->status;
1260*4882a593Smuzhiyun 
1261*4882a593Smuzhiyun 			if (status == MEI_HBMS_NOT_ALLOWED) {
1262*4882a593Smuzhiyun 				dev_dbg(dev->dev, "hbm: dma setup not allowed\n");
1263*4882a593Smuzhiyun 			} else {
1264*4882a593Smuzhiyun 				dev_info(dev->dev, "hbm: dma setup response: failure = %d %s\n",
1265*4882a593Smuzhiyun 					 status,
1266*4882a593Smuzhiyun 					 mei_hbm_status_str(status));
1267*4882a593Smuzhiyun 			}
1268*4882a593Smuzhiyun 			dev->hbm_f_dr_supported = 0;
1269*4882a593Smuzhiyun 			mei_dmam_ring_free(dev);
1270*4882a593Smuzhiyun 		}
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun 		if (mei_hbm_enum_clients_req(dev))
1273*4882a593Smuzhiyun 			return -EIO;
1274*4882a593Smuzhiyun 		break;
1275*4882a593Smuzhiyun 
1276*4882a593Smuzhiyun 	case CLIENT_CONNECT_RES_CMD:
1277*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: client connect response: message received.\n");
1278*4882a593Smuzhiyun 		mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT);
1279*4882a593Smuzhiyun 		break;
1280*4882a593Smuzhiyun 
1281*4882a593Smuzhiyun 	case CLIENT_DISCONNECT_RES_CMD:
1282*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: client disconnect response: message received.\n");
1283*4882a593Smuzhiyun 		mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT);
1284*4882a593Smuzhiyun 		break;
1285*4882a593Smuzhiyun 
1286*4882a593Smuzhiyun 	case MEI_FLOW_CONTROL_CMD:
1287*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: client flow control response: message received.\n");
1288*4882a593Smuzhiyun 
1289*4882a593Smuzhiyun 		fctrl = (struct hbm_flow_control *)mei_msg;
1290*4882a593Smuzhiyun 		mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl);
1291*4882a593Smuzhiyun 		break;
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun 	case MEI_PG_ISOLATION_ENTRY_RES_CMD:
1294*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: power gate isolation entry response received\n");
1295*4882a593Smuzhiyun 		ret = mei_hbm_pg_enter_res(dev);
1296*4882a593Smuzhiyun 		if (ret)
1297*4882a593Smuzhiyun 			return ret;
1298*4882a593Smuzhiyun 		break;
1299*4882a593Smuzhiyun 
1300*4882a593Smuzhiyun 	case MEI_PG_ISOLATION_EXIT_REQ_CMD:
1301*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: power gate isolation exit request received\n");
1302*4882a593Smuzhiyun 		ret = mei_hbm_pg_exit_res(dev);
1303*4882a593Smuzhiyun 		if (ret)
1304*4882a593Smuzhiyun 			return ret;
1305*4882a593Smuzhiyun 		break;
1306*4882a593Smuzhiyun 
1307*4882a593Smuzhiyun 	case HOST_CLIENT_PROPERTIES_RES_CMD:
1308*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: properties response: message received.\n");
1309*4882a593Smuzhiyun 
1310*4882a593Smuzhiyun 		dev->init_clients_timer = 0;
1311*4882a593Smuzhiyun 
1312*4882a593Smuzhiyun 		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
1313*4882a593Smuzhiyun 		    dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
1314*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
1315*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1316*4882a593Smuzhiyun 			return -EPROTO;
1317*4882a593Smuzhiyun 		}
1318*4882a593Smuzhiyun 
1319*4882a593Smuzhiyun 		props_res = (struct hbm_props_response *)mei_msg;
1320*4882a593Smuzhiyun 
1321*4882a593Smuzhiyun 		if (props_res->status == MEI_HBMS_CLIENT_NOT_FOUND) {
1322*4882a593Smuzhiyun 			dev_dbg(dev->dev, "hbm: properties response: %d CLIENT_NOT_FOUND\n",
1323*4882a593Smuzhiyun 				props_res->me_addr);
1324*4882a593Smuzhiyun 		} else if (props_res->status) {
1325*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: properties response: wrong status = %d %s\n",
1326*4882a593Smuzhiyun 				props_res->status,
1327*4882a593Smuzhiyun 				mei_hbm_status_str(props_res->status));
1328*4882a593Smuzhiyun 			return -EPROTO;
1329*4882a593Smuzhiyun 		} else {
1330*4882a593Smuzhiyun 			mei_hbm_me_cl_add(dev, props_res);
1331*4882a593Smuzhiyun 		}
1332*4882a593Smuzhiyun 
1333*4882a593Smuzhiyun 		/* request property for the next client */
1334*4882a593Smuzhiyun 		if (mei_hbm_prop_req(dev, props_res->me_addr + 1))
1335*4882a593Smuzhiyun 			return -EIO;
1336*4882a593Smuzhiyun 
1337*4882a593Smuzhiyun 		break;
1338*4882a593Smuzhiyun 
1339*4882a593Smuzhiyun 	case HOST_ENUM_RES_CMD:
1340*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: enumeration response: message received\n");
1341*4882a593Smuzhiyun 
1342*4882a593Smuzhiyun 		dev->init_clients_timer = 0;
1343*4882a593Smuzhiyun 
1344*4882a593Smuzhiyun 		enum_res = (struct hbm_host_enum_response *) mei_msg;
1345*4882a593Smuzhiyun 		BUILD_BUG_ON(sizeof(dev->me_clients_map)
1346*4882a593Smuzhiyun 				< sizeof(enum_res->valid_addresses));
1347*4882a593Smuzhiyun 		memcpy(dev->me_clients_map, enum_res->valid_addresses,
1348*4882a593Smuzhiyun 				sizeof(enum_res->valid_addresses));
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
1351*4882a593Smuzhiyun 		    dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
1352*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
1353*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1354*4882a593Smuzhiyun 			return -EPROTO;
1355*4882a593Smuzhiyun 		}
1356*4882a593Smuzhiyun 
1357*4882a593Smuzhiyun 		dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
1358*4882a593Smuzhiyun 
1359*4882a593Smuzhiyun 		/* first property request */
1360*4882a593Smuzhiyun 		if (mei_hbm_prop_req(dev, 0))
1361*4882a593Smuzhiyun 			return -EIO;
1362*4882a593Smuzhiyun 
1363*4882a593Smuzhiyun 		break;
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	case HOST_STOP_RES_CMD:
1366*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: stop response: message received\n");
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun 		dev->init_clients_timer = 0;
1369*4882a593Smuzhiyun 
1370*4882a593Smuzhiyun 		if (dev->hbm_state != MEI_HBM_STOPPED) {
1371*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
1372*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1373*4882a593Smuzhiyun 			return -EPROTO;
1374*4882a593Smuzhiyun 		}
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun 		mei_set_devstate(dev, MEI_DEV_POWER_DOWN);
1377*4882a593Smuzhiyun 		dev_info(dev->dev, "hbm: stop response: resetting.\n");
1378*4882a593Smuzhiyun 		/* force the reset */
1379*4882a593Smuzhiyun 		return -EPROTO;
1380*4882a593Smuzhiyun 		break;
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 	case CLIENT_DISCONNECT_REQ_CMD:
1383*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: disconnect request: message received\n");
1384*4882a593Smuzhiyun 
1385*4882a593Smuzhiyun 		disconnect_req = (struct hbm_client_connect_request *)mei_msg;
1386*4882a593Smuzhiyun 		mei_hbm_fw_disconnect_req(dev, disconnect_req);
1387*4882a593Smuzhiyun 		break;
1388*4882a593Smuzhiyun 
1389*4882a593Smuzhiyun 	case ME_STOP_REQ_CMD:
1390*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: stop request: message received\n");
1391*4882a593Smuzhiyun 		dev->hbm_state = MEI_HBM_STOPPED;
1392*4882a593Smuzhiyun 		if (mei_hbm_stop_req(dev)) {
1393*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: stop request: failed to send stop request\n");
1394*4882a593Smuzhiyun 			return -EIO;
1395*4882a593Smuzhiyun 		}
1396*4882a593Smuzhiyun 		break;
1397*4882a593Smuzhiyun 
1398*4882a593Smuzhiyun 	case MEI_HBM_ADD_CLIENT_REQ_CMD:
1399*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: add client request received\n");
1400*4882a593Smuzhiyun 		/*
1401*4882a593Smuzhiyun 		 * after the host receives the enum_resp
1402*4882a593Smuzhiyun 		 * message clients may be added or removed
1403*4882a593Smuzhiyun 		 */
1404*4882a593Smuzhiyun 		if (dev->hbm_state <= MEI_HBM_ENUM_CLIENTS ||
1405*4882a593Smuzhiyun 		    dev->hbm_state >= MEI_HBM_STOPPED) {
1406*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: add client: state mismatch, [%d, %d]\n",
1407*4882a593Smuzhiyun 				dev->dev_state, dev->hbm_state);
1408*4882a593Smuzhiyun 			return -EPROTO;
1409*4882a593Smuzhiyun 		}
1410*4882a593Smuzhiyun 		add_cl_req = (struct hbm_add_client_request *)mei_msg;
1411*4882a593Smuzhiyun 		ret = mei_hbm_fw_add_cl_req(dev, add_cl_req);
1412*4882a593Smuzhiyun 		if (ret) {
1413*4882a593Smuzhiyun 			dev_err(dev->dev, "hbm: add client: failed to send response %d\n",
1414*4882a593Smuzhiyun 				ret);
1415*4882a593Smuzhiyun 			return -EIO;
1416*4882a593Smuzhiyun 		}
1417*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: add client request processed\n");
1418*4882a593Smuzhiyun 		break;
1419*4882a593Smuzhiyun 
1420*4882a593Smuzhiyun 	case MEI_HBM_NOTIFY_RES_CMD:
1421*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: notify response received\n");
1422*4882a593Smuzhiyun 		mei_hbm_cl_res(dev, cl_cmd, notify_res_to_fop(cl_cmd));
1423*4882a593Smuzhiyun 		break;
1424*4882a593Smuzhiyun 
1425*4882a593Smuzhiyun 	case MEI_HBM_NOTIFICATION_CMD:
1426*4882a593Smuzhiyun 		dev_dbg(dev->dev, "hbm: notification\n");
1427*4882a593Smuzhiyun 		mei_hbm_cl_notify(dev, cl_cmd);
1428*4882a593Smuzhiyun 		break;
1429*4882a593Smuzhiyun 
1430*4882a593Smuzhiyun 	default:
1431*4882a593Smuzhiyun 		WARN(1, "hbm: wrong command %d\n", mei_msg->hbm_cmd);
1432*4882a593Smuzhiyun 		return -EPROTO;
1433*4882a593Smuzhiyun 
1434*4882a593Smuzhiyun 	}
1435*4882a593Smuzhiyun 	return 0;
1436*4882a593Smuzhiyun }
1437*4882a593Smuzhiyun 
1438