/** @file timer_watchdog_kapi.c

   Kernel functions for timer watchdog.

   @par CREATION
   @author Krauth Julien
   @date   20.04.09

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

   @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 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 "apci3120-private.h"

EXPORT_SYMBOL(i_APCI3120_InitTimerWatchdog);
EXPORT_SYMBOL(i_APCI3120_StartTimerWatchdog);
EXPORT_SYMBOL(i_APCI3120_StopTimerWatchdog);
EXPORT_SYMBOL(i_APCI3120_ReadTimer);
EXPORT_SYMBOL(i_APCI3120_ReadWatchdogStatus);


/**@def EXPORT_NO_SYMBOLS
 * Function in this file are not exported.
 */
EXPORT_NO_SYMBOLS;

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

/** Initialize the timer or watchdog functionality.
 *
 * @param [in] 	pdev			: The device to initialize.
 * @param [in]	b_TimerMode		: APCI3120_TIMER or APCI3120_WATCHDOG.
 * @param [in]	l_DelayValue	: Timer / Watchdog delay from 70 us to 4587400 us.
 * @param [in]	b_InterruptFlag	: This parameter has to be 0 or PCI3120_DISABLE.<br>
 * 								  The interrupt is not implemeted!
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Timer mode error.
 * @retval 3: The user interrupt routine is not installed.
 * @retval 4: The interrupt parameter is wrong.
 * @retval 5: The time interval selected for timer is wrong.
 */
int i_APCI3120_InitTimerWatchdog (struct pci_dev *pdev,
								  uint8_t b_TimerMode,
								  uint32_t l_DelayTime,
								  uint8_t b_InterruptFlag)
{
	uint32_t ul_TimerValue;
	uint16_t  ui_TimerValueLow  = 0;
	uint16_t  ui_TimerValueHigh = 0;

		/* Interrupt not implemented */
		b_InterruptFlag = APCI3120_DISABLE;

		if (!pdev) return 1;

		/* Tests the mode */
		if (b_TimerMode != APCI3120_TIMER && b_TimerMode != APCI3120_WATCHDOG)
		{
			return 2;
		}

		/* Tests if interrupt used */
		if (b_InterruptFlag != APCI3120_ENABLE &&  b_InterruptFlag != APCI3120_DISABLE)
		{
			return 4;
		}

		/* Tests the interrupt */
		if ( ( (APCI3120_PRIVDATA(pdev)->interruptInitialized == APCI3120_ENABLE) && (b_InterruptFlag == APCI3120_ENABLE)) || b_InterruptFlag == APCI3120_DISABLE)
		{
			/* Tests and calculate the time interval */
			if (b_TimerMode == APCI3120_TIMER)
			{
				if (l_DelayTime < 100 || l_DelayTime > 838860800L)
				return 5;
			}
			else
			{
				if (l_DelayTime < 50 || l_DelayTime > 838860800L)
				return 5;
			}

			/* Save Delay Value */
			APCI3120_PRIVDATA(pdev)->t2Delay = l_DelayTime;
			ul_TimerValue    = (uint32_t) (l_DelayTime / 50);

			if (ul_TimerValue >= 2)
				ul_TimerValue = ul_TimerValue - 2;

			ui_TimerValueLow  = (uint16_t) (ul_TimerValue & 0xFFFF);
			ui_TimerValueHigh = (uint8_t) (ul_TimerValue >> 16);

			/* Resets gate 2 */
			APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_TIMER2;
			outw(APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BASE_IO(pdev) + APCI3120_WR_ADDRESS);

			/* DISABLE TIMER INTERRUPT */
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_TIMER_INT & 0xEF;
			outb(APCI3120_PRIVDATA(pdev)->modeSelect, GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT);

			switch (b_TimerMode)
			{
				case APCI3120_TIMER:
  				/* initializes timer 2 in mode 2 */
  				APCI3120_PRIVDATA(pdev)->crt1  = (APCI3120_PRIVDATA(pdev)->crt1 & 0x0F) | APCI3120_TIMER_2_MODE_2;
  				break;

				case APCI3120_WATCHDOG:
				/* initialize timer 2 in mode 5 */
				APCI3120_PRIVDATA(pdev)->crt1  = (APCI3120_PRIVDATA(pdev)->crt1 & 0x0F) | APCI3120_TIMER_2_MODE_5;
				break;

				default:
				return 2;
            }

			/* Writes timer mode */
			outb(APCI3120_PRIVDATA(pdev)->crt1, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT1 );
			APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_2_LOW_WORD;

			/* Writes the low word */
			outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT0);
			outw(ui_TimerValueLow, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);
			APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_2_HIGH_WORD;

			/* Writes the high word */
			outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT0);
			outw(ui_TimerValueHigh, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);

			/* Saves informations */
			APCI3120_PRIVDATA(pdev)->t2Init = APCI3120_ENABLE;
			APCI3120_PRIVDATA(pdev)->t2Started = APCI3120_DISABLE;

			/* Timer interrupt */
			APCI3120_PRIVDATA(pdev)->t2Interrupt = (uint8_t) b_InterruptFlag;

			/* Timer mode */
			APCI3120_PRIVDATA(pdev)->timerMode = b_TimerMode;
    }
	else
	{
		return 3;
	}

	return 0;
}

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

