/**
 * heci_start - initializes host and fw to start work.
 *
 * @dev: the device structure
 *
 * returns 0 on success, <0 on failure.
 */
int heci_start(struct heci_device *dev)
{
	heci_hw_config(dev);

#ifdef FORCE_FW_INIT_RESET
	/* wait for FW-initiated reset flow, indefinitely */
	heci_hw_start(dev);
	heci_enable_interrupts(dev);
	timed_wait_for_timeout(WAIT_FOR_CONNECT_SLICE, dev->recvd_hw_ready, (2*HZ));
	/* Lock only after FW-reset flow worked or failed.
	 * Otherwise interrupts BH will be locked
	 */
	mutex_lock(&dev->device_lock);
	if (dev->recvd_hw_ready)
		goto reset_done;
#else
	mutex_lock(&dev->device_lock);
#endif

	/* acknowledge interrupt and stop interupts */
	heci_clear_interrupts(dev);
	dev_dbg(&dev->pdev->dev, "reset in start the heci device.\n");
	heci_reset(dev, 1);

reset_done:
	if (heci_hbm_start_wait(dev)) {
		dev_err(&dev->pdev->dev, "HBM haven't started");
		goto err;
	}

	if (!heci_host_is_ready(dev)) {
		dev_err(&dev->pdev->dev, "host is not ready.\n");
		goto err;
	}

	if (!heci_hw_is_ready(dev)) {
		dev_err(&dev->pdev->dev, "ME is not ready.\n");
		goto err;
	}

	/*if (dev->version.major_version != HBM_MAJOR_VERSION ||
	    dev->version.minor_version != HBM_MINOR_VERSION) {
		dev_dbg(&dev->pdev->dev, "HECI start failed.\n");
		goto err;
	}*/

	dev_dbg(&dev->pdev->dev, "link layer has been established.\n");

	mutex_unlock(&dev->device_lock);
	return 0;
err:
	dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
	dev->dev_state = HECI_DEV_DISABLED;
	mutex_unlock(&dev->device_lock);
	return -ENODEV;
}
void heci_stop(struct heci_device *dev)
{
	dev_dbg(&dev->pdev->dev, "stopping the device.\n");
	mutex_lock(&dev->device_lock);
	cancel_delayed_work(&dev->timer_work);
	dev->dev_state = HECI_DEV_POWER_DOWN;
	heci_reset(dev, 0);
	mutex_unlock(&dev->device_lock);
	flush_scheduled_work();
}
/*
 * heci_hw_init  - init host and fw to start work.
 *
 * @dev: Device object for our driver
 *
 * @return 0 on success, <0 on failure.
 */
int
heci_hw_init(struct iamt_heci_device *dev)
{
	int err = 0;

	mutex_enter(&dev->device_lock);
	dev->host_hw_state = read_heci_register(dev, H_CSR);
	dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
	DBG("host_hw_state = 0x%08x, mestate = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);

	if ((dev->host_hw_state & H_IS) == H_IS) {
		/* acknowledge interrupt and stop interupts */
		heci_set_csr_register(dev);
	}
	dev->recvd_msg = 0;
	DBG("reset in start the heci device.\n");

	heci_reset(dev, 1);

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

	/* wait for ME to turn on ME_RDY */
	err = 0;
	while (!dev->recvd_msg && err != -1) {
		err = cv_reltimedwait(&dev->wait_recvd_msg,
		    &dev->device_lock, HECI_INTEROP_TIMEOUT, TR_CLOCK_TICK);
	}

	if (err == -1 && !dev->recvd_msg) {
		dev->heci_state = HECI_DISABLED;
		DBG("wait_event_interruptible_timeout failed"
		    "on wait for ME to turn on ME_RDY.\n");
		mutex_exit(&dev->device_lock);
		return (-ENODEV);
	} else {
		if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
		    ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
			dev->heci_state = HECI_DISABLED;
			DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
			    dev->host_hw_state,
			    dev->me_hw_state);

			if (!(dev->host_hw_state & H_RDY) != H_RDY)
				DBG("host turn off H_RDY.\n");

			if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
				DBG("ME turn off ME_RDY.\n");

			cmn_err(CE_WARN,
			    "heci: link layer initialization failed.\n");
			mutex_exit(&dev->device_lock);
			return (-ENODEV);
		}
	}
	dev->recvd_msg = 0;
	DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
	    dev->host_hw_state, dev->me_hw_state);
	DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
	DBG("heci: link layer has been established.\n");
	mutex_exit(&dev->device_lock);
	return (0);
}
/**
 * heci_ioctl - the IOCTL function
 *
 * @file: pointer to file structure
 * @cmd: ioctl command
 * @data: pointer to heci message structure
 *
 * returns 0 on success , <0 on error
 */
