/* Set the different timeouts needed by the SCU FW */
static int watchdog_set_timeouts(int timer_threshold, int warning_pretimeout,
			    int reset_timeout)
{
	u32	ipc_wbuf[3];
	int	ret = 0;
	u32	freq = watchdog_device.timer7_tbl_ptr->freq_hz;

	ipc_wbuf[0] = timer_threshold * freq;
	ipc_wbuf[1] = warning_pretimeout * freq;
	ipc_wbuf[2] = (reset_timeout - timer_threshold - warning_pretimeout)
			* freq;

	pr_debug(PFX "Watchdog ipc_buff[0]%x\n", ipc_wbuf[0]);
	pr_debug(PFX "Watchdog ipc_buff[1]%x\n", ipc_wbuf[1]);
	pr_debug(PFX "Watchdog ipc_buff[2]%x\n", ipc_wbuf[2]);

	ret = rpmsg_send_command(watchdog_instance,
					IPC_SET_WATCHDOG_TIMER,
					IPC_SET_SUB_LOAD_THRES,
					(u8 *) ipc_wbuf, NULL, 12, 0);
	if (ret)
		pr_crit(PFX "Error Setting SCU Watchdog Timer: %x\n", ret);

	return ret;
}
int rpmsg_send_simple_command(struct rpmsg_instance *instance, u32 cmd,
						u32 sub)
{
	int ret;

	ret = rpmsg_send_command(instance, cmd, sub, NULL, NULL, 0, 0);

	return ret;
}
/* Providing rpmsg ipc generic interfaces.
 * Modules can call these API directly without registering rpmsg driver.
 *
 * The arg list is the same as intel_scu_ipc_command(),
 * so simply change intel_scu_ipc_command() to rpmsg_send_generic_command()
 */
int rpmsg_send_generic_command(u32 cmd, u32 sub,
				u8 *in, u32 inlen,
				u32 *out, u32 outlen)
{
	struct rpmsg_instance *rpmsg_ipc_instance =
		rpmsg_ddata[RPMSG_IPC_COMMAND].rpmsg_instance;

	return rpmsg_send_command(rpmsg_ipc_instance, cmd, sub,
					in, out, inlen, outlen);
}
/* Set the different timeouts needed by the SCU FW and start the
 * kernel watchdog */