/** Start the timer or watchdog functionality.
 *
 * @param [in] 	pdev			: The device to initialize.
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Timer or watchdog not initialized.
 */
int i_APCI3120_StartTimerWatchdog (struct pci_dev *pdev)
{
	uint8_t b_DummyRead = 0;

		if (!pdev) return 1;

		/* Tests if timer initialized */
		if (APCI3120_PRIVDATA(pdev)->t2Init != APCI3120_ENABLE)
		{
			return 2;
		}

		/* RESET FC_TIMER BIT */
		b_DummyRead = XPCI3120_INB(pdev, GET_BASE_IO(pdev) + APCI3120_TIMER_STATUS_REGISTER);

		/* Tests if watchdog initialized */
		if (APCI3120_PRIVDATA(pdev)->timerMode == APCI3120_WATCHDOG)
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = (APCI3120_PRIVDATA(pdev)->modeSelect &0x0B) | APCI3120_ENABLE_WATCHDOG;
		}
		else
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = (APCI3120_PRIVDATA(pdev)->modeSelect & 0x0B);
		}

		/* ENABLE WATCHDOG or ENABLE TIMER COUNTER */
		outb(APCI3120_PRIVDATA(pdev)->modeSelect, GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT);

		if (APCI3120_PRIVDATA(pdev)->t2Interrupt == APCI3120_ENABLE)
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect | APCI3120_ENABLE_TIMER_INT;
		}
		else
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_TIMER_INT;
		}

		/* ENABLE or DISABLE TIMER INTERRUPT */
		outb(APCI3120_PRIVDATA(pdev)->modeSelect, GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT);

		/* Tests if timer */
		if (APCI3120_PRIVDATA(pdev)->timerMode == APCI3120_TIMER)
		{
			/* Sets gate 2 */
			APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr | APCI3120_ENABLE_TIMER2;
			outw(APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BASE_IO(pdev) + APCI3120_WR_ADDRESS);
		}

		APCI3120_PRIVDATA(pdev)->t2Started = APCI3120_ENABLE;

		return 0;
}

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

/** Stop the timer or watchdog functionality.
 *
 * @param [in] 	pdev			: The device to initialize.
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Timer or watchdog not initialized.
 * @retval 3: Timer or watchdog has not been started.
 */
