#ifndef __APCI3120_PRIVATE__
#define __APCI3120_PRIVATE__

/** @file apci3120-private.h
*
* @author Sylvain Nahas
*
* This header contains private definitions (not to be seen by external code)
*/

/** @par LICENCE
* @verbatim
Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.

ADDI-DATA GmbH
Airpark Business Center
Airport Boulevard B210
77836 Rheinmuenster
Germany
Tel: +49(0)7229/1847-0
Fax: +49(0)7229/1847-200
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 shoud 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 <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 <apci3120.h>
#include <apci3120-kapi.h>


	
//------------------------------------------------------------------------------
#define APCI3120_EOC					0x8000U
#define APCI3120_EOS					0x2000U
#define APCI3120_RD_STATUS				0x02
#define APCI3120_EOC_INT_MODE				1
#define APCI3120_FIFO_ADDRESS				0x06
#define APCI3120_TIMER_0_MODE_5				0x03
#define APCI3120_RESET_FIFO				0x0C
#define APCI3120_TIMER_CRT1				0x0C
#define APCI3120_ENABLE_EOC_INT				0x1
#define APCI3120_WRITE_MODE_SELECT			0x0E
#define APCI3120_ENABLE_TIMER0				0x1000U
#define APCI3120_WR_ADDRESS				0x00
#define APCI3120_SELECT_TIMER_0_WORD			0x00
#define APCI3120_TIMER_VALUE				0x04
#define APCI3120_CLEAR_PA				0xFFF0U
#define APCI3120_CLEAR_PR				0xF0FFU
#define APCI3120_CLEAR_PA_PR				(APCI3120_CLEAR_PR & APCI3120_CLEAR_PA)
#define APCI3120_TIMER_CRT0				0x0D
#define APCI3120_START_CONVERSION			0x02
#define APCI3120_ENABLE_SCAN				0x8
#define APCI3120_DISABLE_SCAN            (~APCI3120_ENABLE_SCAN)
#define APCI3120_REG_DIGITAL_INPUT		0x2
#define APCI3120_REG_DIGITAL_OUTPUT		0xD
#define APCI3120_ENABLE_EOS_INT          0x2
#define APCI3120_DISABLE_EOS_INT         (~APCI3120_ENABLE_EOS_INT)
#define APCI3120_EOC_INT_MODE         1
#define APCI3120_EOS_INT_MODE         2
#define APCI3120_DMA_INT_MODE         3

#define APCI3120_ENABLE_EOC_INT          0x1
#define APCI3120_DISABLE_EOC_INT         (~APCI3120_ENABLE_EOC_INT)
#define APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER   (APCI3120_DISABLE_EOS_INT & APCI3120_DISABLE_EOC_INT)
#define APCI3120_DISABLE_ALL_INTERRUPT   (APCI3120_DISABLE_TIMER_INT & APCI3120_DISABLE_EOS_INT & APCI3120_DISABLE_EOC_INT)

#define APCI3120_MAX_INPUT_CHANNEL   16
#define APCI3120_ENABLE_TRANSFER_ADD_ON_LOW       0x00
#define APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH      0x1200
#define APCI3120_A2P_FIFO_MANAGEMENT              0x04000400L
#define APCI3120_AMWEN_ENABLE                     0x02
#define APCI3120_A2P_FIFO_WRITE_ENABLE            0x01
#define APCI3120_FIFO_ADVANCE_ON_BYTE_2           0x20000000L

#define APCI3120_ADD_ON_AGCSTS_LOW       0x3C
#define APCI3120_ADD_ON_AGCSTS_HIGH      (APCI3120_ADD_ON_AGCSTS_LOW + 2)
#define APCI3120_AMCC_OP_MCSR            0x3C

#define APCI3120_ADD_ON_MWAR_LOW         0x24
#define APCI3120_ADD_ON_MWAR_HIGH        (APCI3120_ADD_ON_MWAR_LOW + 2)
#define APCI3120_ADD_ON_MWTC_LOW         0x058
#define APCI3120_ADD_ON_MWTC_HIGH        (APCI3120_ADD_ON_MWTC_LOW + 2)

#define APCI3120_ENABLE_EXT_TRIGGER              0x8000U
#define APCI3120_DISABLE_EXT_TRIGGER             (~APCI3120_ENABLE_EXT_TRIGGER)


#define APCI3120_TIMER_0_MODE_2                   0x01
#define APCI3120_TIMER_0_MODE_5                   0x03
#define APCI3120_TIMER_1_MODE_2                   0x4
#define APCI3120_TIMER_2_MODE_2		              0x10
#define APCI3120_TIMER_2_MODE_0                   0x0
#define APCI3120_TIMER_2_MODE_5                   0x30
#define APCI3120_TIMER_CRT0                       0x0D
#define APCI3120_TIMER_CRT1                       0x0C
#define APCI3120_TIMER_VALUE                      0x04
#define APCI3120_SELECT_TIMER_2_LOW_WORD         0x02
#define APCI3120_SELECT_TIMER_2_HIGH_WORD        0x03
#define APCI3120_ENABLE_TIMER0                   0x1000U
#define APCI3120_ENABLE_TIMER1                   0x2000U
#define APCI3120_ENABLE_TIMER2                   0x4000U
#define APCI3120_DISABLE_TIMER0                  (~APCI3120_ENABLE_TIMER0)
#define APCI3120_DISABLE_TIMER1                  (~APCI3120_ENABLE_TIMER1)
#define APCI3120_DISABLE_TIMER2                  (~APCI3120_ENABLE_TIMER2)
#define APCI3120_DISABLE_ALL_TIMER               (APCI3120_DISABLE_TIMER0 & APCI3120_DISABLE_TIMER1 & APCI3120_DISABLE_TIMER2)
#define APCI3120_DISABLE_TIMER_COUNTER 	(~APCI3120_ENABLE_TIMER_COUNTER)
#define APCI3120_ENABLE_TIMER_INT		4
#define APCI3120_DISABLE_TIMER_INT  (~APCI3120_ENABLE_TIMER_INT)
#define APCI3120_ENABLE_WATCHDOG				0x20
#define APCI3120_DISABLE_WATCHDOG        (~APCI3120_ENABLE_WATCHDOG)
#define APCI3120_TIMER_STATUS_REGISTER			0xd
#define APCI3120_ENABLE_TIMER_COUNTER 	0x10
#define APCI3120_DISABLE_TIMER_COUNTER 	(~APCI3120_ENABLE_TIMER_COUNTER)

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

/** ressources of a board */

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

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

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