static int watchdog_set_timeouts_and_start(int pretimeout,
					   int timeout)
{
	int ret, error = 0;
	struct ipc_wd_start {
		u32 pretimeout;
		u32 timeout;
	} ipc_wd_start = { pretimeout, timeout };

	ret = rpmsg_send_command(watchdog_instance, IPC_WATCHDOG,
				 SCU_WATCHDOG_START, (u8 *)&ipc_wd_start,
				 NULL, sizeof(ipc_wd_start), 0);
	if (ret) {
		pr_crit("Error configuring and starting watchdog: %d\n",
			ret);
		error = -EIO;
	}

	return error;
}
/* stops the timer */
static int intel_scu_stop(void)
{
	int ret;

	pr_crit(PFX "%s\n", __func__);

	ret = rpmsg_send_command(watchdog_instance,
					IPC_SET_WATCHDOG_TIMER,
					IPC_SET_SUB_DISABLE,
					NULL, NULL, 0, 0);
	if (ret) {
		pr_crit(PFX "Error sending disable ipc: %x\n", ret);
		goto err;
	}

	watchdog_device.started = false;

err:
	return ret;
}
/* Keep alive  */
static int watchdog_keepalive(void)
{
	int ret;

	pr_err(PFX "%s\n", __func__);

	if (unlikely(!kicking_active)) {
			/* Close our eyes */
			pr_err(PFX "Transparent kicking\n");
			return 0;
	}
	/* Really kick it */
	ret = rpmsg_send_command(watchdog_instance,
					IPC_SET_WATCHDOG_TIMER,
					IPC_SET_SUB_KEEPALIVE,
					NULL, NULL, 0, 0);
	if (ret)
		pr_err(PFX "Error sending keepalive ipc: %x\n", ret);

	return ret;
}
int rpmsg_send_raw_command(struct rpmsg_instance *instance, u32 cmd,
						u32 sub, u8 *in,
						u32 *out, u32 inlen,
						u32 outlen, u32 sptr,
						u32 dptr)
{
	int ret = 0;

	if (!instance) {
		pr_err("%s: Instance is NULL\n", __func__);
		return -EFAULT;
	}

	mutex_lock(&instance->instance_lock);
	instance->tx_msg->sptr = sptr;
	instance->tx_msg->dptr = dptr;
	mutex_unlock(&instance->instance_lock);

	ret = rpmsg_send_command(instance, cmd, sub, in, out, inlen, outlen);

	return ret;
}
static int
vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq)
{
	int retval = 0;
	unsigned char rtc_control;
	unsigned char enable_bit_save = 0;

	/* There can be only one ... */
	if (mrst_rtc.dev)
		return -EBUSY;

	if (!iomem)
		return -ENODEV;

	iomem = request_mem_region(iomem->start,
			iomem->end + 1 - iomem->start,
			driver_name);
	if (!iomem) {
		dev_dbg(dev, "i/o mem already in use.\n");
		return -EBUSY;
	}

	mrst_rtc.irq = rtc_irq;
	mrst_rtc.iomem = iomem;
	mrst_rtc.dev = dev;
	dev_set_drvdata(dev, &mrst_rtc);

	mrst_rtc.rtc = rtc_device_register(driver_name, dev,
				&mrst_rtc_ops, THIS_MODULE);
	if (IS_ERR(mrst_rtc.rtc)) {
		retval = PTR_ERR(mrst_rtc.rtc);
		goto cleanup0;
	}

	rename_region(iomem, dev_name(&mrst_rtc.rtc->dev));

	printk(KERN_ALERT"(%s) +------------rtc info-----------+\n",__func__);
	spin_lock_irq(&rtc_lock);
	enable_bit_save = vrtc_cmos_read(RTC_CONTROL);
	enable_bit_save &= (RTC_PIE | RTC_AIE);
	mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE);
	rtc_control = vrtc_cmos_read(RTC_CONTROL);
	spin_unlock_irq(&rtc_lock);
	printk(KERN_ALERT"read PIE_AIE_save = 0x%02x, then clear. rtc_control = 0x%02x\n",enable_bit_save,rtc_control);

	if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))
		dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n");

	if (is_valid_irq(rtc_irq)) {
		retval = request_irq(rtc_irq, mrst_rtc_irq,
				IRQF_NO_SUSPEND, dev_name(&mrst_rtc.rtc->dev),
				mrst_rtc.rtc);
		if (retval < 0) {
			dev_dbg(dev, "IRQ %d is already in use, err %d\n",
				rtc_irq, retval);
			goto cleanup1;
		}
	}

	/* make RTC device wake capable from sleep */
	device_init_wakeup(dev, true);

	if ((__intel_mid_cpu_chip == INTEL_MID_CPU_CHIP_PENWELL) ||
	    (__intel_mid_cpu_chip == INTEL_MID_CPU_CHIP_CLOVERVIEW)) {
		retval = rpmsg_send_command(vrtc_mrst_instance,
				IPCMSG_GET_HOBADDR, 0, NULL, &oshob_base, 0, 1);
		if (retval < 0) {
			dev_dbg(dev,
				"Unable to get OSHOB base address, err %d\n",
				retval);
			goto cleanup1;
		}

		oshob_addr = ioremap_nocache(oshob_base+OSHOB_ALARM_OFFSET, 4);
		if (!oshob_addr) {
			dev_dbg(dev, "Unable to do ioremap for OSHOB\n");
			retval = -ENOMEM;
			goto cleanup1;
		}
	}

	spin_lock_irq(&rtc_lock);
	if(enable_bit_save)
		mrst_irq_enable(&mrst_rtc, enable_bit_save);//add for power off rtc issue
	spin_unlock_irq(&rtc_lock);
	rtc_control = vrtc_cmos_read(RTC_CONTROL);
	printk(KERN_ALERT"read resume rtc_control = 0x%02x.\n",rtc_control);
	printk(KERN_ALERT"(%s) +-------------------------------+\n",__func__);
	dev_dbg(dev, "vRTC driver initialised\n");
	return 0;