int i_APCI3120_StopTimerWatchdog (struct pci_dev *pdev)
{
	uint8_t b_DummyRead = 0;

		if (!pdev) return 1;

		/* Tests if timer initialized */
		if (APCI3120_PRIVDATA(pdev)->t2Init != APCI3120_ENABLE)
		{
			return 2;
		}

		/* Tests if timer 2 is started     */
		if (APCI3120_PRIVDATA(pdev)->t2Started != APCI3120_ENABLE)
		{
			return 3;
		}

		/* Tests if watchdog is initialized */
		if (APCI3120_PRIVDATA(pdev)->timerMode == APCI3120_WATCHDOG)
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_WATCHDOG;
		}
		else
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_TIMER_COUNTER;
		}

		if (APCI3120_PRIVDATA(pdev)->t2Interrupt == APCI3120_ENABLE)
		{
			APCI3120_PRIVDATA(pdev)->modeSelect = APCI3120_PRIVDATA(pdev)->modeSelect & APCI3120_DISABLE_TIMER_INT;
		}

		outb(APCI3120_PRIVDATA(pdev)->modeSelect, GET_BASE_IO(pdev) + APCI3120_WRITE_MODE_SELECT);

		/* Resets gate 2 */
		APCI3120_PRIVDATA(pdev)->nWrAddr = APCI3120_PRIVDATA(pdev)->nWrAddr & APCI3120_DISABLE_TIMER2;
		outw(APCI3120_PRIVDATA(pdev)->nWrAddr, GET_BASE_IO(pdev) + APCI3120_WR_ADDRESS);

		/* RESET FC_TIMER BIT */
		b_DummyRead = XPCI3120_INB(pdev, GET_BASE_IO(pdev) + APCI3120_TIMER_STATUS_REGISTER);
		APCI3120_PRIVDATA(pdev)->t2Started = APCI3120_DISABLE;

		return 0;
}

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

/** Read the timer value.
 *
 * @param [in] 	pdev			: The device to initialize.
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Timer not initialized.
 * @retval 3: Watchdog was initialized, not a timer.
 */
int i_APCI3120_ReadTimer (struct pci_dev *pdev,
                          uint32_t *pl_ReadValue)
{
	uint16_t ui_TimerValueLow;
	uint16_t ui_TimerValueHigh;

		if (!pdev) return 1;

		/* Tests if timer initialized */
		if (APCI3120_PRIVDATA(pdev)->t2Init != APCI3120_ENABLE)
		{
			return 2;
		}

	    APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_2_LOW_WORD;

	    /* Reads the low word */
	    outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT0);
	    ui_TimerValueLow= XPCI3120_INW(pdev, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);
	    APCI3120_PRIVDATA(pdev)->crt0.timer_select = APCI3120_SELECT_TIMER_2_HIGH_WORD;

	    /* Reads the high word */
	    outb(APCI3120_PRIVDATA(pdev)->crt0.raw, GET_BASE_IO(pdev) + APCI3120_TIMER_CRT0);
	    ui_TimerValueHigh = XPCI3120_INW(pdev, GET_BASE_IO(pdev) + APCI3120_TIMER_VALUE);
	    *pl_ReadValue = ui_TimerValueLow | ((uint32_t) ui_TimerValueHigh << 16);

		return 0;
}

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

/** Read the watchdog status.
 *
 * @param [in] 	pdev				: The device to initialize.
 *
 * @param [out] pb_watchdogStatus	: 0: Watchdog has not run down<br>
 *                                    1: Watchdog has run down
 *
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Watchdog not initialized.
 * @retval 3: Timer was initialized, not a Watchdog.
 */
int i_APCI3120_ReadWatchdogStatus (struct pci_dev *pdev, uint8_t *pb_watchdogStatus)
{
	uint16_t ui_StatusValue = 0;
	uint8_t b_DummyRead = 0;

		if (!pdev) return 1;

		/* Tests if timer initialized */
		if (APCI3120_PRIVDATA(pdev)->t2Init != APCI3120_ENABLE)
		{
			return 2;
		}

		/* Tests if watchdog */
		if (APCI3120_PRIVDATA(pdev)->timerMode != APCI3120_WATCHDOG)
		{
			return 3;
		}

		ui_StatusValue = XPCI3120_INW(pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS);
		*pb_watchdogStatus = ((ui_StatusValue >> 12) & 1);

		if (*pb_watchdogStatus)
		{
			/* RESET FC_TIMER BIT */
			b_DummyRead = XPCI3120_INB(pdev, GET_BASE_IO(pdev) + APCI3120_TIMER_STATUS_REGISTER);
		}

		return 0;
}