static long heci_ioctl(struct file *file, unsigned int cmd, unsigned long data)
{
	struct heci_device *dev;
	struct heci_cl *cl = file->private_data;
	struct heci_connect_client_data *connect_data = NULL;
	int rets;

	dev = cl->dev;
	dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);

	/* Test API for triggering host-initiated IPC reset to ISH */
	if (cmd == 0x12345678) {
		/* Re-init */
		dev->dev_state = HECI_DEV_INITIALIZING;
		heci_reset(dev, 1);

		if (heci_hbm_start_wait(dev)) {
			dev_err(&dev->pdev->dev, "HBM haven't started");
			goto err;
		}

		if (!heci_host_is_ready(dev)) {
			dev_err(&dev->pdev->dev, "host is not ready.\n");
			goto err;
		}

		if (!heci_hw_is_ready(dev)) {
			dev_err(&dev->pdev->dev, "ME is not ready.\n");
			goto err;
		}

		return	0;
err:
		dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
		dev->dev_state = HECI_DEV_DISABLED;
		return -ENODEV;
	}

	/* Test API for triggering host disabling */
	if (cmd == 0xAA55AA55) {
		/* Handle ISH reset against upper layers */
		/* Remove all client devices */
		heci_bus_remove_all_clients(dev);
		dev->dev_state = HECI_DEV_DISABLED;
		return	0;
	}

	if (cmd != IOCTL_HECI_CONNECT_CLIENT)
		return -EINVAL;

	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	mutex_lock(&dev->device_lock);
	if (dev->dev_state != HECI_DEV_ENABLED) {
		rets = -ENODEV;
		goto out;
	}

	dev_dbg(&dev->pdev->dev, ": IOCTL_HECI_CONNECT_CLIENT.\n");

	connect_data = kzalloc(sizeof(struct heci_connect_client_data),
							GFP_KERNEL);
	if (!connect_data) {
		rets = -ENOMEM;
		goto out;
	}
	dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
	if (copy_from_user(connect_data, (char __user *)data,
				sizeof(struct heci_connect_client_data))) {
		dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
		rets = -EFAULT;
		goto out;
	}

	rets = heci_ioctl_connect_client(file, connect_data);

	/* if all is ok, copying the data back to user. */
	if (rets)
		goto out;

	dev_dbg(&dev->pdev->dev, "copy connect data to user\n");
	if (copy_to_user((char __user *)data, connect_data,
				sizeof(struct heci_connect_client_data))) {
		dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
		rets = -EFAULT;
		goto out;
	}

