/**
 * heci_reset - resets host and fw.
 *
 * @dev: the device structure
 * @interrupts_enabled: if interrupt should be enabled after reset.
 */
void heci_reset(struct heci_device *dev, int interrupts_enabled)
{
	bool unexpected;
	int ret;

	unexpected = (dev->dev_state != HECI_DEV_INITIALIZING &&
			dev->dev_state != HECI_DEV_DISABLED &&
			dev->dev_state != HECI_DEV_POWER_DOWN &&
			dev->dev_state != HECI_DEV_POWER_UP);

	ret = heci_hw_reset(dev, interrupts_enabled);
	if (ret) {
		dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
		interrupts_enabled = false;
		dev->dev_state = HECI_DEV_DISABLED;
	}

	dev->hbm_state = HECI_HBM_IDLE;

	if (dev->dev_state != HECI_DEV_INITIALIZING) {
		if (dev->dev_state != HECI_DEV_DISABLED &&
		    dev->dev_state != HECI_DEV_POWER_DOWN)
			dev->dev_state = HECI_DEV_RESETTING;

		heci_cl_all_disconnect(dev);
		memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
	}

	dev->me_clients_num = 0;
	dev->rd_msg_hdr = 0;

	if (unexpected)
		dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
			 heci_dev_state_str(dev->dev_state));

	if (!interrupts_enabled) {
		dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
		return;
	}
	dev_dbg(&dev->pdev->dev, "before sending HOST start\n");
	ret = heci_hw_start(dev);
	if (ret) {
		dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n");
		dev->dev_state = HECI_DEV_DISABLED;
		return;
	}

	dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
	/* link is established * start sending messages.  */

	dev->dev_state = HECI_DEV_INIT_CLIENTS;
	dev->hbm_state = HECI_HBM_START;
	heci_hbm_start_req(dev);
	/* wake up all readings so they can be interrupted */
	heci_cl_all_read_wakeup(dev);

	/* remove all waiting requests */
	heci_cl_all_write_clear(dev);
}
/*
 * heci_reset  - reset host and fw.
 *
 * @dev: Device object for our driver
 * @interrupts: if interrupt should be enable after reset.
 */
void
heci_reset(struct iamt_heci_device *dev, int interrupts)
{
	struct heci_file_private *file_pos = NULL;
	struct heci_file_private *file_next = NULL;
	struct heci_cb_private *priv_cb_pos = NULL;
	struct heci_cb_private *priv_cb_next = NULL;
	int unexpected = 0;

	if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
		dev->need_reset = 1;
		return;
	}

	if (dev->heci_state != HECI_INITIALIZING &&
	    dev->heci_state != HECI_DISABLED &&
	    dev->heci_state != HECI_POWER_DOWN &&
	    dev->heci_state != HECI_POWER_UP)
		unexpected = 1;

	if (dev->reinit_tsk != NULL) {
		mutex_exit(&dev->device_lock);
		(void) ddi_taskq_wait(dev->reinit_tsk);
		mutex_enter(&dev->device_lock);
	}

	dev->host_hw_state = read_heci_register(dev, H_CSR);

	DBG("before reset host_hw_state = 0x%08x.\n",
	    dev->host_hw_state);

	heci_hw_reset(dev, interrupts);

	dev->host_hw_state &= ~H_RST;
	dev->host_hw_state |= H_IG;

	write_heci_register(dev, H_CSR, dev->host_hw_state);

	DBG("currently saved host_hw_state = 0x%08x.\n",
	    dev->host_hw_state);

	dev->need_reset = 0;

	if (dev->heci_state != HECI_INITIALIZING) {
		if ((dev->heci_state != HECI_DISABLED) &&
		    (dev->heci_state != HECI_POWER_DOWN))
			dev->heci_state = HECI_RESETING;

		list_for_each_entry_safe(file_pos,
		    file_next, &dev->file_list, link,
		    struct heci_file_private) {
			file_pos->state = HECI_FILE_DISCONNECTED;
			file_pos->flow_ctrl_creds = 0;
			file_pos->read_cb = NULL;
			file_pos->timer_count = 0;
		}
		/* remove entry if already in list */
		DBG("list del iamthif and wd file list.\n");
		heci_remove_client_from_file_list(dev,
		    dev->wd_file_ext.host_client_id);

		heci_remove_client_from_file_list(dev,
		    dev->iamthif_file_ext.host_client_id);

		heci_reset_iamthif_params(dev);
		dev->wd_due_counter = 0;
		dev->extra_write_index = 0;
	}

	dev->num_heci_me_clients = 0;
	dev->rd_msg_hdr = 0;
	dev->stop = 0;
	dev->wd_pending = 0;

	/* update the state of the registers after reset */
	dev->host_hw_state =  read_heci_register(dev, H_CSR);
	dev->me_hw_state =  read_heci_register(dev, ME_CSR_HA);

	DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);

	if (unexpected)
		cmn_err(CE_WARN, "unexpected heci reset.\n");

	/* Wake up all readings so they can be interrupted */
	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
	    struct heci_file_private) {
		cmn_err(CE_NOTE, "heci: Waking up client!\n");
		cv_broadcast(&file_pos->rx_wait);
	}
	/* remove all waiting requests */
	if (dev->write_list.status == 0 &&
	    !list_empty(&dev->write_list.heci_cb.cb_list)) {
		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
		    &dev->write_list.heci_cb.cb_list, cb_list,
		    struct heci_cb_private) {
			if (priv_cb_pos) {
				list_del(&priv_cb_pos->cb_list);
				heci_free_cb_private(priv_cb_pos);
			}
		}
	}