191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/*
291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * Intel Management Engine Interface (Intel MEI) Linux driver
4733ba91cc0d5b0a3cc012431b8c5b354697b57c1Tomas Winkler * Copyright (c) 2003-2012, Intel Corporation.
591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * This program is free software; you can redistribute it and/or modify it
791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * under the terms and conditions of the GNU General Public License,
891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * version 2, as published by the Free Software Foundation.
991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
1091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * This program is distributed in the hope it will be useful, but WITHOUT
1191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * more details.
1491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
1591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
1691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
1791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include <linux/pci.h>
1891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include <linux/sched.h>
1991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include <linux/wait.h>
2091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include <linux/delay.h>
2191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
2291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include "mei_dev.h"
2391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include "hw.h"
2491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include "interface.h"
2591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil#include "mei.h"
2691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
2791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilconst uuid_le mei_amthi_guid  = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
2891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil						0xa8, 0x46, 0xe0, 0xff, 0x65,
2991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil						0x81, 0x4c);
3091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
3191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
320288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler * mei_io_list_init - Sets up a queue list.
3391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
340288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler * @list: An instance io list structure
3591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
3691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
370288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winklervoid mei_io_list_init(struct mei_io_list *list)
3891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
3991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* initialize our queue list */
4091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	INIT_LIST_HEAD(&list->mei_cb.cb_list);
4191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
4291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
4391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
440288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler * mei_io_list_flush - removes list entry belonging to cl.
4591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
4691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @list:  An instance of our list structure
4791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @cl: private data of the file object
4891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
490288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winklervoid mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl)
5091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
513a5352fc9d316fe1be18daf80571e80805e06f11Tomas Winkler	struct mei_cl_cb *pos;
523a5352fc9d316fe1be18daf80571e80805e06f11Tomas Winkler	struct mei_cl_cb *next;
5391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
54b7cd2d9fdc0185e0f4fede78f2a040c5f7c5a179Tomas Winkler	list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) {
553a5352fc9d316fe1be18daf80571e80805e06f11Tomas Winkler		if (pos->file_private) {
560288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler			struct mei_cl *cl_tmp;
57b7cd2d9fdc0185e0f4fede78f2a040c5f7c5a179Tomas Winkler			cl_tmp = (struct mei_cl *)pos->file_private;
580288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler			if (mei_cl_cmp_id(cl, cl_tmp))
59b7cd2d9fdc0185e0f4fede78f2a040c5f7c5a179Tomas Winkler				list_del(&pos->cb_list);
6091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
6191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
6291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
630288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler/**
640288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl.
650288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler *
660288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler * @dev: the device structure
670288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler * @cl: private data of the file object
680288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler */
690288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winklerint mei_cl_flush_queues(struct mei_cl *cl)
700288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler{
710288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	if (!cl || !cl->dev)
720288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler		return -EINVAL;
730288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler
740288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n");
750288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->read_list, cl);
760288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->write_list, cl);
770288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->write_waiting_list, cl);
780288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
790288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
800288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->amthi_cmd_list, cl);
810288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&cl->dev->amthi_read_complete_list, cl);
820288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	return 0;
830288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler}
840288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler
850288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler
8691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
8791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
8891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_reset_iamthif_params - initializes mei device iamthif
8991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
9091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
9191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
9291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilstatic void mei_reset_iamthif_params(struct mei_device *dev)
9391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
9491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* reset iamthif parameters. */
9591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_current_cb = NULL;
9691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_msg_buf_size = 0;
9791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_msg_buf_index = 0;
98eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->iamthif_canceled = false;
99eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->iamthif_ioctl = false;
10091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_state = MEI_IAMTHIF_IDLE;
10191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_timer = 0;
10291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
10391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
10491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
10591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * init_mei_device - allocates and initializes the mei device structure
10691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
10791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @pdev: The pci device structure
10891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
10991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns The mei_device_device pointer on success, NULL on failure.
11091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
111c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklerstruct mei_device *mei_device_init(struct pci_dev *pdev)
11291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
11391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_device *dev;
11491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
11591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL);
11691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!dev)
11791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return NULL;
11891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
11991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* setup our list array */
12091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	INIT_LIST_HEAD(&dev->file_list);
12191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	INIT_LIST_HEAD(&dev->wd_cl.link);
12291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	INIT_LIST_HEAD(&dev->iamthif_cl.link);
12391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mutex_init(&dev->device_lock);
12491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	init_waitqueue_head(&dev->wait_recvd_msg);
12591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	init_waitqueue_head(&dev->wait_stop_wd);
12691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->mei_state = MEI_INITIALIZING;
12791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_state = MEI_IAMTHIF_IDLE;
1289ce178e539e0511571a9517e63980e68427b5585Oren Weil	dev->wd_interface_reg = false;
1290288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler
1300288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler
1310288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->read_list);
1320288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->write_list);
1330288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->write_waiting_list);
1340288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->ctrl_wr_list);
1350288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->ctrl_rd_list);
1360288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->amthi_cmd_list);
1370288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_init(&dev->amthi_read_complete_list);
13891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->pdev = pdev;
13991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return dev;
14091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
14191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
14291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
14391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_hw_init - initializes host and fw to start work.
14491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
14591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
14691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
14791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns 0 on success, <0 on failure.
14891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
14991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilint mei_hw_init(struct mei_device *dev)
15091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
15191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int err = 0;
15291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int ret;
15391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
15491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mutex_lock(&dev->device_lock);
15591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
15691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->host_hw_state = mei_hcsr_read(dev);
15791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->me_hw_state = mei_mecsr_read(dev);
15891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, mestate = 0x%08x.\n",
15991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->host_hw_state, dev->me_hw_state);
16091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
16191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* acknowledge interrupt and stop interupts */
16291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if ((dev->host_hw_state & H_IS) == H_IS)
16391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_reg_write(dev, H_CSR, dev->host_hw_state);
16491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
165eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->recvd_msg = false;
16691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
16791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
16891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_reset(dev, 1);
16991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
17091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
17191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->host_hw_state, dev->me_hw_state);
17291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
17391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* wait for ME to turn on ME_RDY */
17491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!dev->recvd_msg) {
17591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mutex_unlock(&dev->device_lock);
17691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		err = wait_event_interruptible_timeout(dev->wait_recvd_msg,
17791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->recvd_msg, MEI_INTEROP_TIMEOUT);
17891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mutex_lock(&dev->device_lock);
17991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
18091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
181a534bb6eea72c0d082dd2faab85450e5554ba1c8Tomas Winkler	if (err <= 0 && !dev->recvd_msg) {
18291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->mei_state = MEI_DISABLED;
18391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev,
18491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			"wait_event_interruptible_timeout failed"
18591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			"on wait for ME to turn on ME_RDY.\n");
18691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		ret = -ENODEV;
18791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		goto out;
18891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
18991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
19091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
19191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	      ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
19291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->mei_state = MEI_DISABLED;
19391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev,
19491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			"host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
19591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->host_hw_state, dev->me_hw_state);
19691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
1978eb73c6c2675f708283822bd058ad1e416836ba2Dan Carpenter		if (!(dev->host_hw_state & H_RDY))
19891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n");
19991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
2008eb73c6c2675f708283822bd058ad1e416836ba2Dan Carpenter		if (!(dev->me_hw_state & ME_RDY_HRA))
20191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n");
20291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
20391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		printk(KERN_ERR "mei: link layer initialization failed.\n");
20491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		ret = -ENODEV;
20591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		goto out;
20691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
20791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
20891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (dev->version.major_version != HBM_MAJOR_VERSION ||
20991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->version.minor_version != HBM_MINOR_VERSION) {
21091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
21191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		ret = -ENODEV;
21291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		goto out;
21391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
21491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
215eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->recvd_msg = false;
21691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
21791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->host_hw_state, dev->me_hw_state);
21891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n");
21991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
22091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "MEI  start success.\n");
22191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	ret = 0;
22291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
22391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilout:
22491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mutex_unlock(&dev->device_lock);
22591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return ret;
22691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
22791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
22891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
22991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_hw_reset - resets fw via mei csr register.
23091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
23191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
23291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @interrupts_enabled: if interrupt should be enabled after reset.
23391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
23491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilstatic void mei_hw_reset(struct mei_device *dev, int interrupts_enabled)
23591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
23691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->host_hw_state |= (H_RST | H_IG);
23791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
23891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (interrupts_enabled)
23991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_enable_interrupts(dev);
24091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	else
24191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_disable_interrupts(dev);
24291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
24391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
24491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
24591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_reset - resets host and fw.
24691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
24791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
24891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @interrupts_enabled: if interrupt should be enabled after reset.
24991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
25091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilvoid mei_reset(struct mei_device *dev, int interrupts_enabled)
25191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
25291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl *cl_pos = NULL;
25391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl *cl_next = NULL;
25491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl_cb *cb_pos = NULL;
25591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl_cb *cb_next = NULL;
25691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	bool unexpected;
25791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
25891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (dev->mei_state == MEI_RECOVERING_FROM_RESET) {
259eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler		dev->need_reset = true;
26091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return;
26191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
26291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
26391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	unexpected = (dev->mei_state != MEI_INITIALIZING &&
26491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->mei_state != MEI_DISABLED &&
26591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->mei_state != MEI_POWER_DOWN &&
26691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->mei_state != MEI_POWER_UP);
26791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
26891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->host_hw_state = mei_hcsr_read(dev);
26991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
27091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "before reset host_hw_state = 0x%08x.\n",
27191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->host_hw_state);
27291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
27391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hw_reset(dev, interrupts_enabled);
27491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
27591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->host_hw_state &= ~H_RST;
27691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->host_hw_state |= H_IG;
27791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
27891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hcsr_set(dev);
27991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
28091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n",
28191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->host_hw_state);
28291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
283eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->need_reset = false;
28491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
28591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (dev->mei_state != MEI_INITIALIZING) {
28691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (dev->mei_state != MEI_DISABLED &&
28791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		    dev->mei_state != MEI_POWER_DOWN)
28891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->mei_state = MEI_RESETING;
28991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
29091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		list_for_each_entry_safe(cl_pos,
29191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				cl_next, &dev->file_list, link) {
29291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			cl_pos->state = MEI_FILE_DISCONNECTED;
29391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			cl_pos->mei_flow_ctrl_creds = 0;
29491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			cl_pos->read_cb = NULL;
29591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			cl_pos->timer_count = 0;
29691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
29791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		/* remove entry if already in list */
29891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n");
29991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_remove_client_from_file_list(dev,
30091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				dev->wd_cl.host_client_id);
30191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
30291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_remove_client_from_file_list(dev,
30391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				dev->iamthif_cl.host_client_id);
30491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
30591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_reset_iamthif_params(dev);
30691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->wd_due_counter = 0;
30791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->extra_write_index = 0;
30891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
30991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
310cf9673dad4dd76ecdccd265809921ceed752f19eTomas Winkler	dev->me_clients_num = 0;
31191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->rd_msg_hdr = 0;
312eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->stop = false;
313eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->wd_pending = false;
31491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
31591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* update the state of the registers after reset */
31691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->host_hw_state = mei_hcsr_read(dev);
31791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->me_hw_state = mei_mecsr_read(dev);
31891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
31991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
32091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    dev->host_hw_state, dev->me_hw_state);
32191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
32291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (unexpected)
32391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_warn(&dev->pdev->dev, "unexpected reset.\n");
32491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
32591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* Wake up all readings so they can be interrupted */
32691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
32791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (waitqueue_active(&cl_pos->rx_wait)) {
32891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "Waking up client!\n");
32991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			wake_up_interruptible(&cl_pos->rx_wait);
33091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
33191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
33291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* remove all waiting requests */
333b7cd2d9fdc0185e0f4fede78f2a040c5f7c5a179Tomas Winkler	list_for_each_entry_safe(cb_pos, cb_next,
334b7cd2d9fdc0185e0f4fede78f2a040c5f7c5a179Tomas Winkler			&dev->write_list.mei_cb.cb_list, cb_list) {
3353a5352fc9d316fe1be18daf80571e80805e06f11Tomas Winkler		list_del(&cb_pos->cb_list);
3363a5352fc9d316fe1be18daf80571e80805e06f11Tomas Winkler		mei_free_cb_private(cb_pos);
33791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
33891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
33991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
34091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
34191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
34291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
34391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * host_start_message - mei host sends start message.
34491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
34591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
34691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
34791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns none.
34891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
349c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklervoid mei_host_start_message(struct mei_device *dev)
35091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
35191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_msg_hdr *mei_hdr;
35291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct hbm_host_version_request *host_start_req;
35391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
35491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* host start message */
35591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
35691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->host_addr = 0;
35791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->me_addr = 0;
35891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->length = sizeof(struct hbm_host_version_request);
35991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->msg_complete = 1;
36091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->reserved = 0;
36191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
36291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	host_start_req =
36391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	    (struct hbm_host_version_request *) &dev->wr_msg_buf[1];
36491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	memset(host_start_req, 0, sizeof(struct hbm_host_version_request));
3651ca7e782864222bcdbe305e543df25df4e343963Tomas Winkler	host_start_req->hbm_cmd = HOST_START_REQ_CMD;
36691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
36791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
368eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler	dev->recvd_msg = false;
3691ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler	if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req,
37091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				       mei_hdr->length)) {
37191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
37291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->mei_state = MEI_RESETING;
37391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_reset(dev, 1);
37491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
37591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->init_clients_state = MEI_START_MESSAGE;
37691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
37791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return ;
37891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
37991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
38091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
38191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * host_enum_clients_message - host sends enumeration client request message.
38291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
38391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
38491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
38591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns none.
38691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
387c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklervoid mei_host_enum_clients_message(struct mei_device *dev)
38891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
38991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_msg_hdr *mei_hdr;
39091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct hbm_host_enum_request *host_enum_req;
39191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
39291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* enumerate clients */
39391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->host_addr = 0;
39491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->me_addr = 0;
39591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->length = sizeof(struct hbm_host_enum_request);
39691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->msg_complete = 1;
39791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_hdr->reserved = 0;
39891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
39991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
40091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request));
4011ca7e782864222bcdbe305e543df25df4e343963Tomas Winkler	host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
4021ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler	if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req,
40391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				mei_hdr->length)) {
40491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->mei_state = MEI_RESETING;
40591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
40691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_reset(dev, 1);
40791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
40891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
40991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
4101ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler	return;
41191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
41291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
41391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
41491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
41591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * allocate_me_clients_storage - allocates storage for me clients
41691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
41791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
41891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
41991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns none.
42091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
421c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklervoid mei_allocate_me_clients_storage(struct mei_device *dev)
42291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
42391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_me_client *clients;
42491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int b;
42591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
42691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* count how many ME clients we have */
42791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
428cf9673dad4dd76ecdccd265809921ceed752f19eTomas Winkler		dev->me_clients_num++;
42991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
430cf9673dad4dd76ecdccd265809921ceed752f19eTomas Winkler	if (dev->me_clients_num <= 0)
43191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return ;
43291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
43391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
43491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (dev->me_clients != NULL) {
43591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		kfree(dev->me_clients);
43691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->me_clients = NULL;
43791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
43891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n",
439cf9673dad4dd76ecdccd265809921ceed752f19eTomas Winkler		dev->me_clients_num * sizeof(struct mei_me_client));
44091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* allocate storage for ME clients representation */
441cf9673dad4dd76ecdccd265809921ceed752f19eTomas Winkler	clients = kcalloc(dev->me_clients_num,
44291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			sizeof(struct mei_me_client), GFP_KERNEL);
44391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!clients) {
44491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
44591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->mei_state = MEI_RESETING;
44691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_reset(dev, 1);
44791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return ;
44891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
44991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->me_clients = clients;
45091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return ;
45191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
45291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
45391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * host_client_properties - reads properties for client
45491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
45591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
45691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
457abc51b6de532b1071d8335918970c5d9154da73fOren Weil * returns:
458abc51b6de532b1071d8335918970c5d9154da73fOren Weil * 	< 0 - Error.
459abc51b6de532b1071d8335918970c5d9154da73fOren Weil *  = 0 - no more clients.
460abc51b6de532b1071d8335918970c5d9154da73fOren Weil *  = 1 - still have clients to send properties request.
46191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
462abc51b6de532b1071d8335918970c5d9154da73fOren Weilint mei_host_client_properties(struct mei_device *dev)
46391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
46491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_msg_hdr *mei_header;
46591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct hbm_props_request *host_cli_req;
46691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int b;
46791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	u8 client_num = dev->me_client_presentation_num;
46891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
46991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	b = dev->me_client_index;
47091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b);
47191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (b < MEI_CLIENTS_MAX) {
47291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->me_clients[client_num].client_id = b;
47391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
47491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
47591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_header->host_addr = 0;
47691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_header->me_addr = 0;
47791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_header->length = sizeof(struct hbm_props_request);
47891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_header->msg_complete = 1;
47991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		mei_header->reserved = 0;
48091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
48191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
48291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
48391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		memset(host_cli_req, 0, sizeof(struct hbm_props_request));
48491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
4851ca7e782864222bcdbe305e543df25df4e343963Tomas Winkler		host_cli_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
48691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		host_cli_req->address = b;
48791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
4881ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler		if (mei_write_message(dev, mei_header,
48991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				(unsigned char *)host_cli_req,
49091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				mei_header->length)) {
49191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->mei_state = MEI_RESETING;
49291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
49391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			mei_reset(dev, 1);
494abc51b6de532b1071d8335918970c5d9154da73fOren Weil			return -EIO;
49591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
49691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
49791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
49891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->me_client_index = b;
499abc51b6de532b1071d8335918970c5d9154da73fOren Weil		return 1;
50091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
50191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
502abc51b6de532b1071d8335918970c5d9154da73fOren Weil	return 0;
50391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
50491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
50591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
50691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_init_file_private - initializes private file structure.
50791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
50891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @priv: private file structure to be initialized
50991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @file: the file structure
51091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
511c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklervoid mei_cl_init(struct mei_cl *priv, struct mei_device *dev)
51291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
51391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	memset(priv, 0, sizeof(struct mei_cl));
51491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	init_waitqueue_head(&priv->wait);
51591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	init_waitqueue_head(&priv->rx_wait);
51691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	init_waitqueue_head(&priv->tx_wait);
51791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	INIT_LIST_HEAD(&priv->link);
51891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	priv->reading_state = MEI_IDLE;
51991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	priv->writing_state = MEI_IDLE;
52091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	priv->dev = dev;
52191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
52291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
52391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilint mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid)
52491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
52591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int i, res = -1;
52691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
527cf9673dad4dd76ecdccd265809921ceed752f19eTomas Winkler	for (i = 0; i < dev->me_clients_num; ++i)
52891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (uuid_le_cmp(cuuid,
52991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				dev->me_clients[i].props.protocol_name) == 0) {
53091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			res = i;
53191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			break;
53291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
53391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
53491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return res;
53591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
53691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
53791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
53891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
53991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_find_me_client_update_filext - searches for ME client guid
54091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *                       sets client_id in mei_file_private if found
54191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
54291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @priv: private file structure to set client_id in
54391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @cguid: searched guid of ME client
54491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @client_id: id of host client to be set in file private structure
54591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
54691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns ME client index
54791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
54891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilu8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv,
54991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				const uuid_le *cguid, u8 client_id)
55091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
55191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int i;
55291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
55391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!dev || !priv || !cguid)
55491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return 0;
55591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
55691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* check for valid client id */
55791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	i = mei_find_me_client_index(dev, *cguid);
55891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (i >= 0) {
55991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		priv->me_client_id = dev->me_clients[i].client_id;
56091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		priv->state = MEI_FILE_CONNECTING;
56191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		priv->host_client_id = client_id;
56291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
56391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		list_add_tail(&priv->link, &dev->file_list);
56491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return (u8)i;
56591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
56691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
56791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return 0;
56891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
56991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
57091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
57191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * host_init_iamthif - mei initialization iamthif client.
57291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
57391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
57491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
57591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
576c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklervoid mei_host_init_iamthif(struct mei_device *dev)
57791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
57891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	u8 i;
57991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	unsigned char *msg_buf;
58091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
581c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winkler	mei_cl_init(&dev->iamthif_cl, dev);
58291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
58391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
58491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* find ME amthi client */
58591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	i = mei_find_me_client_update_filext(dev, &dev->iamthif_cl,
58691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			    &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
58791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (dev->iamthif_cl.state != MEI_FILE_CONNECTING) {
58891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
58991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return;
59091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
59191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
592c9667bff9d191db5775d383d9b5efecc26eccae1Tomas Winkler	/* Assign iamthif_mtu to the value received from ME  */
59391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
59491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length;
595c9667bff9d191db5775d383d9b5efecc26eccae1Tomas Winkler	dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n",
59691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev->me_clients[i].props.max_msg_length);
59791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
59891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	kfree(dev->iamthif_msg_buf);
59991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_msg_buf = NULL;
60091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
60191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	/* allocate storage for ME message buffer */
60291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	msg_buf = kcalloc(dev->iamthif_mtu,
60391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			sizeof(unsigned char), GFP_KERNEL);
60491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!msg_buf) {
60591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n");
60691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return;
60791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
60891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
60991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	dev->iamthif_msg_buf = msg_buf;
61091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
6111ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler	if (mei_connect(dev, &dev->iamthif_cl)) {
61291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n");
61391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
61491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->iamthif_cl.host_client_id = 0;
61591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	} else {
61691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev->iamthif_cl.timer_count = CONNECT_TIMEOUT;
61791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
61891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
61991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
62091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
62191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_alloc_file_private - allocates a private file structure and sets it up.
62291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @file: the file structure
62391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
62491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns  The allocated file or NULL on failure
62591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
626c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winklerstruct mei_cl *mei_cl_allocate(struct mei_device *dev)
62791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
628c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winkler	struct mei_cl *cl;
62991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
630c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winkler	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
631c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winkler	if (!cl)
63291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return NULL;
63391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
634c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winkler	mei_cl_init(cl, dev);
63591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
636c95efb741f1db9c804a582a3ba52413b9cbd8f7fTomas Winkler	return cl;
63791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
63891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
63991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
64091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
64191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
64291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_disconnect_host_client - sends disconnect message to fw from host client.
64391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
64491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
64591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @cl: private data of the file object
64691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
64791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * Locking: called under "dev->device_lock" lock
64891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
64991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * returns 0 on success, <0 on failure.
65091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
65191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilint mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
65291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
65391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	int rets, err;
65491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	long timeout = 15;	/* 15 seconds */
65591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl_cb *cb;
65691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
65791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!dev || !cl)
65891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return -ENODEV;
65991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
66091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (cl->state != MEI_FILE_DISCONNECTING)
66191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return 0;
66291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
66391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
66491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (!cb)
66591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		return -ENOMEM;
66691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
66791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	INIT_LIST_HEAD(&cb->cb_list);
66891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	cb->file_private = cl;
66991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	cb->major_file_operations = MEI_CLOSE;
67091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (dev->mei_host_buffer_is_empty) {
671eb9af0acec3858d835276935df5d5eecd62535d0Tomas Winkler		dev->mei_host_buffer_is_empty = false;
67291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (mei_disconnect(dev, cl)) {
67391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			rets = -ENODEV;
67491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "failed to call mei_disconnect.\n");
67591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			goto free;
67691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
6771ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler		mdelay(10); /* Wait for hardware disconnection ready */
6781ccb7b6249f9bc50678e2a383084ed0a34cc9239Tomas Winkler		list_add_tail(&cb->cb_list, &dev->ctrl_rd_list.mei_cb.cb_list);
67991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	} else {
68091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
68191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		list_add_tail(&cb->cb_list,
68291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				&dev->ctrl_wr_list.mei_cb.cb_list);
68391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
68491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mutex_unlock(&dev->device_lock);
68591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
68691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	err = wait_event_timeout(dev->wait_recvd_msg,
68791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		 (MEI_FILE_DISCONNECTED == cl->state),
68891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		 timeout * HZ);
68991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
69091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mutex_lock(&dev->device_lock);
69191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	if (MEI_FILE_DISCONNECTED == cl->state) {
69291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		rets = 0;
69391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
69491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	} else {
69591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		rets = -ENODEV;
69691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (MEI_FILE_DISCONNECTED != cl->state)
69791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
69891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
69991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (err)
70091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev,
70191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil					"wait failed disconnect err=%08x\n",
70291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil					err);
70391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
70491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
70591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
70691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
7070288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&dev->ctrl_rd_list, cl);
7080288c7c9754d45531944992e8731d1ee9c59ecc3Tomas Winkler	mei_io_list_flush(&dev->ctrl_wr_list, cl);
70991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilfree:
71091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	mei_free_cb_private(cb);
71191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	return rets;
71291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
71391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil
71491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil/**
71591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * mei_remove_client_from_file_list -
71691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *	removes file private data from device file list
71791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil *
71891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @dev: the device structure
71991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil * @host_client_id: host client id to be removed
72091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil */
72191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weilvoid mei_remove_client_from_file_list(struct mei_device *dev,
72291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil				       u8 host_client_id)
72391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil{
72491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl *cl_pos = NULL;
72591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	struct mei_cl *cl_next = NULL;
72691f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
72791f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		if (host_client_id == cl_pos->host_client_id) {
72891f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
72991f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil					cl_pos->host_client_id,
73091f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil					cl_pos->me_client_id);
73191f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			list_del_init(&cl_pos->link);
73291f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil			break;
73391f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil		}
73491f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil	}
73591f01c6d45e634b98dd68439afdb04b674a95eeeOren Weil}
736