/** @par LICENCE
* @verbatim
Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
        
    ADDI-DATA GmbH
    Airpark Business Center
    Airport Boulevard B210
    77836 Rheinmünster
    Germany
    Tel: +49(0)7229/1847-0
    http://www.addi-data-com
    info@addi-data.com
        
This program is free software; you can redistribute it and/or modify it under 
the terms of the GNU General Public License as published by the Free Software 
Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with 
this program; if not, write to the Free Software Foundation, 
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

You shoud also find the complete GPL in the COPYING file 
accompanying this source code.

* @endverbatim
*/

#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 <apcie1564-kapi.h>
#include "apcie1564-private.h"
#include "tcw.h"

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

EXPORT_NO_SYMBOLS;


//------------------------------------------------------------------------------------------------------
/** Configure and start the counter
 * 
 */

int APCIE1564_CounterInitAndStart(struct pci_dev *pdev, uint32_t CounterIndex, uint32_t ReloadValue, unsigned long DirectionSelection, unsigned long LevelSelection)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;
	
	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}
	
	switch(DirectionSelection)
	{
		case 0:
		case 1:
			break;
		default:
			return -EINVAL;
	}
	
	switch(LevelSelection)
	{
		case 1:
		case 2:
		case 3:
			break;
		default:
			return -EINVAL;
	}
	
	if ( ReloadValue == 0 )
		return -EINVAL;
	
	/* perform actual configuration */
	{
		register unsigned long base_address = COUNTER_BASE_ADDR(pdev,CounterIndex);
		
		/* stop counter */
		tcw_stop(base_address);
		
		/* write reload value */
		tcw_set_reload_value(base_address,ReloadValue);
		
		/* Configure counter ( this step is counter specific ) */
		{
			/* 
			 * clear bits:
			 * Enable/Disable
			 * E/D Warning
			 * E/D Reset
			 * E/D Timer
			 * Trg
			 * Clear/counter
			 * Timer mode
			 * External clock
			 * 
			 * set bit:
			 * enable counter */
			 register uint32_t val = inl(base_address + APCIE1564_TCW_CONTROL_REG);
			 val = (val & (uint32_t) 0xFFFC19E2UL) | (uint32_t) 0x80000UL;
			 
			 /* set bit U/D */
			 val = (val & (uint32_t) 0xFFFBFFFF) | (uint32_t) (DirectionSelection << 18);
			 
			 /* set field "Ext clk" = LevelSelection */
			 val = (val & (uint32_t) 0xFFFCFFFF) | (uint32_t) (LevelSelection << 16);
			  
			 /* set enable bit */
			 val = (val) | (uint32_t) (0x1);
			  
			 outl(val, base_address + APCIE1564_TCW_CONTROL_REG);
		}
		
		/* activate interrupt */
		tcw_enable_interrupt(base_address);
	}
	return 0;
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1564_CounterInitAndStart(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{	
	struct
	{
		unsigned long CounterIndex; 
		uint32_t ReloadValue;
		unsigned long DirectionSelection;
		unsigned long LevelSelection;
	} 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 APCIE1564_CounterInitAndStart */
	return APCIE1564_CounterInitAndStart(pdev, params.CounterIndex, params.ReloadValue, params.DirectionSelection, params.LevelSelection);
}
//------------------------------------------------------------------------------------------------------
int APCIE1564_ReadCounterValue(struct pci_dev *pdev, uint32_t CounterIndex, uint32_t * Value)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	/* check parameters */
	if (!Value)
		return -EINVAL;
		
	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}

	*Value = tcw_read_value( COUNTER_BASE_ADDR(pdev,CounterIndex) );
	return 0; 
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1564_ReadCounterValue(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	uint32_t Value;
	uint32_t CounterIndex;
	
	if ( copy_from_user( &CounterIndex, (uint32_t __user *) arg,  sizeof(CounterIndex) ) )
		return -EFAULT;
	
	/* the code below doesn't need locking since no private dat is accessed by APCIE1564_ReadCounterValue */
	{
		int ret = APCIE1564_ReadCounterValue(pdev,CounterIndex, &Value);
		if(ret)
			return ret;
	}
	/* returns value to the user */
	if ( copy_to_user( (uint32_t __user *) arg, &Value, sizeof(Value) ) )
		return -EFAULT;
	
	return 0;
}
//------------------------------------------------------------------------------------------------------
int APCIE1564_CounterStop(struct pci_dev *pdev, uint32_t CounterIndex)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}

	tcw_stop( COUNTER_BASE_ADDR(pdev,CounterIndex) );
	tcw_disable_interrupt( COUNTER_BASE_ADDR(pdev,CounterIndex) );
	return 0;
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1564_CounterStop(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	/* the code below doesn't need locking since no private dat is accessed by APCIE1564_CounterStop */
	return APCIE1564_CounterStop(pdev, arg);
}
//------------------------------------------------------------------------------------------------------
int APCIE1564_CounterGetStatus(struct pci_dev *pdev, uint32_t CounterIndex, uint32_t * SoftwareTriggerStatus, uint32_t * SoftwareClearStatus, uint32_t * OverflowStatus)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}
	
	{
		register uint32_t val = tcw_get_status( COUNTER_BASE_ADDR(pdev,CounterIndex) );
		*SoftwareTriggerStatus = TCW_SOFTWARE_TRIGGER_STATUS(val); 
		*SoftwareClearStatus = TCW_SOFTWARE_CLEAR_STATUS(val);
		*OverflowStatus = TCW_OVERFLOW_STATUS(val);
	}
	return 0;
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1564_CounterGetStatus(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	struct
	{
		uint32_t CounterIndex; 
		uint32_t SoftwareTriggerStatus;
		uint32_t SoftwareClearStatus;
		uint32_t OverflowStatus;
	} 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 APCIE1564_CounterGetStatus */
	{
		int ret = APCIE1564_CounterGetStatus(pdev, params.CounterIndex, &(params.SoftwareTriggerStatus), &(params.SoftwareClearStatus), &(params.OverflowStatus));
		if (ret)
			return ret;
	}
	
	if ( copy_to_user( (void __user *) arg, &params, sizeof(params) ) )
		return -EFAULT;
		
	return 0;
}

