/** @file apci3200-private.h

Contains all functions to initialize digital inputs of the APCI-1648.

@par CREATION
@author This header contains private definitions (not to be seen by external code)
@date

@par VERSION
@verbatim
$LastChangedRevision:$
$LastChangedDate:$
@endverbatim

@par COMPILER
@verbatim
Compiler	: gcc
Version	: gcc (GCC) 3.3.6 (Debian 1:3.3.6-5)
kernel	: 2.4.26
@endverbatim

@par UPDATES
@verbatim
Date			: 15.06.2005
Author		: Krauth Julien
Description	: Add timeout management in OUTPDW_WAIT_READY and<br>
a new function test_eoc_with_timeout to test the eoc flag<br>
with timeout.
@endverbatim

@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
*/

#ifndef __APCI3200_PRIVATE__
#define __APCI3200_PRIVATE__

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
    #include <linux/autoconf.h>
#else
    #include <generated/autoconf.h>
#endif
    // #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
	#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
    #include <linux/smp_lock.h>
#endif

#include <linux/spinlock.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h> // struct pci_dev
#include <asm/io.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
	#include <asm/system.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
#include <asm/uaccess.h>
#else
    #include <linux/uaccess.h>
#endif
#include <asm/bitops.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/proc_fs.h>

#include <apci.h>
#include <apci3200.h>
#include <apci3200-kapi.h>

/** Define fpr init.c */
#define APCI3200_WAIT_STATE_REG	0x60

#define APCI3200_INTCSR	0x38


/** ressources of a board */

static inline unsigned long GET_BAR0(struct pci_dev * pdev)
{
	return pdev->resource[0].start;
}

static inline unsigned long GET_BAR1(struct pci_dev * pdev)
{
	return pdev->resource[1].start;
}

static inline unsigned long GET_BAR2(struct pci_dev * pdev)
{
	return pdev->resource[2].start;
}

static inline unsigned long GET_BAR3(struct pci_dev * pdev)
{
	return pdev->resource[3].start;
}

/** Utility fonction that returns 0 if the given index corresponds to a card already configured.
*
* @param count Number of board
* @param index Minor number to check
*
* @return 0 if minor number valid, not 0 otherwise
*
* count ranges from 1 to ...
* index ranges from 0 to ...
*
* */

static inline int apci3200_INDEX_NOT_VALID(atomic_t * count , unsigned int index )
{
	volatile unsigned int __count =  atomic_read(count);
	if (index >= __count) return 1;
	return 0;
}

/* number of board detected by the kernel */
extern atomic_t apci3200_count;

#define __DRIVER_NAME "apci3200"

/* major number (attributed by the OS) */
extern unsigned int apci3200_majornumber;


//-----------------------  Direct IO functions ---------------------------------
static inline uint32_t INPDW (unsigned long dw_Address)
{
	return (inl (dw_Address));
}

static inline int is_ready_bit_set(unsigned long regaddr)
{
	unsigned long tmp = inl(regaddr);
	return (tmp & (1<<19));
}

/* @retval 0 OK
 * @retval -1 timeout
 * */
static inline int wait_ready_bit (unsigned long regaddr)
{
	unsigned long delay = jiffies + APCI3200_TIMEOUT;
	while(time_before(jiffies,delay)){
		/* check ready bit */
		if(is_ready_bit_set(regaddr))
			return 0;
	}
	/* timeout */
	return -1;
}

/** Write a 32 bits integer in the module at address "module_address".
 *
* @retval	0	No error.
* @retval	1	Timeout occurred.
*
* @note At this time the timeout is fixed to 2 seconds.
*/
static inline int outl_module(unsigned long module_address, unsigned long offset, uint32_t value)
{
	if( wait_ready_bit (module_address + 12) )
		return 1;

	barrier();
	/* Writes on the register */
	outl (value, module_address+offset);
	return 0;
}
/** Write a 32 bits integer in the CJC area of board "pdev".
 *
* @retval	0	No error.
* @retval	1	Timeout occurred.
*
* @note At this time the timeout is fixed to 2 seconds.
*/
static inline int outl_cjc(struct pci_dev *pdev, unsigned long offset, uint32_t value)
{
	if( wait_ready_bit (GET_BAR1(pdev) + 12) )
		return 1;

	barrier();
	/* Writes on the register */
	outl(value, GET_BAR1(pdev)+offset);
	return 0;
}