static __inline__ unsigned long GET_BAR3(struct pci_dev * dev)
{
	return dev->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 APCI3120_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 apci3120_count;

#define __DRIVER_NAME "apci3120"

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

/* /dev function */
/* Asynchronous IO signal implementation - send a SIGIO to all registered processes*/
/* If using signal to inform user mode, uncomment following line: */
// int apci3120_fasync_lookup(int fd, struct file *filp, int mode);
int apci3120_open_lookup (struct inode *inode, struct file *filp);
int apci3120_release_lookup (struct inode *inode,struct file *filp);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
int apci3120_ioctl_lookup (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
#else
long apci3120_ioctl_lookup(struct file *filp, unsigned int cmd, unsigned long arg);
#endif

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

/*/proc functions  */
void apci3120_proc_init(void);
void apci3120_proc_release(void);
void apci3120_proc_create_device(struct pci_dev * dev, unsigned int minor_number);
void apci3120_proc_release_device(struct pci_dev * dev);

/* interrupt related function */
int apci3120_register_interrupt(struct pci_dev * pdev);
int apci3120_deregister_interrupt(struct pci_dev * pdev);

/** busy loops until the condition is true or a timeout occurs
*
* This function is intended to be used in context when interrupts are or could be deactivated.
*
* Its use must be restrained to these cases, as busylooping is the most inefficient way of doing that.
*
* @param[in] pdev The device concerned.
* @param[in] checkf A function that performs the check. it must return 0 if the condition is not fullfilled, 1 otherwise
* @param[in] timeout The timeout in ticks of 5 microseconds
*
*/
static inline int apci3120_busywait_with_timeout(struct pci_dev * pdev, int (*checkf) (struct pci_dev * pdev), unsigned int timeout)
{
	unsigned long count = 0;
	while(1)
	{
		if ( checkf(pdev) )
			return 0; /* condition becomes true */

			/* wait 5 microseconds */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
		usleep_range(5, 10); /* PREEMPT_RT-friendly: yields the CPU */
#else
		udelay(5);
#endif
		count++;
		if (count>=timeout)
			return 1; /* timeout */
	}
}
//------------------------------------------------------------------------------
#include "api.h"
#include "privdata.h"
//------------------------------------------------------------------------------
/* returns base I/O address - depends of the board type, must be computed at board detection */
static inline unsigned long GET_BASE_IO(struct pci_dev *pdev)
{
	return APCI3120_PRIVDATA(pdev)->base_io;
}

/* Functions for the CPCI MSX-Box. */
#ifdef CPCIMSXBOX

	static inline uint8_t XPCI3120_INB (struct pci_dev * pdev, unsigned long address)
	{
		outw (0, GET_BAR0 (pdev));
		return inb (address);
	}
	
	static inline uint16_t XPCI3120_INW (struct pci_dev * pdev, unsigned long address)
	{
		outw (0, GET_BAR0 (pdev));
		return inw (address);
	}

#else
	#define XPCI3120_INB(pdev,address)	inb(address)
	#define XPCI3120_INW(pdev,address)	inw(address)

#endif

#endif //__APCI3120_PRIVATE__