cleanup1:
	rtc_device_unregister(mrst_rtc.rtc);
cleanup0:
	dev_set_drvdata(dev, NULL);
	mrst_rtc.dev = NULL;
	release_mem_region(iomem->start, resource_size(iomem));
	dev_err(dev, "rtc-mrst: unable to initialise\n");
	return retval;
}
/* ioctl */
static long intel_scu_ioctl(struct file *file, unsigned int cmd,
			    unsigned long arg)
{
	void __user *argp = (void __user *)arg;
	u32 __user *p = argp;
	u32 val;
	int options;

	static const struct watchdog_info ident = {
		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
		/* @todo Get from SCU via ipc_get_scu_fw_version()? */
		.firmware_version = 0,
		/* len < 32 */
		.identity = "Intel_SCU IOH Watchdog"
	};

	switch (cmd) {
	case WDIOC_GETSUPPORT:
		return copy_to_user(argp, &ident,
				    sizeof(ident)) ? -EFAULT : 0;
	case WDIOC_GETSTATUS:
	case WDIOC_GETBOOTSTATUS:
		return put_user(0, p);
	case WDIOC_KEEPALIVE:
		pr_warn("%s: KeepAlive ioctl\n", __func__);
		if (!watchdog_device.started)
			return -EINVAL;

		watchdog_keepalive();
		return 0;
	case WDIOC_SETPRETIMEOUT:
		pr_warn("%s: SetPreTimeout ioctl\n", __func__);

		if (watchdog_device.started)
			return -EBUSY;

		/* Timeout to warn */
		if (get_user(val, p))
			return -EFAULT;

		pre_timeout = val;
		return 0;
	case WDIOC_SETTIMEOUT:
		pr_warn("%s: SetTimeout ioctl\n", __func__);

		if (watchdog_device.started)
			return -EBUSY;

		if (get_user(val, p))
			return -EFAULT;

		timeout = val;
		return 0;
	case WDIOC_GETTIMEOUT:
		return put_user(timeout, p);
	case WDIOC_SETOPTIONS:
		if (get_user(options, p))
			return -EFAULT;

		if (options & WDIOS_DISABLECARD) {
			pr_warn("%s: Stopping the watchdog\n", __func__);
			watchdog_stop();
			return 0;
		}

		if (options & WDIOS_ENABLECARD) {
			pr_warn("%s: Starting the watchdog\n", __func__);

			if (watchdog_device.started)
				return -EBUSY;

			if (check_timeouts(pre_timeout, timeout)) {
				pr_warn("%s: Invalid thresholds\n",
					__func__);
				return -EINVAL;
			}
			if (watchdog_config_and_start(timeout, pre_timeout))
				return -EINVAL;
			return 0;
		}
		return 0;
	default:
		return -ENOTTY;
	}
}

static int watchdog_set_reset_type(int reset_type)
{
	int ret;
	struct ipc_wd_on_timeout {
		u32 reset_type;
	} ipc_wd_on_timeout = { reset_type };

	ret = rpmsg_send_command(watchdog_instance, IPC_WATCHDOG,
				 SCU_WATCHDOG_SET_ACTION_ON_TIMEOUT,
				 (u8 *)&ipc_wd_on_timeout, NULL,
				 sizeof(ipc_wd_on_timeout), 0);
	if (ret) {
		pr_crit("Error setting watchdog action: %d\n", ret);
		return -EIO;
	}

	watchdog_device.normal_wd_action = reset_type;

	return 0;
}