/** Wait the End Of a Conversion.
*
* This function wait the end of a conversion.
* While a loop is using a timeout is used.
*
* @param [in]	sec	Timeout in seconds.
*
* @retval 1	Eoc occurred.
* @retval 2	Timeout occurred.
*/
static inline int wait_for_eoc(struct pci_dev *pdev, uint32_t offset)
{
	unsigned long delay = jiffies + APCI3200_TIMEOUT;
	while(time_before(jiffies,delay))
	{
		if ( (inl(GET_BAR1(pdev) + offset + 20) & 1) == 1 )
			return 0;
	}
	/* timeout */
	return 1;
}
//------------------------------------------------------------------------------

/* /dev function */
int apci3200_open_lookup (struct inode *inode, struct file *filp);
int apci3200_release_lookup (struct inode *inode,struct file *filp);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
        int apci3200_ioctl_lookup (struct inode *inode,
                                                           struct file *filp,
                                                           unsigned int cmd,
                                                           unsigned long arg);
#else
        long apci3200_ioctl_lookup (struct file *filp,
                                                                unsigned int cmd,
                                                                unsigned long arg);
#endif

/* kernel-side accessible API */
int apci3200_do_ioctl(struct pci_dev * pdev, unsigned int cmd, unsigned long arg);

/*/proc functions  */
void apci3200_proc_init(void);
void apci3200_proc_release(void);
void apci3200_proc_create_device(struct pci_dev * dev, unsigned int minor_number);
void apci3200_proc_release_device(struct pci_dev * dev);

/* interrupt related function */
int apci3200_register_interrupt(struct pci_dev * pdev);
int apci3200_deregister_interrupt(struct pci_dev * pdev);

/* eeprom functions */
int apci3200_get_header_calibration_value (struct pci_dev *pdev, const char* name, int num);
int apci3200_get_channel_calibration_value (struct pci_dev *pdev, unsigned int channel_num,
		unsigned int ConnectionType,
		unsigned int Gain,
		unsigned long *CJCCurrentSource,
		unsigned long *ChannelCurrentSource,
		unsigned long *ChannelGainFactor);

/* Initialisation functions */
int apci3200_initialize(struct pci_dev *pdev);
//------------------------------------------------------------------------------
#include "api.h"
#include "privdata.h"
//------------------------------------------------------------------------------
/** @retval 0 channel number OK
 * @retval otherwise channel number incorrect
 * */
static inline int check_channel(uint8_t chan)
{
	//TODO: should take differential into account
	return chan > 15;
}
//------------------------------------------------------------------------------
static inline int channel_to_module(unsigned char chan)
{
	switch(chan)
	{
	case 0 ... 3:
		return 0;
	case 4 ... 7:
		return 1;
	case 8 ... 11:
		return 2;
	case 12 ... 15:
		return 3;
	default:
		return -1;
	}
}
//------------------------------------------------------------------------------
/** convert a channel to its index in the relative module */
static inline int channel_to_index(unsigned char chan)
{
	return chan - (channel_to_module(chan)*APCI3200_MAX_ANALOG_INPUTS_PER_MODULE);
}
//------------------------------------------------------------------------------
/* returns the offset in BAR1 corresponding to the given module */
static inline unsigned long module_to_offset(uint8_t module)
{
	return (module*64);
}
//------------------------------------------------------------------------------
/** get the offset in a module*/
static inline int channel_to_offset(unsigned char chan)
{
	return module_to_offset(channel_to_module(chan));
}
//------------------------------------------------------------------------------
/** for a given module, expected correct, returns the corresponding memory address */
static inline unsigned long module_to_address(struct pci_dev *pdev, uint8_t module)
{
	return GET_BAR1(pdev) + module_to_offset(module);
}
//------------------------------------------------------------------------------
static inline unsigned int apci3200_get_reference_voltage(struct pci_dev *pdev, unsigned char chan)
{
	return GET_MODULE_DATA(pdev,channel_to_module(chan))->dw_ReferenceVoltage[channel_to_index(chan)];
}
//------------------------------------------------------------------------------
#endif //__APCI3200_PRIVATE__
