#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
	#include <linux/config.h>
#else
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
		#include <linux/autoconf.h>
	#else
		#include <generated/autoconf.h>
	#endif
#endif

#include <apcie1532-kapi.h>
#include "apcie1532-private.h"
#include "tcw.h"

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
	#define __user
#endif

EXPORT_NO_SYMBOLS;

//------------------------------------------------------------------------------------------------------
/** Configure and start the TimerWatchdog
 *
 */
int APCIE1532_TimerWatchdogInitAndStart(struct pci_dev *pdev,
					 uint8_t timerIndex,
                     uint8_t timeBase,
                     uint32_t reloadValue,
                     uint8_t interruptFlag)
{
	uint32_t val;
    unsigned long functionality = 1; // Set as a timer
	pr_debug("APCIE1532_TimerWatchdogInitAndStart called with params: timerIndex %u, timeBase %u, reloadValue %u, interruptFlag %u", timerIndex, timeBase, reloadValue , interruptFlag);
	/* check parameters */
	/* If Watchdog is to be used */
    if (timerIndex == 2)
    {
        timerIndex = 0; // In fact the watchdog is managed with the first timer
        functionality = 0;  // Set as a watchdog
    }

	if (timerIndex > 1)
		return 2;

	/*Test if the time base is ok*/
	if (timeBase > 2)
		return 3;

	/*Test if reload value is ok*/
	if  (reloadValue > 4095)
		return 4;

	/*Test if interrupt flag is ok*/
	if (interruptFlag > 1)
		return 5;

	/* perform actual configuration */
	{
		/* stop TimerWatchdog */
		tcw_stop(GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + (timerIndex * 0x20));

		/* timebase */
		outl((timeBase + 1), GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + APCIE1532_TCW_TIME_BASE_REG + (timerIndex * 0x20));

		/* write reload value */
		tcw_set_reload_value(GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + (timerIndex * 0x20), reloadValue);

		/* activate interrupt */
		val = (2 << 13) | // Timer mode always on 2
			(interruptFlag << 1) |
			(functionality << 4) |
			1; // Start

		outl(val, GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + APCIE1532_TCW_CONTROL_REG + (timerIndex * 0x20));
	}
	return 0;
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1532_TimerWatchdogInitAndStart(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	struct
	{
		uint8_t timerIndex;
		uint8_t timeBase;
		uint32_t reloadValue;
		uint8_t interruptFlag;
	} params;

	if ( copy_from_user( &params, (void __user *) arg,  sizeof(params) ) )
		return -EFAULT;

	/* the code below doesn't need locking since no private dat is accessed by APCIE1532_TimerWatchdogInitAndStart */
	return APCIE1532_TimerWatchdogInitAndStart(pdev, params.timerIndex, params.timeBase, params.reloadValue, params.interruptFlag);
}
//------------------------------------------------------------------------------------------------------
int APCIE1532_ReadTimerWatchdogValue(struct pci_dev *pdev, uint8_t timerIndex, uint32_t * value)
{
	/* If Watchdog is to be used */
    if (timerIndex == 2)
        timerIndex = 0; // In fact the watchdog is managed with the first timer

    if (timerIndex > 1) {
		pr_err("APCIE1532_ReadTimerWatchdogValue recieved a timer index bigger than allowed.");
        return -EINVAL;
	}

    *value = tcw_read_value(GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + timerIndex * 0x20);
    return 0;
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1532_ReadTimerWatchdogValue(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	struct
	{
		uint8_t timerIndex;
		uint32_t value;
	} params;

	if ( copy_from_user( &params, (void __user *) arg,  sizeof(params) ) )
		return -EFAULT;

	/* the code below doesn't need locking since no private dat is accessed by APCIE1532_ReadTimerWatchdogValue */
	{
		int ret = APCIE1532_ReadTimerWatchdogValue(pdev,params.timerIndex, &params.value);
		if(ret)
			return ret;
	}
	/* returns value to the user */
	if ( copy_to_user( (void __user *) arg, &params, sizeof(params) ) )
		return -EFAULT;

	return 0;
}
//------------------------------------------------------------------------------------------------------
int APCIE1532_TimerWatchdogStop(struct pci_dev *pdev, uint8_t timerIndex)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	/* Watchdog is on timer 0*/
	if (timerIndex == 2)
		timerIndex = 0;

	tcw_stop(GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + (timerIndex * 0x20));
	tcw_disable_interrupt(GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + (timerIndex * 0x20));
	return 0;
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1532_TimerWatchdogStop(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	/* the code below doesn't need locking since no private data is accessed by APCIE1532_TimerWatchdogStop */
	return APCIE1532_TimerWatchdogStop(pdev,arg);
}
//------------------------------------------------------------------------------------------------------
int APCIE1532_TimerWatchdogTrigger(struct pci_dev *pdev, uint8_t timerIndex)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	/* Watchdog is on timer 0*/
	if (timerIndex == 2)
		timerIndex = 0;

	tcw_trigger(GET_BAR3(pdev) + APCIE1532_TIMERWATCHDOG_OFFSET + (timerIndex * 0x20));
	return 0;
}
//------------------------------------------------------------------------------------------------------

int do_CMD_APCIE1532_TimerWatchdogTrigger(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	/* the code below doesn't need locking since no private data is accessed by APCIE1532_TimerWatchdogTrigger */
	return APCIE1532_TimerWatchdogTrigger(pdev,arg);
}