out:
	kfree(connect_data);
	mutex_unlock(&dev->device_lock);
	return rets;
}
static int
heci_bh_process_device(struct iamt_heci_device *dev)
{
	struct io_heci_list complete_list;
	int32_t slots;
	int rets, isr_pending = 0;
	struct heci_cb_private *cb_pos = NULL, *cb_next = NULL;
	struct heci_file_private *file_ext;
	int bus_message_received = 0;

	DBG("function called after ISR to handle the interrupt processing.\n");
	/* initialize our complete list */
	mutex_enter(&dev->device_lock);
	heci_initialize_list(&complete_list, dev);
	dev->host_hw_state = read_heci_register(dev, H_CSR);
	dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);

	/* check if ME wants a reset */
	if (((dev->me_hw_state & ME_RDY_HRA) == 0) &&
	    (dev->heci_state != HECI_RESETING) &&
	    (dev->heci_state != HECI_INITIALIZING)) {
		DBG("FW not ready.\n");
		heci_reset(dev, 1);
		mutex_exit(&dev->device_lock);
		return (0);
	}

	/*  check if we need to start the dev */
	if ((dev->host_hw_state & H_RDY) == 0) {
		if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
			DBG("we need to start the dev.\n");
			dev->host_hw_state |= (H_IE | H_IG | H_RDY);
			heci_set_csr_register(dev);
			if (dev->heci_state == HECI_INITIALIZING) {
				dev->recvd_msg = 1;
				cv_broadcast(&dev->wait_recvd_msg);
				mutex_exit(&dev->device_lock);

				return (0);

			} else {
				mutex_exit(&dev->device_lock);
				if (dev->reinit_tsk &&
				    ddi_taskq_dispatch(dev->reinit_tsk,
				    heci_task_initialize_clients,
				    dev, DDI_SLEEP) == DDI_FAILURE) {

					cmn_err(CE_WARN, "taskq_dispatch "
					    "failed for reinit_tsk");
				}
				return (0);
			}
		} else {
			DBG("enable interrupt FW not ready.\n");
			heci_csr_enable_interrupts(dev);
			mutex_exit(&dev->device_lock);
			return (0);
		}
	}
	/* check slots avalable for reading */
	slots = count_full_read_slots(dev);
	DBG("slots =%08x  extra_write_index =%08x.\n",
	    slots, dev->extra_write_index);
	while ((slots > 0) && (!dev->extra_write_index)) {
		DBG("slots =%08x  extra_write_index =%08x.\n", slots,
		    dev->extra_write_index);
		DBG("call heci_bh_read_handler.\n");
		rets = heci_bh_read_handler(&complete_list, dev, &slots);
		if (rets != 0)
			goto end;
	}
	rets = heci_bh_write_handler(&complete_list, dev, &slots);
end:
	DBG("end of bottom half function.\n");
	dev->host_hw_state = read_heci_register(dev, H_CSR);
	dev->host_buffer_is_empty = host_buffer_is_empty(dev);

	if ((dev->host_hw_state & H_IS) == H_IS) {
		/* acknowledge interrupt and disable interrupts */
		heci_csr_disable_interrupts(dev);

		DBG("schedule work the heci_bh_handler.\n");
		isr_pending = 1;


	} else {
		heci_csr_enable_interrupts(dev);
	}

	if (dev->recvd_msg) {
		DBG("received waiting bus message\n");
		bus_message_received = 1;
	}

	if (bus_message_received) {
		DBG("wake up dev->wait_recvd_msg\n");
		cv_broadcast(&dev->wait_recvd_msg);
		bus_message_received = 0;
	}
	if ((complete_list.status != 0) ||
	    list_empty(&complete_list.heci_cb.cb_list)) {
		mutex_exit(&dev->device_lock);
		return (isr_pending);
	}

	mutex_exit(&dev->device_lock);

	list_for_each_entry_safe(cb_pos, cb_next,
	    &complete_list.heci_cb.cb_list, cb_list, struct heci_cb_private) {
		file_ext = (struct heci_file_private *)cb_pos->file_private;
		list_del(&cb_pos->cb_list);
		if (file_ext != NULL) {
			if (file_ext != &dev->iamthif_file_ext) {
				DBG("completing call back.\n");
				_heci_cmpl(file_ext, cb_pos);
				cb_pos = NULL;
			} else if (file_ext == &dev->iamthif_file_ext) {
				_heci_cmpl_iamthif(dev, cb_pos);
			}
		}
	}
	return (isr_pending);
}