xref: /titanic_41/usr/src/uts/intel/io/heci/heci_init.c (revision d92fc07239af943d62e4e67791879707ec89e7d3)
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 		clock_t tm;
306 		tm = ddi_get_lbolt();
307 		err = cv_timedwait(&dev->wait_recvd_msg,
308 		    &dev->device_lock,
309 		    tm + HECI_INTEROP_TIMEOUT);
310 	}
311 
312 	if (err == -1 && !dev->recvd_msg) {
313 		dev->heci_state = HECI_DISABLED;
314 		DBG("wait_event_interruptible_timeout failed"
315 		    "on wait for ME to turn on ME_RDY.\n");
316 		mutex_exit(&dev->device_lock);
317 		return (-ENODEV);
318 	} else {
319 		if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
320 		    ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
321 			dev->heci_state = HECI_DISABLED;
322 			DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
323 			    dev->host_hw_state,
324 			    dev->me_hw_state);
325 
326 			if (!(dev->host_hw_state & H_RDY) != H_RDY)
327 				DBG("host turn off H_RDY.\n");
328 
329 			if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
330 				DBG("ME turn off ME_RDY.\n");
331 
332 			cmn_err(CE_WARN,
333 			    "heci: link layer initialization failed.\n");
334 			mutex_exit(&dev->device_lock);
335 			return (-ENODEV);
336 		}
337 	}
338 	dev->recvd_msg = 0;
339 	DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
340 	    dev->host_hw_state, dev->me_hw_state);
341 	DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
342 	DBG("heci: link layer has been established.\n");
343 	mutex_exit(&dev->device_lock);
344 	return (0);
345 }
346 
347 /*
348  * heci_hw_reset  - reset fw via heci csr register.
349  *
350  * @dev: Device object for our driver
351  * @interrupts: if interrupt should be enable after reset.
352  */
353 static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts)
354 {
355 	dev->host_hw_state |= (H_RST | H_IG);
356 
357 	if (interrupts)
358 		heci_csr_enable_interrupts(dev);
359 	else
360 		heci_csr_disable_interrupts(dev);
361 
362 }
363 
364 /*
365  * heci_reset  - reset host and fw.
366  *
367  * @dev: Device object for our driver
368  * @interrupts: if interrupt should be enable after reset.
369  */
370 void
371 heci_reset(struct iamt_heci_device *dev, int interrupts)
372 {
373 	struct heci_file_private *file_pos = NULL;
374 	struct heci_file_private *file_next = NULL;
375 	struct heci_cb_private *priv_cb_pos = NULL;
376 	struct heci_cb_private *priv_cb_next = NULL;
377 	int unexpected = 0;
378 
379 	if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
380 		dev->need_reset = 1;
381 		return;
382 	}
383 
384 	if (dev->heci_state != HECI_INITIALIZING &&
385 	    dev->heci_state != HECI_DISABLED &&
386 	    dev->heci_state != HECI_POWER_DOWN &&
387 	    dev->heci_state != HECI_POWER_UP)
388 		unexpected = 1;
389 
390 	if (dev->reinit_tsk != NULL) {
391 		mutex_exit(&dev->device_lock);
392 		(void) ddi_taskq_wait(dev->reinit_tsk);
393 		mutex_enter(&dev->device_lock);
394 	}
395 
396 	dev->host_hw_state = read_heci_register(dev, H_CSR);
397 
398 	DBG("before reset host_hw_state = 0x%08x.\n",
399 	    dev->host_hw_state);
400 
401 	heci_hw_reset(dev, interrupts);
402 
403 	dev->host_hw_state &= ~H_RST;
404 	dev->host_hw_state |= H_IG;
405 
406 	write_heci_register(dev, H_CSR, dev->host_hw_state);
407 
408 	DBG("currently saved host_hw_state = 0x%08x.\n",
409 	    dev->host_hw_state);
410 
411 	dev->need_reset = 0;
412 
413 	if (dev->heci_state != HECI_INITIALIZING) {
414 		if ((dev->heci_state != HECI_DISABLED) &&
415 		    (dev->heci_state != HECI_POWER_DOWN))
416 			dev->heci_state = HECI_RESETING;
417 
418 		list_for_each_entry_safe(file_pos,
419 		    file_next, &dev->file_list, link,
420 		    struct heci_file_private) {
421 			file_pos->state = HECI_FILE_DISCONNECTED;
422 			file_pos->flow_ctrl_creds = 0;
423 			file_pos->read_cb = NULL;
424 			file_pos->timer_count = 0;
425 		}
426 		/* remove entry if already in list */
427 		DBG("list del iamthif and wd file list.\n");
428 		heci_remove_client_from_file_list(dev,
429 		    dev->wd_file_ext.host_client_id);
430 
431 		heci_remove_client_from_file_list(dev,
432 		    dev->iamthif_file_ext.host_client_id);
433 
434 		heci_reset_iamthif_params(dev);
435 		dev->wd_due_counter = 0;
436 		dev->extra_write_index = 0;
437 	}
438 
439 	dev->num_heci_me_clients = 0;
440 	dev->rd_msg_hdr = 0;
441 	dev->stop = 0;
442 	dev->wd_pending = 0;
443 
444 	/* update the state of the registers after reset */
445 	dev->host_hw_state =  read_heci_register(dev, H_CSR);
446 	dev->me_hw_state =  read_heci_register(dev, ME_CSR_HA);
447 
448 	DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
449 	    dev->host_hw_state, dev->me_hw_state);
450 
451 	if (unexpected)
452 		cmn_err(CE_WARN, "unexpected heci reset.\n");
453 
454 	/* Wake up all readings so they can be interrupted */
455 	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
456 	    struct heci_file_private) {
457 		cmn_err(CE_NOTE, "heci: Waking up client!\n");
458 		cv_broadcast(&file_pos->rx_wait);
459 	}
460 	/* remove all waiting requests */
461 	if (dev->write_list.status == 0 &&
462 	    !list_empty(&dev->write_list.heci_cb.cb_list)) {
463 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
464 		    &dev->write_list.heci_cb.cb_list, cb_list,
465 		    struct heci_cb_private) {
466 			if (priv_cb_pos) {
467 				list_del(&priv_cb_pos->cb_list);
468 				heci_free_cb_private(priv_cb_pos);
469 			}
470 		}
471 	}
472 }
473 
474 /*
475  * heci_initialize_clients  -  routine.
476  *
477  * @dev: Device object for our driver
478  *
479  */
480 int
481 heci_initialize_clients(struct iamt_heci_device *dev)
482 {
483 	int status;
484 
485 	/* msleep(100) FW needs time to be ready to talk with us */
486 	delay(drv_usectohz(100000));
487 	DBG("link is established start sending messages.\n");
488 	/* link is established start sending messages. */
489 	status = host_start_message(dev);
490 	if (status != 0) {
491 		mutex_enter(&dev->device_lock);
492 		dev->heci_state = HECI_DISABLED;
493 		mutex_exit(&dev->device_lock);
494 		DBG("start sending messages failed.\n");
495 		return (status);
496 	}
497 	/* enumerate clients */
498 
499 	status = host_enum_clients_message(dev);
500 	if (status != 0) {
501 		mutex_enter(&dev->device_lock);
502 		dev->heci_state = HECI_DISABLED;
503 		mutex_exit(&dev->device_lock);
504 		DBG("enum clients failed.\n");
505 		return (status);
506 	}
507 	/* allocate storage for ME clients representation */
508 	status = allocate_me_clients_storage(dev);
509 	if (status != 0) {
510 		mutex_enter(&dev->device_lock);
511 		dev->num_heci_me_clients = 0;
512 		dev->heci_state = HECI_DISABLED;
513 		mutex_exit(&dev->device_lock);
514 		DBG("allocate clients failed.\n");
515 		return (status);
516 	}
517 
518 	heci_check_asf_mode(dev);
519 	/* heci initialization wd */
520 	host_init_wd(dev);
521 	/* heci initialization iamthif client */
522 	host_init_iamthif(dev);
523 
524 	mutex_enter(&dev->device_lock);
525 	if (dev->need_reset) {
526 		dev->need_reset = 0;
527 		dev->heci_state = HECI_DISABLED;
528 		mutex_exit(&dev->device_lock);
529 		return (-ENODEV);
530 	}
531 
532 	(void) memset(dev->heci_host_clients, 0,
533 	    sizeof (dev->heci_host_clients));
534 	dev->open_handle_count = 0;
535 	dev->heci_host_clients[0] |= 7;
536 	dev->current_host_client_id = 3;
537 	dev->heci_state = HECI_ENABLED;
538 	mutex_exit(&dev->device_lock);
539 	DBG("initialization heci clients successful.\n");
540 	return (0);
541 }
542 
543 /*
544  * heci_task_initialize_clients  -  routine.
545  *
546  * @data: Device object for our driver
547  *
548  */
549 void
550 heci_task_initialize_clients(void *data)
551 {
552 	int ret;
553 	struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
554 
555 	ret = heci_initialize_clients(dev);
556 	if (ret)
557 		cmn_err(CE_WARN, "heci_initialize_clients() failed\n");
558 }
559 
560 /*
561  * host_start_message - heci host send start message.
562  *
563  * @dev: Device object for our driver
564  *
565  * @return 0 on success, <0 on failure.
566  */
567 static int
568 host_start_message(struct iamt_heci_device *dev)
569 {
570 	long timeout = 60;	/* 60 second */
571 	struct heci_msg_hdr *heci_hdr;
572 	struct hbm_host_version_request *host_start_req;
573 	struct hbm_host_stop_request *host_stop_req;
574 	int err = 0;
575 
576 	/* host start message */
577 	mutex_enter(&dev->device_lock);
578 	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
579 	heci_hdr->host_addr = 0;
580 	heci_hdr->me_addr = 0;
581 	heci_hdr->length = sizeof (struct hbm_host_version_request);
582 	heci_hdr->msg_complete = 1;
583 	heci_hdr->reserved = 0;
584 
585 	host_start_req =
586 	    (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
587 	(void) memset(host_start_req, 0,
588 	    sizeof (struct hbm_host_version_request));
589 	host_start_req->cmd.cmd = HOST_START_REQ_CMD;
590 	host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
591 	host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
592 	dev->recvd_msg = 0;
593 	if (!heci_write_message(dev, heci_hdr,
594 	    (unsigned char *)(host_start_req),
595 	    heci_hdr->length)) {
596 		DBG("send version to fw fail.\n");
597 		mutex_exit(&dev->device_lock);
598 		return (-ENODEV);
599 	}
600 	DBG("call wait_event_interruptible_timeout for response message.\n");
601 	err = 0;
602 	while (err != -1 && !dev->recvd_msg) {
603 		clock_t tm;
604 		tm = ddi_get_lbolt();
605 		err = cv_timedwait(&dev->wait_recvd_msg,
606 		    &dev->device_lock, tm + timeout * HZ);
607 	}
608 	if (err == -1 && !dev->recvd_msg) {
609 		DBG("wait_timeout failed on host start response message.\n");
610 		mutex_exit(&dev->device_lock);
611 		return (-ENODEV);
612 	}
613 	dev->recvd_msg = 0;
614 	DBG("wait_timeout successful on host start response message.\n");
615 	if ((dev->version.major_version != HBM_MAJOR_VERSION) ||
616 	    (dev->version.minor_version != HBM_MINOR_VERSION)) {
617 		/* send stop message */
618 		heci_hdr->host_addr = 0;
619 		heci_hdr->me_addr = 0;
620 		heci_hdr->length = sizeof (struct hbm_host_stop_request);
621 		heci_hdr->msg_complete = 1;
622 		heci_hdr->reserved = 0;
623 
624 		host_stop_req =
625 		    (struct hbm_host_stop_request *)&dev->wr_msg_buf[1];
626 
627 		(void) memset(host_stop_req, 0,
628 		    sizeof (struct hbm_host_stop_request));
629 		host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
630 		host_stop_req->reason = DRIVER_STOP_REQUEST;
631 		if (!heci_write_message(dev, heci_hdr,
632 		    (unsigned char *)(host_stop_req),
633 		    heci_hdr->length)) {
634 			DBG("sending stop msg to fw failed.\n");
635 		}
636 		DBG("version mismatch.\n");
637 		mutex_exit(&dev->device_lock);
638 		return (-ENODEV);
639 	}
640 	mutex_exit(&dev->device_lock);
641 	return (0);
642 }
643 
644 /*
645  * host_enum_clients_message - host send enumeration client request message.
646  *
647  * @dev: Device object for our driver
648  * @return 0 on success, <0 on failure.
649  */
650 static int
651 host_enum_clients_message(struct iamt_heci_device *dev)
652 {
653 	long timeout = 5;	/* 5 second */
654 	struct heci_msg_hdr *heci_hdr;
655 	struct hbm_host_enum_request *host_enum_req;
656 	int err = 0;
657 	uint8_t i, j;
658 
659 	mutex_enter(&dev->device_lock);
660 
661 	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
662 	/* enumerate clients */
663 	heci_hdr->host_addr = 0;
664 	heci_hdr->me_addr = 0;
665 	heci_hdr->length = sizeof (struct hbm_host_enum_request);
666 	heci_hdr->msg_complete = 1;
667 	heci_hdr->reserved = 0;
668 
669 	host_enum_req = (struct hbm_host_enum_request *)&dev->wr_msg_buf[1];
670 	(void) memset(host_enum_req, 0, sizeof (struct hbm_host_enum_request));
671 	host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
672 	if (!heci_write_message(dev, heci_hdr,
673 	    (unsigned char *)(host_enum_req),
674 	    heci_hdr->length)) {
675 		DBG("send enumeration request failed.\n");
676 		mutex_exit(&dev->device_lock);
677 		return (-ENODEV);
678 	}
679 	/* wait for response */
680 	dev->recvd_msg = 0;
681 	err = 0;
682 	while (!dev->recvd_msg && err != -1) {
683 		clock_t tm;
684 		tm = ddi_get_lbolt();
685 		err = cv_timedwait(&dev->wait_recvd_msg,
686 		    &dev->device_lock,
687 		    tm + timeout * HZ);
688 	}
689 	if (err == -1 && !dev->recvd_msg) {
690 		DBG("wait_event_interruptible_timeout failed "
691 		"on enumeration clients response message.\n");
692 		mutex_exit(&dev->device_lock);
693 		return (-ENODEV);
694 	}
695 	dev->recvd_msg = 0;
696 
697 	/* count how many ME clients we have */
698 	for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
699 		for (j = 0; j < 8; j++) {
700 			if ((dev->heci_me_clients[i] & (1 << j)) != 0)
701 				dev->num_heci_me_clients++;
702 
703 		}
704 	}
705 	mutex_exit(&dev->device_lock);
706 
707 	return (0);
708 }
709 
710 /*
711  * host_client_properties - reads properties for client
712  *
713  * @dev: Device object for our driver
714  * @idx: client index in me client array
715  * @client_id: id of the client
716  *
717  * @return 0 on success, <0 on failure.
718  */
719 static int
720 host_client_properties(struct iamt_heci_device *dev,
721 	struct heci_me_client *client)
722 {
723 	struct heci_msg_hdr *heci_hdr;
724 	struct hbm_props_request *host_cli_req;
725 	int err;
726 
727 	mutex_enter(&dev->device_lock);
728 	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
729 	heci_hdr->host_addr = 0;
730 	heci_hdr->me_addr = 0;
731 	heci_hdr->length = sizeof (struct hbm_props_request);
732 	heci_hdr->msg_complete = 1;
733 	heci_hdr->reserved = 0;
734 
735 	host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
736 	(void) memset(host_cli_req, 0, sizeof (struct hbm_props_request));
737 	host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD;
738 	host_cli_req->address = client->client_id;
739 	if (!heci_write_message(dev, heci_hdr,
740 	    (unsigned char *)(host_cli_req), heci_hdr->length)) {
741 		DBG("send props request failed.\n");
742 		mutex_exit(&dev->device_lock);
743 		return (-ENODEV);
744 	}
745 	/* wait for response */
746 	dev->recvd_msg = 0;
747 
748 	err = 0;
749 	while (!dev->recvd_msg && err != -1) {
750 		clock_t tm;
751 		tm = ddi_get_lbolt();
752 		err = cv_timedwait(&dev->wait_recvd_msg,
753 		    &dev->device_lock,
754 		    tm + 10 * HZ);
755 	}
756 	if (err == -1 && !dev->recvd_msg) {
757 		DBG("wait failed on props resp msg.\n");
758 		mutex_exit(&dev->device_lock);
759 		return (-ENODEV);
760 	}
761 	dev->recvd_msg = 0;
762 	mutex_exit(&dev->device_lock);
763 	return (0);
764 }
765 
766 /*
767  * allocate_me_clients_storage - allocate storage for me clients
768  *
769  * @dev: Device object for our driver
770  *
771  * @return 0 on success, <0 on failure.
772  */
773 static int
774 allocate_me_clients_storage(struct iamt_heci_device *dev)
775 {
776 	struct heci_me_client *clients;
777 	struct heci_me_client *client;
778 	uint8_t num, i, j;
779 	int err;
780 
781 	if (dev->num_heci_me_clients == 0)
782 		return (0);
783 
784 	mutex_enter(&dev->device_lock);
785 	if (dev->me_clients) {
786 		kmem_free(dev->me_clients, dev->num_heci_me_clients*
787 		    sizeof (struct heci_me_client));
788 		dev->me_clients = NULL;
789 	}
790 	mutex_exit(&dev->device_lock);
791 
792 	/* allocate storage for ME clients representation */
793 	clients = kmem_zalloc(dev->num_heci_me_clients*
794 	    sizeof (struct heci_me_client), KM_SLEEP);
795 	if (!clients) {
796 		DBG("memory allocation for ME clients failed.\n");
797 		return (-ENOMEM);
798 	}
799 
800 	mutex_enter(&dev->device_lock);
801 	dev->me_clients = clients;
802 	mutex_exit(&dev->device_lock);
803 
804 	num = 0;
805 	for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
806 		for (j = 0; j < 8; j++) {
807 			if ((dev->heci_me_clients[i] & (1 << j)) != 0) {
808 				client = &dev->me_clients[num];
809 				client->client_id = (i * 8) + j;
810 				client->flow_ctrl_creds = 0;
811 				err = host_client_properties(dev, client);
812 				if (err != 0) {
813 					mutex_enter(&dev->device_lock);
814 					kmem_free(dev->me_clients,
815 					    dev->num_heci_me_clients*
816 					    sizeof (struct heci_me_client));
817 					dev->me_clients = NULL;
818 					mutex_exit(&dev->device_lock);
819 					return (err);
820 				}
821 				num++;
822 			}
823 		}
824 	}
825 
826 	return (0);
827 }
828 
829 /*
830  * heci_init_file_private - initializes private file structure.
831  *
832  * @priv: private file structure to be initialized
833  * @file: the file structure
834  *
835  */
836 static void
837 heci_init_file_private(struct heci_file_private *priv,
838 	struct heci_file *file)
839 {
840 	_NOTE(ARGUNUSED(file));
841 
842 	(void) memset(priv, 0, sizeof (struct heci_file_private));
843 	mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL);
844 	mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL);
845 	mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL);
846 	cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL);
847 	DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait);
848 	LIST_INIT_HEAD(&priv->link);
849 	priv->reading_state = HECI_IDLE;
850 	priv->writing_state = HECI_IDLE;
851 }
852 
853 /*
854  * heci_find_me_client - search for ME client guid
855  *                       sets client_id in heci_file_private if found
856  * @dev: Device object for our driver
857  * @priv: private file structure to set client_id in
858  * @cguid: searched guid of ME client
859  * @client_id: id of host client to be set in file private structure
860  *
861  * @return ME client index
862  */
863 static uint8_t
864 heci_find_me_client(struct iamt_heci_device *dev,
865 				struct heci_file_private *priv,
866 				const struct guid *cguid, uint8_t client_id)
867 {
868 	uint8_t i;
869 
870 	if ((dev == NULL) || (priv == NULL) || (cguid == NULL))
871 		return (0);
872 
873 	for (i = 0; i < dev->num_heci_me_clients; i++) {
874 		if (memcmp(cguid,
875 		    &dev->me_clients[i].props.protocol_name,
876 		    sizeof (struct guid)) == 0) {
877 			priv->me_client_id = dev->me_clients[i].client_id;
878 			priv->state = HECI_FILE_CONNECTING;
879 			priv->host_client_id = client_id;
880 
881 			list_add_tail(&priv->link, &dev->file_list);
882 			return (i);
883 		}
884 	}
885 	return (0);
886 }
887 
888 /*
889  * heci_check_asf_mode - check for ASF client
890  *
891  * @dev: Device object for our driver
892  *
893  */
894 static void
895 heci_check_asf_mode(struct iamt_heci_device *dev)
896 {
897 	uint8_t i;
898 
899 	mutex_enter(&dev->device_lock);
900 	dev->asf_mode = 0;
901 	/* find ME ASF client - otherwise assume AMT mode */
902 	DBG("find ME ASF client - otherwise assume AMT mode.\n");
903 	for (i = 0; i < dev->num_heci_me_clients; i++) {
904 		if (memcmp(&heci_asf_guid,
905 		    &dev->me_clients[i].props.protocol_name,
906 		    sizeof (struct guid)) == 0) {
907 			dev->asf_mode = 1;
908 			mutex_exit(&dev->device_lock);
909 			DBG("found ME ASF client.\n");
910 			return;
911 		}
912 	}
913 	mutex_exit(&dev->device_lock);
914 	DBG("assume AMT mode.\n");
915 }
916 
917 /*
918  * heci_connect_me_client - connect ME client
919  * @dev: Device object for our driver
920  * @priv: private file structure
921  * @timeout: connect timeout in seconds
922  *
923  * @return 1 - if connected, 0 - if not
924  */
925 static uint8_t
926 heci_connect_me_client(struct iamt_heci_device *dev,
927 	struct heci_file_private *priv,
928 	long timeout)
929 {
930 	int err = 0;
931 
932 	if ((dev == NULL) || (priv == NULL))
933 		return (0);
934 
935 	if (!heci_connect(dev, priv)) {
936 		DBG("failed to call heci_connect for client_id=%d.\n",
937 		    priv->host_client_id);
938 		heci_remove_client_from_file_list(dev, priv->host_client_id);
939 		priv->state = HECI_FILE_DISCONNECTED;
940 		return (0);
941 	}
942 	err = 0;
943 	while (!(HECI_FILE_CONNECTED == priv->state ||
944 	    HECI_FILE_DISCONNECTED == priv->state) &&
945 	    err != -1) {
946 		clock_t tm;
947 		tm = ddi_get_lbolt();
948 		err = cv_timedwait(&dev->wait_recvd_msg,
949 		    &dev->device_lock,
950 		    tm + timeout*HZ);
951 	}
952 	if (HECI_FILE_CONNECTED != priv->state) {
953 		heci_remove_client_from_file_list(dev, priv->host_client_id);
954 		DBG("failed to connect client_id=%d state=%d.\n",
955 		    priv->host_client_id, priv->state);
956 		if (err)
957 			DBG("failed connect err=%08x\n", err);
958 		priv->state = HECI_FILE_DISCONNECTED;
959 		return (0);
960 	}
961 	DBG("successfully connected client_id=%d.\n",
962 	    priv->host_client_id);
963 	return (1);
964 }
965 
966 /*
967  * host_init_wd - heci initialization wd.
968  *
969  * @dev: Device object for our driver
970  *
971  */
972 static void host_init_wd(struct iamt_heci_device *dev)
973 {
974 
975 	mutex_enter(&dev->device_lock);
976 
977 	heci_init_file_private(&dev->wd_file_ext, NULL);
978 
979 	/* look for WD client and connect to it */
980 	dev->wd_file_ext.state = HECI_FILE_DISCONNECTED;
981 	dev->wd_timeout = 0;
982 
983 	if (dev->asf_mode) {
984 		(void) memcpy(dev->wd_data, stop_wd_params,
985 		    HECI_WD_PARAMS_SIZE);
986 	} else {
987 		/* AMT mode */
988 		dev->wd_timeout = AMT_WD_VALUE;
989 		DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
990 		(void) memcpy(dev->wd_data, start_wd_params,
991 		    HECI_WD_PARAMS_SIZE);
992 		(void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE,
993 		    &dev->wd_timeout, sizeof (uint16_t));
994 	}
995 
996 	/* find ME WD client */
997 	(void) heci_find_me_client(dev, &dev->wd_file_ext,
998 	    &heci_wd_guid, HECI_WD_HOST_CLIENT_ID);
999 
1000 	DBG("check wd_file_ext\n");
1001 	if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) {
1002 		if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) {
1003 			DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
1004 			if (dev->wd_timeout != 0)
1005 				dev->wd_due_counter = 1;
1006 			else
1007 				dev->wd_due_counter = 0;
1008 			DBG("successfully connected to WD client.\n");
1009 		}
1010 	} else
1011 		DBG("failed to find WD client.\n");
1012 
1013 
1014 	mutex_exit(&dev->device_lock);
1015 }
1016 
1017 
1018 /*
1019  * host_init_iamthif - heci initialization iamthif client.
1020  *
1021  * @dev: Device object for our driver
1022  *
1023  */
1024 static void
1025 host_init_iamthif(struct iamt_heci_device *dev)
1026 {
1027 	uint8_t i;
1028 
1029 	mutex_enter(&dev->device_lock);
1030 
1031 	heci_init_file_private(&dev->iamthif_file_ext, NULL);
1032 	dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED;
1033 
1034 	/* find ME PTHI client */
1035 	i = heci_find_me_client(dev, &dev->iamthif_file_ext,
1036 	    &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID);
1037 	if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) {
1038 		DBG("failed to find iamthif client.\n");
1039 		mutex_exit(&dev->device_lock);
1040 		return;
1041 	}
1042 
1043 	ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU);
1044 
1045 	if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) {
1046 		DBG("connected to iamthif client.\n");
1047 		dev->iamthif_state = HECI_IAMTHIF_IDLE;
1048 	}
1049 	mutex_exit(&dev->device_lock);
1050 }
1051 
1052 /*
1053  * heci_alloc_file_private - allocates a private file structure and set it up.
1054  * @file: the file structure
1055  *
1056  * @return  The allocated file or NULL on failure
1057  */
1058 struct heci_file_private *
1059 heci_alloc_file_private(struct heci_file *file)
1060 {
1061 	struct heci_file_private *priv;
1062 
1063 	priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP);
1064 	if (!priv)
1065 		return (NULL);
1066 
1067 	heci_init_file_private(priv, file);
1068 
1069 	return (priv);
1070 }
1071 
1072 /*
1073  * heci_free_file_private - free a private file structure that were previously
1074  * allocated by heci_alloc_file_private
1075  */
1076 void
1077 heci_free_file_private(struct heci_file_private *priv)
1078 {
1079 	mutex_destroy(&priv->file_lock);
1080 	mutex_destroy(&priv->read_io_lock);
1081 	mutex_destroy(&priv->write_io_lock);
1082 	cv_destroy(&priv->rx_wait);
1083 	kmem_free(priv, sizeof (struct heci_file_private));
1084 
1085 }
1086 
1087 /*
1088  * heci_disconnect_host_client  - send disconnect message  to fw from host
1089  * client.
1090  *
1091  * @dev: Device object for our driver
1092  * @file_ext: private data of the file object
1093  *
1094  * @return 0 on success, <0 on failure.
1095  */
1096 int
1097 heci_disconnect_host_client(struct iamt_heci_device *dev,
1098 		struct heci_file_private *file_ext)
1099 {
1100 	int rets, err;
1101 	long timeout = 15;	/* 15 seconds */
1102 	struct heci_cb_private *priv_cb;
1103 
1104 	if ((!dev) || (!file_ext))
1105 		return (-ENODEV);
1106 
1107 	if (file_ext->state != HECI_FILE_DISCONNECTING)
1108 		return (0);
1109 
1110 	priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1111 	if (!priv_cb)
1112 		return (-ENOMEM);
1113 
1114 	LIST_INIT_HEAD(&priv_cb->cb_list);
1115 	priv_cb->file_private = file_ext;
1116 	priv_cb->major_file_operations = HECI_CLOSE;
1117 	mutex_enter(&dev->device_lock);
1118 	if (dev->host_buffer_is_empty) {
1119 		dev->host_buffer_is_empty = 0;
1120 		if (heci_disconnect(dev, file_ext)) {
1121 			list_add_tail(&priv_cb->cb_list,
1122 			    &dev->ctrl_rd_list.heci_cb.cb_list);
1123 		} else {
1124 			mutex_exit(&dev->device_lock);
1125 			rets = -ENODEV;
1126 			DBG("failed to call heci_disconnect.\n");
1127 			goto free;
1128 		}
1129 	} else {
1130 		DBG("add disconnect cb to control write list\n");
1131 		list_add_tail(&priv_cb->cb_list,
1132 		    &dev->ctrl_wr_list.heci_cb.cb_list);
1133 	}
1134 
1135 	err = 0;
1136 	while (err != -1 &&
1137 	    (HECI_FILE_DISCONNECTED != file_ext->state)) {
1138 
1139 	clock_t tm;
1140 	tm = ddi_get_lbolt();
1141 	err = cv_timedwait(&dev->wait_recvd_msg,
1142 	    &dev->device_lock,
1143 	    tm + timeout * HZ);
1144 	}
1145 	mutex_exit(&dev->device_lock);
1146 
1147 	if (HECI_FILE_DISCONNECTED == file_ext->state) {
1148 		rets = 0;
1149 		DBG("successfully disconnected from fw client."
1150 		    " me_client_id:%d, host_client_id:%d\n",
1151 		    file_ext->me_client_id,
1152 		    file_ext->host_client_id);
1153 	} else {
1154 		rets = -ENODEV;
1155 		if (HECI_FILE_DISCONNECTED != file_ext->state)
1156 			DBG("wrong status client disconnect.\n");
1157 
1158 		if (err)
1159 			DBG("wait failed disconnect err=%08x\n", err);
1160 
1161 		DBG("failed to disconnect from fw client.\n"
1162 		    " me_client_id:%d, host_client_id:%d\n",
1163 		    file_ext->me_client_id,
1164 		    file_ext->host_client_id);
1165 	}
1166 
1167 	mutex_enter(&dev->device_lock);
1168 	heci_flush_list(&dev->ctrl_rd_list, file_ext);
1169 	heci_flush_list(&dev->ctrl_wr_list, file_ext);
1170 	mutex_exit(&dev->device_lock);
1171 free:
1172 	heci_free_cb_private(priv_cb);
1173 	return (rets);
1174 }
1175 
1176 /*
1177  * heci_remove_client_from_file_list  -
1178  *	remove file private data from device file list
1179  *
1180  * @dev: Device object for our driver
1181  * @host_client_id: host client id to be removed
1182  *
1183  */
1184 void
1185 heci_remove_client_from_file_list(struct iamt_heci_device *dev,
1186 	uint8_t host_client_id)
1187 {
1188 	struct heci_file_private *file_pos = NULL;
1189 	struct heci_file_private *file_next = NULL;
1190 	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
1191 		struct heci_file_private) {
1192 		if (host_client_id == file_pos->host_client_id) {
1193 			DBG("remove host client = %d, ME client = %d\n",
1194 					file_pos->host_client_id,
1195 					file_pos->me_client_id);
1196 			list_del_init(&file_pos->link);
1197 			break;
1198 		}
1199 	}
1200 }
1201 
1202 /*
1203  * heci_fe_same_id - tell if file private data have same id
1204  *
1205  * @fe1: private data of 1. file object
1206  * @fe2: private data of 2. file object
1207  *
1208  * @return  !=0 - if ids are the same, 0 - if differ.
1209  */
1210 static inline int heci_fe_same_id(struct heci_file_private *fe1,
1211 		struct heci_file_private *fe2)
1212 {
1213 	return ((fe1->host_client_id == fe2->host_client_id) &&
1214 	    (fe1->me_client_id == fe2->me_client_id));
1215 }
1216