//------------------------------------------------------------------------------------------------------
int APCIE1564_CounterGetConfigRegister(struct pci_dev *pdev, uint32_t CounterIndex, uint32_t * CounterConfig)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}
	
	{
		*CounterConfig = tcw_get_config( COUNTER_BASE_ADDR(pdev,CounterIndex) );
	}
	
	return 0;	
}
//------------------------------------------------------------------------------------------------------
int do_CMD_APCIE1564_CounterGetConfigRegister(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	uint32_t Value;
	uint32_t CounterIndex;
	
	if ( copy_from_user( &CounterIndex, (uint32_t __user *) arg,  sizeof(CounterIndex) ) )
		return -EFAULT;
	
	/* the code below doesn't need locking since no private dat is accessed by APCIE1564_CounterGetConfigRegister */
	{
		int ret = APCIE1564_CounterGetConfigRegister(pdev, CounterIndex, &Value);
		if(ret) return ret;
	}
	
	/* returns value to the user */
	if ( copy_to_user( (uint32_t __user *) arg, &Value, sizeof(Value) ) )
		return -EFAULT;
		
	return 0;
}

//------------------------------------------------------------------------------------------------------

int APCIE1564_CounterReset(struct pci_dev *pdev, uint32_t CounterIndex)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}
	{
		int ret = APCIE1564_CounterStop(pdev, CounterIndex);
		if (ret) return ret;
	} 
	tcw_reset( COUNTER_BASE_ADDR(pdev,CounterIndex) );
	return 0;
}

//------------------------------------------------------------------------------------------------------

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

//------------------------------------------------------------------------------------------------------
int APCIE1564_CounterTrigger(struct pci_dev *pdev, uint32_t CounterIndex)
{
	/* check parameters */
	if (!pdev)
		return -EINVAL;

	switch(CounterIndex)
	{
		case 0:
		case 1:
		case 2:
			break;
		default:
			return -EINVAL;
	}
	tcw_trigger( COUNTER_BASE_ADDR(pdev,CounterIndex) );
	return 0;
}
//------------------------------------------------------------------------------------------------------

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

