xref: /titanic_41/usr/src/uts/intel/io/heci/heci_init.c (revision 1402f64f9efa57c2c2cba776f3f45c67e6055deb)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Part of Intel(R) Manageability Engine Interface Linux driver
8  *
9  * Copyright (c) 2003 - 2008 Intel Corp.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions, and the following disclaimer,
17  *    without modification.
18  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19  *    substantially similar to the "NO WARRANTY" disclaimer below
20  *    ("Disclaimer") and any redistribution must be conditioned upon
21  *    including a substantially similar Disclaimer requirement for further
22  *    binary redistribution.
23  * 3. Neither the names of the above-listed copyright holders nor the names
24  *    of any contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * Alternatively, this software may be distributed under the terms of the
28  * GNU General Public License ("GPL") version 2 as published by the Free
29  * Software Foundation.
30  *
31  * NO WARRANTY
32  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42  * POSSIBILITY OF SUCH DAMAGES.
43  *
44  */
45 
46 #include <sys/types.h>
47 #include <sys/cmn_err.h>
48 #include <sys/conf.h>
49 #include <sys/ddi.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/devops.h>
52 #include <sys/instance.h>
53 #include <sys/modctl.h>
54 #include <sys/open.h>
55 #include <sys/stat.h>
56 #include <sys/sunddi.h>
57 #include <sys/sunndi.h>
58 #include <sys/systm.h>
59 #include <sys/mkdev.h>
60 #include <sys/list.h>
61 #include <sys/note.h>
62 #include "heci_data_structures.h"
63 #include "heci_interface.h"
64 #include "heci.h"
65 
66 
67 const uint8_t watch_dog_data[] = {
68 	1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
69 };
70 const uint8_t start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
71 const uint8_t stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
72 
73 const uint8_t heci_wd_state_independence_msg[3][4] = {
74 	{0x05, 0x02, 0x51, 0x10},
75 	{0x05, 0x02, 0x52, 0x10},
76 	{0x07, 0x02, 0x01, 0x10}
77 };
78 
79 const struct guid heci_asf_guid = {
80 	0x75B30CD6, 0xA29E, 0x4AF7,
81 	{0xA7, 0x12, 0xE6, 0x17, 0x43, 0x93, 0xC8, 0xA6}
82 };
83 const struct guid heci_wd_guid = {
84 	0x05B79A6F, 0x4628, 0x4D7F,
85 	{0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB}
86 };
87 const struct guid heci_pthi_guid = {
88 	0x12f80028, 0xb4b7, 0x4b2d,
89 	{0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}
90 };
91 
92 
93 /*
94  *  heci init function prototypes
95  */
96 static void heci_check_asf_mode(struct iamt_heci_device *dev);
97 static int host_start_message(struct iamt_heci_device *dev);
98 static int host_enum_clients_message(struct iamt_heci_device *dev);
99 static int allocate_me_clients_storage(struct iamt_heci_device *dev);
100 static void host_init_wd(struct iamt_heci_device *dev);
101 static void host_init_iamthif(struct iamt_heci_device *dev);
102 static inline int heci_fe_same_id(struct heci_file_private *fe1,
103 		struct heci_file_private *fe2);
104 
105 
106 
107 /*
108  * heci_initialize_list - Sets up a  queue  list.
109  *
110  * @list: An instance of our list structure
111  * @dev: Device object for our driver
112  */
113 void
114 heci_initialize_list(struct io_heci_list *list,
115 	struct iamt_heci_device *dev)
116 {
117 	/* initialize our queue list */
118 	LIST_INIT_HEAD(&list->heci_cb.cb_list);
119 	list->status = 0;
120 	list->device_extension = dev;
121 }
122 
123 /*
124  * heci_flush_queues - flush our queues list belong to file_ext.
125  *
126  * @dev: Device object for our driver
127  * @file_ext: private data of the file object
128  *
129  */
130 void
131 heci_flush_queues(struct iamt_heci_device *dev,
132 	struct heci_file_private *file_ext)
133 {
134 	int i;
135 
136 	if (!dev || !file_ext)
137 		return;
138 
139 	/* flush our queue list belong to file_ext */
140 	for (i = 0; i < HECI_IO_LISTS_NUMBER; i++) {
141 		DBG("remove list entry belong to file_ext\n");
142 		heci_flush_list(dev->io_list_array[i], file_ext);
143 	}
144 }
145 
146 
147 /*
148  * heci_flush_list - remove list entry belong to file_ext.
149  *
150  * @list:  An instance of our list structure
151  * @file_ext: private data of the file object
152  */
153 void
154 heci_flush_list(struct io_heci_list *list,
155 	struct heci_file_private *file_ext)
156 {
157 	struct heci_file_private *file_ext_tmp;
158 	struct heci_cb_private *priv_cb_pos = NULL;
159 	struct heci_cb_private *priv_cb_next = NULL;
160 
161 	if (!list || !file_ext)
162 		return;
163 
164 	if (list->status != 0)
165 		return;
166 
167 	if (list_empty(&list->heci_cb.cb_list))
168 		return;
169 
170 	list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
171 		&list->heci_cb.cb_list, cb_list, struct heci_cb_private) {
172 		if (priv_cb_pos) {
173 			file_ext_tmp = (struct heci_file_private *)
174 				priv_cb_pos->file_private;
175 			if (file_ext_tmp) {
176 				if (heci_fe_same_id(file_ext, file_ext_tmp))
177 					list_del(&priv_cb_pos->cb_list);
178 			}
179 		}
180 	}
181 }
182 
183 /*
184  * heci_reset_iamthif_params - initializes heci device iamthif
185  * @dev: The heci device structure
186  */
187 static void heci_reset_iamthif_params(struct iamt_heci_device *dev)
188 {
189 	/* reset iamthif parameters. */
190 	dev->iamthif_current_cb = NULL;
191 	dev->iamthif_msg_buf_size = 0;
192 	dev->iamthif_msg_buf_index = 0;
193 	dev->iamthif_canceled = 0;
194 	dev->iamthif_file_ext.file = NULL;
195 	dev->iamthif_ioctl = 0;
196 	dev->iamthif_state = HECI_IAMTHIF_IDLE;
197 	dev->iamthif_timer = 0;
198 }
199 
200 /*
201  * fini_heci_device - release resources allocated in init_heci_device()
202  */
203 void
204 fini_heci_device(struct iamt_heci_device *device)
205 {
206 	mutex_destroy(&device->device_lock);
207 	cv_destroy(&device->wait_recvd_msg);
208 	cv_destroy(&device->wait_stop_wd);
209 	if (device->work)
210 		ddi_taskq_destroy(device->work);
211 	if (device->reinit_tsk)
212 		ddi_taskq_destroy(device->reinit_tsk);
213 
214 }
215 
216 /*
217  * init_heci_device - initializes the heci device structure
218  *
219  */
220 void
221 init_heci_device(dev_info_t *dip,
222 	struct iamt_heci_device *device)
223 {
224 	int i;
225 
226 	if (!device)
227 		return;
228 
229 	/* setup our list array */
230 	device->io_list_array[0] = &device->read_list;
231 	device->io_list_array[1] = &device->write_list;
232 	device->io_list_array[2] = &device->write_waiting_list;
233 	device->io_list_array[3] = &device->ctrl_wr_list;
234 	device->io_list_array[4] = &device->ctrl_rd_list;
235 	device->io_list_array[5] = &device->pthi_cmd_list;
236 	device->io_list_array[6] = &device->pthi_read_complete_list;
237 	LIST_INIT_HEAD(&device->file_list);
238 	LIST_INIT_HEAD(&device->wd_file_ext.link);
239 	LIST_INIT_HEAD(&device->iamthif_file_ext.link);
240 	mutex_init(&device->device_lock, NULL, MUTEX_DRIVER, NULL);
241 	cv_init(&device->wait_recvd_msg, NULL, CV_DRIVER, NULL);
242 	cv_init(&device->wait_stop_wd, NULL, CV_DRIVER, NULL);
243 	device->open_handle_count = 0;
244 	device->num_heci_me_clients = 0;
245 	device->extra_write_index = 0;
246 	device->rd_msg_hdr = 0;
247 	device->mem_addr = NULL;
248 	device->asf_mode = B_FALSE;
249 	device->need_reset = B_FALSE;
250 	device->recvd_msg = B_FALSE;
251 	device->heci_state = HECI_INITIALIZING;
252 	device->iamthif_state = HECI_IAMTHIF_IDLE;
253 
254 	device->work = ddi_taskq_create(dip, "heci_bh_handler", 1,
255 	    TASKQ_DEFAULTPRI, 0);
256 	if (device->work == NULL)
257 		cmn_err(CE_WARN, "taskq_create failed for heci_bh_handler");
258 	device->reinit_tsk = ddi_taskq_create(dip, "heci_reinit_tsk", 1,
259 	    TASKQ_DEFAULTPRI, 0);
260 	if (device->reinit_tsk == NULL)
261 		cmn_err(CE_WARN, "taskq_create failed for reinit_tsk");
262 
263 	device->wd_pending = B_FALSE;
264 	device->wd_stoped = B_FALSE;
265 
266 	device->me_clients = NULL;
267 	for (i = 0; i < HECI_IO_LISTS_NUMBER; i++)
268 		heci_initialize_list(device->io_list_array[i], device);
269 	device->dip = dip;
270 }
271 
272 /*
273  * heci_hw_init  - init host and fw to start work.
274  *
275  * @dev: Device object for our driver
276  *
277  * @return 0 on success, <0 on failure.
278  */
279 int
280 heci_hw_init(struct iamt_heci_device *dev)
281 {
282 	int err = 0;
283 
284 	mutex_enter(&dev->device_lock);
285 	dev->host_hw_state = read_heci_register(dev, H_CSR);
286 	dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
287 	DBG("host_hw_state = 0x%08x, mestate = 0x%08x.\n",
288 	    dev->host_hw_state, dev->me_hw_state);
289 
290 	if ((dev->host_hw_state & H_IS) == H_IS) {
291 		/* acknowledge interrupt and stop interupts */
292 		heci_set_csr_register(dev);
293 	}
294 	dev->recvd_msg = 0;
295 	DBG("reset in start the heci device.\n");
296 
297 	heci_reset(dev, 1);
298 
299 	DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
300 	    dev->host_hw_state, dev->me_hw_state);
301 
302 	/* wait for ME to turn on ME_RDY */
303 	err = 0;
304 	while (!dev->recvd_msg && err != -1) {
305 		err = cv_reltimedwait(&dev->wait_recvd_msg,
306 		    &dev->device_lock, HECI_INTEROP_TIMEOUT, TR_CLOCK_TICK);
307 	}
308 
309 	if (err == -1 && !dev->recvd_msg) {
310 		dev->heci_state = HECI_DISABLED;
311 		DBG("wait_event_interruptible_timeout failed"
312 		    "on wait for ME to turn on ME_RDY.\n");
313 		mutex_exit(&dev->device_lock);
314 		return (-ENODEV);
315 	} else {
316 		if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
317 		    ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
318 			dev->heci_state = HECI_DISABLED;
319 			DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
320 			    dev->host_hw_state,
321 			    dev->me_hw_state);
322 
323 			if (!(dev->host_hw_state & H_RDY) != H_RDY)
324 				DBG("host turn off H_RDY.\n");
325 
326 			if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
327 				DBG("ME turn off ME_RDY.\n");
328 
329 			cmn_err(CE_WARN,
330 			    "heci: link layer initialization failed.\n");
331 			mutex_exit(&dev->device_lock);
332 			return (-ENODEV);
333 		}
334 	}
335 	dev->recvd_msg = 0;
336 	DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
337 	    dev->host_hw_state, dev->me_hw_state);
338 	DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
339 	DBG("heci: link layer has been established.\n");
340 	mutex_exit(&dev->device_lock);
341 	return (0);
342 }
343 
344 /*
345  * heci_hw_reset  - reset fw via heci csr register.
346  *
347  * @dev: Device object for our driver
348  * @interrupts: if interrupt should be enable after reset.
349  */
350 static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts)
351 {
352 	dev->host_hw_state |= (H_RST | H_IG);
353 
354 	if (interrupts)
355 		heci_csr_enable_interrupts(dev);
356 	else
357 		heci_csr_disable_interrupts(dev);
358 
359 }
360 
361 /*
362  * heci_reset  - reset host and fw.
363  *
364  * @dev: Device object for our driver
365  * @interrupts: if interrupt should be enable after reset.
366  */
367 void
368 heci_reset(struct iamt_heci_device *dev, int interrupts)
369 {
370 	struct heci_file_private *file_pos = NULL;
371 	struct heci_file_private *file_next = NULL;
372 	struct heci_cb_private *priv_cb_pos = NULL;
373 	struct heci_cb_private *priv_cb_next = NULL;
374 	int unexpected = 0;
375 
376 	if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
377 		dev->need_reset = 1;
378 		return;
379 	}
380 
381 	if (dev->heci_state != HECI_INITIALIZING &&
382 	    dev->heci_state != HECI_DISABLED &&
383 	    dev->heci_state != HECI_POWER_DOWN &&
384 	    dev->heci_state != HECI_POWER_UP)
385 		unexpected = 1;
386 
387 	if (dev->reinit_tsk != NULL) {
388 		mutex_exit(&dev->device_lock);
389 		(void) ddi_taskq_wait(dev->reinit_tsk);
390 		mutex_enter(&dev->device_lock);
391 	}
392 
393 	dev->host_hw_state = read_heci_register(dev, H_CSR);
394 
395 	DBG("before reset host_hw_state = 0x%08x.\n",
396 	    dev->host_hw_state);
397 
398 	heci_hw_reset(dev, interrupts);
399 
400 	dev->host_hw_state &= ~H_RST;
401 	dev->host_hw_state |= H_IG;
402 
403 	write_heci_register(dev, H_CSR, dev->host_hw_state);
404 
405 	DBG("currently saved host_hw_state = 0x%08x.\n",
406 	    dev->host_hw_state);
407 
408 	dev->need_reset = 0;
409 
410 	if (dev->heci_state != HECI_INITIALIZING) {
411 		if ((dev->heci_state != HECI_DISABLED) &&
412 		    (dev->heci_state != HECI_POWER_DOWN))
413 			dev->heci_state = HECI_RESETING;
414 
415 		list_for_each_entry_safe(file_pos,
416 		    file_next, &dev->file_list, link,
417 		    struct heci_file_private) {
418 			file_pos->state = HECI_FILE_DISCONNECTED;
419 			file_pos->flow_ctrl_creds = 0;
420 			file_pos->read_cb = NULL;
421 			file_pos->timer_count = 0;
422 		}
423 		/* remove entry if already in list */
424 		DBG("list del iamthif and wd file list.\n");
425 		heci_remove_client_from_file_list(dev,
426 		    dev->wd_file_ext.host_client_id);
427 
428 		heci_remove_client_from_file_list(dev,
429 		    dev->iamthif_file_ext.host_client_id);
430 
431 		heci_reset_iamthif_params(dev);
432 		dev->wd_due_counter = 0;
433 		dev->extra_write_index = 0;
434 	}
435 
436 	dev->num_heci_me_clients = 0;
437 	dev->rd_msg_hdr = 0;
438 	dev->stop = 0;
439 	dev->wd_pending = 0;
440 
441 	/* update the state of the registers after reset */
442 	dev->host_hw_state =  read_heci_register(dev, H_CSR);
443 	dev->me_hw_state =  read_heci_register(dev, ME_CSR_HA);
444 
445 	DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
446 	    dev->host_hw_state, dev->me_hw_state);
447 
448 	if (unexpected)
449 		cmn_err(CE_WARN, "unexpected heci reset.\n");
450 
451 	/* Wake up all readings so they can be interrupted */
452 	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
453 	    struct heci_file_private) {
454 		cmn_err(CE_NOTE, "heci: Waking up client!\n");
455 		cv_broadcast(&file_pos->rx_wait);
456 	}
457 	/* remove all waiting requests */
458 	if (dev->write_list.status == 0 &&
459 	    !list_empty(&dev->write_list.heci_cb.cb_list)) {
460 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
461 		    &dev->write_list.heci_cb.cb_list, cb_list,
462 		    struct heci_cb_private) {
463 			if (priv_cb_pos) {
464 				list_del(&priv_cb_pos->cb_list);
465 				heci_free_cb_private(priv_cb_pos);
466 			}
467 		}
468 	}
469 }
470 
471 /*
472  * heci_initialize_clients  -  routine.
473  *
474  * @dev: Device object for our driver
475  *
476  */
477 int
478 heci_initialize_clients(struct iamt_heci_device *dev)
479 {
480 	int status;
481 
482 	/* msleep(100) FW needs time to be ready to talk with us */
483 	delay(drv_usectohz(100000));
484 	DBG("link is established start sending messages.\n");
485 	/* link is established start sending messages. */
486 	status = host_start_message(dev);
487 	if (status != 0) {
488 		mutex_enter(&dev->device_lock);
489 		dev->heci_state = HECI_DISABLED;
490 		mutex_exit(&dev->device_lock);
491 		DBG("start sending messages failed.\n");
492 		return (status);
493 	}
494 	/* enumerate clients */
495 
496 	status = host_enum_clients_message(dev);
497 	if (status != 0) {
498 		mutex_enter(&dev->device_lock);
499 		dev->heci_state = HECI_DISABLED;
500 		mutex_exit(&dev->device_lock);
501 		DBG("enum clients failed.\n");
502 		return (status);
503 	}
504 	/* allocate storage for ME clients representation */
505 	status = allocate_me_clients_storage(dev);
506 	if (status != 0) {
507 		mutex_enter(&dev->device_lock);
508 		dev->num_heci_me_clients = 0;
509 		dev->heci_state = HECI_DISABLED;
510 		mutex_exit(&dev->device_lock);
511 		DBG("allocate clients failed.\n");
512 		return (status);
513 	}
514 
515 	heci_check_asf_mode(dev);
516 	/* heci initialization wd */
517 	host_init_wd(dev);
518 	/* heci initialization iamthif client */
519 	host_init_iamthif(dev);
520 
521 	mutex_enter(&dev->device_lock);
522 	if (dev->need_reset) {
523 		dev->need_reset = 0;
524 		dev->heci_state = HECI_DISABLED;
525 		mutex_exit(&dev->device_lock);
526 		return (-ENODEV);
527 	}
528 
529 	(void) memset(dev->heci_host_clients, 0,
530 	    sizeof (dev->heci_host_clients));
531 	dev->open_handle_count = 0;
532 	dev->heci_host_clients[0] |= 7;
533 	dev->current_host_client_id = 3;
534 	dev->heci_state = HECI_ENABLED;
535 	mutex_exit(&dev->device_lock);
536 	DBG("initialization heci clients successful.\n");
537 	return (0);
538 }
539 
540 /*
541  * heci_task_initialize_clients  -  routine.
542  *
543  * @data: Device object for our driver
544  *
545  */
546 void
547 heci_task_initialize_clients(void *data)
548 {
549 	int ret;
550 	struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
551 
552 	ret = heci_initialize_clients(dev);
553 	if (ret)
554 		cmn_err(CE_WARN, "heci_initialize_clients() failed\n");
555 }
556 
557 /*
558  * host_start_message - heci host send start message.
559  *
560  * @dev: Device object for our driver
561  *
562  * @return 0 on success, <0 on failure.
563  */
564 static int
565 host_start_message(struct iamt_heci_device *dev)
566 {
567 	long timeout = 60;	/* 60 second */
568 	struct heci_msg_hdr *heci_hdr;
569 	struct hbm_host_version_request *host_start_req;
570 	struct hbm_host_stop_request *host_stop_req;
571 	int err = 0;
572 	clock_t delta = (clock_t)(timeout * HZ);
573 
574 	/* host start message */
575 	mutex_enter(&dev->device_lock);
576 	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
577 	heci_hdr->host_addr = 0;
578 	heci_hdr->me_addr = 0;
579 	heci_hdr->length = sizeof (struct hbm_host_version_request);
580 	heci_hdr->msg_complete = 1;
581 	heci_hdr->reserved = 0;
582 
583 	host_start_req =
584 	    (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
585 	(void) memset(host_start_req, 0,
586 	    sizeof (struct hbm_host_version_request));
587 	host_start_req->cmd.cmd = HOST_START_REQ_CMD;
588 	host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
589 	host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
590 	dev->recvd_msg = 0;
591 	if (!heci_write_message(dev, heci_hdr,
592 	    (unsigned char *)(host_start_req),
593 	    heci_hdr->length)) {
594 		DBG("send version to fw fail.\n");
595 		mutex_exit(&dev->device_lock);
596 		return (-ENODEV);
597 	}
598 	DBG("call wait_event_interruptible_timeout for response message.\n");
599 	err = 0;
600 	while (err != -1 && !dev->recvd_msg) {
601 		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
602 		    delta, TR_CLOCK_TICK);
603 	}
604 	if (err == -1 && !dev->recvd_msg) {
605 		DBG("wait_timeout failed on host start response message.\n");
606 		mutex_exit(&dev->device_lock);
607 		return (-ENODEV);
608 	}
609 	dev->recvd_msg = 0;
610 	DBG("wait_timeout successful on host start response message.\n");
611 	if ((dev->version.major_version != HBM_MAJOR_VERSION) ||
612 	    (dev->version.minor_version != HBM_MINOR_VERSION)) {
613 		/* send stop message */
614 		heci_hdr->host_addr = 0;
615 		heci_hdr->me_addr = 0;
616 		heci_hdr->length = sizeof (struct hbm_host_stop_request);
617 		heci_hdr->msg_complete = 1;
618 		heci_hdr->reserved = 0;
619 
620 		host_stop_req =
621 		    (struct hbm_host_stop_request *)&dev->wr_msg_buf[1];
622 
623 		(void) memset(host_stop_req, 0,
624 		    sizeof (struct hbm_host_stop_request));
625 		host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
626 		host_stop_req->reason = DRIVER_STOP_REQUEST;
627 		if (!heci_write_message(dev, heci_hdr,
628 		    (unsigned char *)(host_stop_req),
629 		    heci_hdr->length)) {
630 			DBG("sending stop msg to fw failed.\n");
631 		}
632 		DBG("version mismatch.\n");
633 		mutex_exit(&dev->device_lock);
634 		return (-ENODEV);
635 	}
636 	mutex_exit(&dev->device_lock);
637 	return (0);
638 }
639 
640 /*
641  * host_enum_clients_message - host send enumeration client request message.
642  *
643  * @dev: Device object for our driver
644  * @return 0 on success, <0 on failure.
645  */
646 static int
647 host_enum_clients_message(struct iamt_heci_device *dev)
648 {
649 	long timeout = 5;	/* 5 second */
650 	struct heci_msg_hdr *heci_hdr;
651 	struct hbm_host_enum_request *host_enum_req;
652 	int err = 0;
653 	uint8_t i, j;
654 	clock_t delta = (clock_t)(timeout * HZ);
655 
656 	mutex_enter(&dev->device_lock);
657 
658 	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
659 	/* enumerate clients */
660 	heci_hdr->host_addr = 0;
661 	heci_hdr->me_addr = 0;
662 	heci_hdr->length = sizeof (struct hbm_host_enum_request);
663 	heci_hdr->msg_complete = 1;
664 	heci_hdr->reserved = 0;
665 
666 	host_enum_req = (struct hbm_host_enum_request *)&dev->wr_msg_buf[1];
667 	(void) memset(host_enum_req, 0, sizeof (struct hbm_host_enum_request));
668 	host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
669 	if (!heci_write_message(dev, heci_hdr,
670 	    (unsigned char *)(host_enum_req),
671 	    heci_hdr->length)) {
672 		DBG("send enumeration request failed.\n");
673 		mutex_exit(&dev->device_lock);
674 		return (-ENODEV);
675 	}
676 	/* wait for response */
677 	dev->recvd_msg = 0;
678 	err = 0;
679 	while (!dev->recvd_msg && err != -1) {
680 		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
681 		    delta, TR_CLOCK_TICK);
682 	}
683 	if (err == -1 && !dev->recvd_msg) {
684 		DBG("wait_event_interruptible_timeout failed "
685 		"on enumeration clients response message.\n");
686 		mutex_exit(&dev->device_lock);
687 		return (-ENODEV);
688 	}
689 	dev->recvd_msg = 0;
690 
691 	/* count how many ME clients we have */
692 	for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
693 		for (j = 0; j < 8; j++) {
694 			if ((dev->heci_me_clients[i] & (1 << j)) != 0)
695 				dev->num_heci_me_clients++;
696 
697 		}
698 	}
699 	mutex_exit(&dev->device_lock);
700 
701 	return (0);
702 }
703 
704 /*
705  * host_client_properties - reads properties for client
706  *
707  * @dev: Device object for our driver
708  * @idx: client index in me client array
709  * @client_id: id of the client
710  *
711  * @return 0 on success, <0 on failure.
712  */
713 static int
714 host_client_properties(struct iamt_heci_device *dev,
715 	struct heci_me_client *client)
716 {
717 	struct heci_msg_hdr *heci_hdr;
718 	struct hbm_props_request *host_cli_req;
719 	int err;
720 	clock_t delta = 10 * HZ;
721 
722 	mutex_enter(&dev->device_lock);
723 	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
724 	heci_hdr->host_addr = 0;
725 	heci_hdr->me_addr = 0;
726 	heci_hdr->length = sizeof (struct hbm_props_request);
727 	heci_hdr->msg_complete = 1;
728 	heci_hdr->reserved = 0;
729 
730 	host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
731 	(void) memset(host_cli_req, 0, sizeof (struct hbm_props_request));
732 	host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD;
733 	host_cli_req->address = client->client_id;
734 	if (!heci_write_message(dev, heci_hdr,
735 	    (unsigned char *)(host_cli_req), heci_hdr->length)) {
736 		DBG("send props request failed.\n");
737 		mutex_exit(&dev->device_lock);
738 		return (-ENODEV);
739 	}
740 	/* wait for response */
741 	dev->recvd_msg = 0;
742 
743 	err = 0;
744 	while (!dev->recvd_msg && err != -1) {
745 		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
746 		    delta, TR_CLOCK_TICK);
747 	}
748 	if (err == -1 && !dev->recvd_msg) {
749 		DBG("wait failed on props resp msg.\n");
750 		mutex_exit(&dev->device_lock);
751 		return (-ENODEV);
752 	}
753 	dev->recvd_msg = 0;
754 	mutex_exit(&dev->device_lock);
755 	return (0);
756 }
757 
758 /*
759  * allocate_me_clients_storage - allocate storage for me clients
760  *
761  * @dev: Device object for our driver
762  *
763  * @return 0 on success, <0 on failure.
764  */
765 static int
766 allocate_me_clients_storage(struct iamt_heci_device *dev)
767 {
768 	struct heci_me_client *clients;
769 	struct heci_me_client *client;
770 	uint8_t num, i, j;
771 	int err;
772 
773 	if (dev->num_heci_me_clients == 0)
774 		return (0);
775 
776 	mutex_enter(&dev->device_lock);
777 	if (dev->me_clients) {
778 		kmem_free(dev->me_clients, dev->num_heci_me_clients*
779 		    sizeof (struct heci_me_client));
780 		dev->me_clients = NULL;
781 	}
782 	mutex_exit(&dev->device_lock);
783 
784 	/* allocate storage for ME clients representation */
785 	clients = kmem_zalloc(dev->num_heci_me_clients*
786 	    sizeof (struct heci_me_client), KM_SLEEP);
787 	if (!clients) {
788 		DBG("memory allocation for ME clients failed.\n");
789 		return (-ENOMEM);
790 	}
791 
792 	mutex_enter(&dev->device_lock);
793 	dev->me_clients = clients;
794 	mutex_exit(&dev->device_lock);
795 
796 	num = 0;
797 	for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
798 		for (j = 0; j < 8; j++) {
799 			if ((dev->heci_me_clients[i] & (1 << j)) != 0) {
800 				client = &dev->me_clients[num];
801 				client->client_id = (i * 8) + j;
802 				client->flow_ctrl_creds = 0;
803 				err = host_client_properties(dev, client);
804 				if (err != 0) {
805 					mutex_enter(&dev->device_lock);
806 					kmem_free(dev->me_clients,
807 					    dev->num_heci_me_clients*
808 					    sizeof (struct heci_me_client));
809 					dev->me_clients = NULL;
810 					mutex_exit(&dev->device_lock);
811 					return (err);
812 				}
813 				num++;
814 			}
815 		}
816 	}
817 
818 	return (0);
819 }
820 
821 /*
822  * heci_init_file_private - initializes private file structure.
823  *
824  * @priv: private file structure to be initialized
825  * @file: the file structure
826  *
827  */
828 static void
829 heci_init_file_private(struct heci_file_private *priv,
830 	struct heci_file *file)
831 {
832 	_NOTE(ARGUNUSED(file));
833 
834 	(void) memset(priv, 0, sizeof (struct heci_file_private));
835 	mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL);
836 	mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL);
837 	mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL);
838 	cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL);
839 	DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait);
840 	LIST_INIT_HEAD(&priv->link);
841 	priv->reading_state = HECI_IDLE;
842 	priv->writing_state = HECI_IDLE;
843 }
844 
845 /*
846  * heci_find_me_client - search for ME client guid
847  *                       sets client_id in heci_file_private if found
848  * @dev: Device object for our driver
849  * @priv: private file structure to set client_id in
850  * @cguid: searched guid of ME client
851  * @client_id: id of host client to be set in file private structure
852  *
853  * @return ME client index
854  */
855 static uint8_t
856 heci_find_me_client(struct iamt_heci_device *dev,
857 				struct heci_file_private *priv,
858 				const struct guid *cguid, uint8_t client_id)
859 {
860 	uint8_t i;
861 
862 	if ((dev == NULL) || (priv == NULL) || (cguid == NULL))
863 		return (0);
864 
865 	for (i = 0; i < dev->num_heci_me_clients; i++) {
866 		if (memcmp(cguid,
867 		    &dev->me_clients[i].props.protocol_name,
868 		    sizeof (struct guid)) == 0) {
869 			priv->me_client_id = dev->me_clients[i].client_id;
870 			priv->state = HECI_FILE_CONNECTING;
871 			priv->host_client_id = client_id;
872 
873 			list_add_tail(&priv->link, &dev->file_list);
874 			return (i);
875 		}
876 	}
877 	return (0);
878 }
879 
880 /*
881  * heci_check_asf_mode - check for ASF client
882  *
883  * @dev: Device object for our driver
884  *
885  */
886 static void
887 heci_check_asf_mode(struct iamt_heci_device *dev)
888 {
889 	uint8_t i;
890 
891 	mutex_enter(&dev->device_lock);
892 	dev->asf_mode = 0;
893 	/* find ME ASF client - otherwise assume AMT mode */
894 	DBG("find ME ASF client - otherwise assume AMT mode.\n");
895 	for (i = 0; i < dev->num_heci_me_clients; i++) {
896 		if (memcmp(&heci_asf_guid,
897 		    &dev->me_clients[i].props.protocol_name,
898 		    sizeof (struct guid)) == 0) {
899 			dev->asf_mode = 1;
900 			mutex_exit(&dev->device_lock);
901 			DBG("found ME ASF client.\n");
902 			return;
903 		}
904 	}
905 	mutex_exit(&dev->device_lock);
906 	DBG("assume AMT mode.\n");
907 }
908 
909 /*
910  * heci_connect_me_client - connect ME client
911  * @dev: Device object for our driver
912  * @priv: private file structure
913  * @timeout: connect timeout in seconds
914  *
915  * @return 1 - if connected, 0 - if not
916  */
917 static uint8_t
918 heci_connect_me_client(struct iamt_heci_device *dev,
919 	struct heci_file_private *priv,
920 	long timeout)
921 {
922 	int err = 0;
923 	clock_t delta = (clock_t)(timeout * HZ);
924 
925 	if ((dev == NULL) || (priv == NULL))
926 		return (0);
927 
928 	if (!heci_connect(dev, priv)) {
929 		DBG("failed to call heci_connect for client_id=%d.\n",
930 		    priv->host_client_id);
931 		heci_remove_client_from_file_list(dev, priv->host_client_id);
932 		priv->state = HECI_FILE_DISCONNECTED;
933 		return (0);
934 	}
935 	err = 0;
936 	while (!(HECI_FILE_CONNECTED == priv->state ||
937 	    HECI_FILE_DISCONNECTED == priv->state) &&
938 	    err != -1) {
939 		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
940 		    delta, TR_CLOCK_TICK);
941 	}
942 	if (HECI_FILE_CONNECTED != priv->state) {
943 		heci_remove_client_from_file_list(dev, priv->host_client_id);
944 		DBG("failed to connect client_id=%d state=%d.\n",
945 		    priv->host_client_id, priv->state);
946 		if (err)
947 			DBG("failed connect err=%08x\n", err);
948 		priv->state = HECI_FILE_DISCONNECTED;
949 		return (0);
950 	}
951 	DBG("successfully connected client_id=%d.\n",
952 	    priv->host_client_id);
953 	return (1);
954 }
955 
956 /*
957  * host_init_wd - heci initialization wd.
958  *
959  * @dev: Device object for our driver
960  *
961  */
962 static void host_init_wd(struct iamt_heci_device *dev)
963 {
964 
965 	mutex_enter(&dev->device_lock);
966 
967 	heci_init_file_private(&dev->wd_file_ext, NULL);
968 
969 	/* look for WD client and connect to it */
970 	dev->wd_file_ext.state = HECI_FILE_DISCONNECTED;
971 	dev->wd_timeout = 0;
972 
973 	if (dev->asf_mode) {
974 		(void) memcpy(dev->wd_data, stop_wd_params,
975 		    HECI_WD_PARAMS_SIZE);
976 	} else {
977 		/* AMT mode */
978 		dev->wd_timeout = AMT_WD_VALUE;
979 		DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
980 		(void) memcpy(dev->wd_data, start_wd_params,
981 		    HECI_WD_PARAMS_SIZE);
982 		(void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE,
983 		    &dev->wd_timeout, sizeof (uint16_t));
984 	}
985 
986 	/* find ME WD client */
987 	(void) heci_find_me_client(dev, &dev->wd_file_ext,
988 	    &heci_wd_guid, HECI_WD_HOST_CLIENT_ID);
989 
990 	DBG("check wd_file_ext\n");
991 	if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) {
992 		if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) {
993 			DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
994 			if (dev->wd_timeout != 0)
995 				dev->wd_due_counter = 1;
996 			else
997 				dev->wd_due_counter = 0;
998 			DBG("successfully connected to WD client.\n");
999 		}
1000 	} else
1001 		DBG("failed to find WD client.\n");
1002 
1003 
1004 	mutex_exit(&dev->device_lock);
1005 }
1006 
1007 
1008 /*
1009  * host_init_iamthif - heci initialization iamthif client.
1010  *
1011  * @dev: Device object for our driver
1012  *
1013  */
1014 static void
1015 host_init_iamthif(struct iamt_heci_device *dev)
1016 {
1017 	uint8_t i;
1018 
1019 	mutex_enter(&dev->device_lock);
1020 
1021 	heci_init_file_private(&dev->iamthif_file_ext, NULL);
1022 	dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED;
1023 
1024 	/* find ME PTHI client */
1025 	i = heci_find_me_client(dev, &dev->iamthif_file_ext,
1026 	    &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID);
1027 	if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) {
1028 		DBG("failed to find iamthif client.\n");
1029 		mutex_exit(&dev->device_lock);
1030 		return;
1031 	}
1032 
1033 	ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU);
1034 
1035 	if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) {
1036 		DBG("connected to iamthif client.\n");
1037 		dev->iamthif_state = HECI_IAMTHIF_IDLE;
1038 	}
1039 	mutex_exit(&dev->device_lock);
1040 }
1041 
1042 /*
1043  * heci_alloc_file_private - allocates a private file structure and set it up.
1044  * @file: the file structure
1045  *
1046  * @return  The allocated file or NULL on failure
1047  */
1048 struct heci_file_private *
1049 heci_alloc_file_private(struct heci_file *file)
1050 {
1051 	struct heci_file_private *priv;
1052 
1053 	priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP);
1054 	if (!priv)
1055 		return (NULL);
1056 
1057 	heci_init_file_private(priv, file);
1058 
1059 	return (priv);
1060 }
1061 
1062 /*
1063  * heci_free_file_private - free a private file structure that were previously
1064  * allocated by heci_alloc_file_private
1065  */
1066 void
1067 heci_free_file_private(struct heci_file_private *priv)
1068 {
1069 	mutex_destroy(&priv->file_lock);
1070 	mutex_destroy(&priv->read_io_lock);
1071 	mutex_destroy(&priv->write_io_lock);
1072 	cv_destroy(&priv->rx_wait);
1073 	kmem_free(priv, sizeof (struct heci_file_private));
1074 
1075 }
1076 
1077 /*
1078  * heci_disconnect_host_client  - send disconnect message  to fw from host
1079  * client.
1080  *
1081  * @dev: Device object for our driver
1082  * @file_ext: private data of the file object
1083  *
1084  * @return 0 on success, <0 on failure.
1085  */
1086 int
1087 heci_disconnect_host_client(struct iamt_heci_device *dev,
1088 		struct heci_file_private *file_ext)
1089 {
1090 	int rets, err;
1091 	long timeout = 15;	/* 15 seconds */
1092 	struct heci_cb_private *priv_cb;
1093 	clock_t delta = (clock_t)(timeout * HZ);
1094 
1095 	if ((!dev) || (!file_ext))
1096 		return (-ENODEV);
1097 
1098 	if (file_ext->state != HECI_FILE_DISCONNECTING)
1099 		return (0);
1100 
1101 	priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1102 	if (!priv_cb)
1103 		return (-ENOMEM);
1104 
1105 	LIST_INIT_HEAD(&priv_cb->cb_list);
1106 	priv_cb->file_private = file_ext;
1107 	priv_cb->major_file_operations = HECI_CLOSE;
1108 	mutex_enter(&dev->device_lock);
1109 	if (dev->host_buffer_is_empty) {
1110 		dev->host_buffer_is_empty = 0;
1111 		if (heci_disconnect(dev, file_ext)) {
1112 			list_add_tail(&priv_cb->cb_list,
1113 			    &dev->ctrl_rd_list.heci_cb.cb_list);
1114 		} else {
1115 			mutex_exit(&dev->device_lock);
1116 			rets = -ENODEV;
1117 			DBG("failed to call heci_disconnect.\n");
1118 			goto free;
1119 		}
1120 	} else {
1121 		DBG("add disconnect cb to control write list\n");
1122 		list_add_tail(&priv_cb->cb_list,
1123 		    &dev->ctrl_wr_list.heci_cb.cb_list);
1124 	}
1125 
1126 	err = 0;
1127 	while (err != -1 &&
1128 	    (HECI_FILE_DISCONNECTED != file_ext->state)) {
1129 
1130 		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
1131 		    delta, TR_CLOCK_TICK);
1132 	}
1133 	mutex_exit(&dev->device_lock);
1134 
1135 	if (HECI_FILE_DISCONNECTED == file_ext->state) {
1136 		rets = 0;
1137 		DBG("successfully disconnected from fw client."
1138 		    " me_client_id:%d, host_client_id:%d\n",
1139 		    file_ext->me_client_id,
1140 		    file_ext->host_client_id);
1141 	} else {
1142 		rets = -ENODEV;
1143 		if (HECI_FILE_DISCONNECTED != file_ext->state)
1144 			DBG("wrong status client disconnect.\n");
1145 
1146 		if (err)
1147 			DBG("wait failed disconnect err=%08x\n", err);
1148 
1149 		DBG("failed to disconnect from fw client.\n"
1150 		    " me_client_id:%d, host_client_id:%d\n",
1151 		    file_ext->me_client_id,
1152 		    file_ext->host_client_id);
1153 	}
1154 
1155 	mutex_enter(&dev->device_lock);
1156 	heci_flush_list(&dev->ctrl_rd_list, file_ext);
1157 	heci_flush_list(&dev->ctrl_wr_list, file_ext);
1158 	mutex_exit(&dev->device_lock);
1159 free:
1160 	heci_free_cb_private(priv_cb);
1161 	return (rets);
1162 }
1163 
1164 /*
1165  * heci_remove_client_from_file_list  -
1166  *	remove file private data from device file list
1167  *
1168  * @dev: Device object for our driver
1169  * @host_client_id: host client id to be removed
1170  *
1171  */
1172 void
1173 heci_remove_client_from_file_list(struct iamt_heci_device *dev,
1174 	uint8_t host_client_id)
1175 {
1176 	struct heci_file_private *file_pos = NULL;
1177 	struct heci_file_private *file_next = NULL;
1178 	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
1179 		struct heci_file_private) {
1180 		if (host_client_id == file_pos->host_client_id) {
1181 			DBG("remove host client = %d, ME client = %d\n",
1182 					file_pos->host_client_id,
1183 					file_pos->me_client_id);
1184 			list_del_init(&file_pos->link);
1185 			break;
1186 		}
1187 	}
1188 }
1189 
1190 /*
1191  * heci_fe_same_id - tell if file private data have same id
1192  *
1193  * @fe1: private data of 1. file object
1194  * @fe2: private data of 2. file object
1195  *
1196  * @return  !=0 - if ids are the same, 0 - if differ.
1197  */
1198 static inline int heci_fe_same_id(struct heci_file_private *fe1,
1199 		struct heci_file_private *fe2)
1200 {
1201 	return ((fe1->host_client_id == fe2->host_client_id) &&
1202 	    (fe1->me_client_id == fe2->me_client_id));
1203 }
1204