/** @file sample_etm_01.c
*
* @author Krauth Julien
*
*  Initialise ETM with the selected mode.
*  Read ETM.
*/

/** @par LICENCE
* @verbatim
    Copyright (C) 2011  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
#include <asm/uaccess.h>
#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 <apci1710.h>
#include <apci1710-kapi.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ADDI-DATA GmbH <info@addi-data.com>");
MODULE_DESCRIPTION("APCI-1710");

EXPORT_NO_SYMBOLS;

static const char modulename[] = "sample_etm_01";
static struct pci_dev *pdev;
static uint8_t b_ModuleNbr = 0;  // The module to use (0 to 3).

static int sample_etm_kernel (struct pci_dev *pdev)
{
    int i_ReturnValue = 0;
    int i = 0;
    unsigned long irqstate;
    uint32_t ul_Timing = 0;
    uint8_t     b_ETMtatus = 0;
    uint32_t ul_ETMValue = 0;

    /* Display management */
    uint8_t nostartflag = 0;
    uint8_t stopflag = 0;
    uint8_t startflag = 0;
    uint8_t b_TimingUnit = 0;

    /*
     * APCI1710_40MHZ
     *
     * +-----------+--------------------+--------------------+
     * | Time Unit | Min. Time Interval | Max. Time Interval |
     * +-----------+--------------------+--------------------+
     * | ns (0)    | 50                 | 419430375          |
     * +-----------+--------------------+--------------------+
     * | us (1)    |  1                 |    419430          |
     * +-----------+--------------------+--------------------+
     * | ms (2)    |  1                 |       419          |
     * +-----------+--------------------+--------------------+
     */
    ul_Timing = 50;
    b_TimingUnit = 0;

    /* Lock the function to avoid parallel configurations */
    apci1710_lock(pdev,&irqstate);

    i_ReturnValue = i_APCI1710_InitETM (pdev,
                            b_ModuleNbr,
                            APCI1710_40MHZ,
                            b_TimingUnit,
                            ul_Timing);

    /* Unlock the function so that other applications can call it */
    apci1710_unlock(pdev,irqstate);

    switch (i_ReturnValue)
    {
        case 0:
        {
            printk ("\ni_APCI1710_InitETM OK");

            /* Lock the function to avoid parallel configurations */
            apci1710_lock(pdev,&irqstate);

            i_ReturnValue = i_APCI1710_EnableETM (    pdev,
                                                    b_ModuleNbr,
                                                    0,                     // Selected ETM (0 or 1)
                                                    0,                     //     Selection from edge to measure the time
                                                                        //    0 : Measure the low level time
                                                                        //    1 : Measure the high level time

                                                    0,                    // Selection from trigger level
                                                                        //    0 : The ETM trigger which a low level
                                                                        //    1 : The ETM trigger which a high level

                                                    1,                     // Selection from mode
                                                                        //    0 : Single mode
                                                                        //    1 : Continuous mode.
                                                                        //    Each trigger stop the
                                                                        //    measurement a start a
                                                                        //    new measurement cycle

                                                    0,                    //     First trigger mode
                                                                        //    0 : The first edge time measurement start after the calling from "i_APCI1710_EnableETM"
                                                                        //    1 : The first edge time measurement start after the next trigger signal
                                                    APCI1710_DISABLE);    // Enable or disable the ETM interrupt.

            /* Unlock the function so that other applications can call it */
            apci1710_unlock(pdev,irqstate);

            switch (i_ReturnValue)
            {
                case 0:
                    printk ("\ni_APCI1710_EnableETM OK");
                break;

                default:
                    printk ("\ni_APCI1710_EnableETM error");
                    printk ("\nError = %d.", i_ReturnValue);
                    return 0;
                break;
            }

            printk ("\n\n");

            do
            {
                /* Read the ETM */
                i_ReturnValue = i_APCI1710_ReadETMValue    (pdev,
                        b_ModuleNbr,
                        0,
                        0,
                        &b_ETMtatus,
                        &ul_ETMValue);

                /* Test the return value */
                switch (i_ReturnValue)
                {
                    case 0:
                        switch (b_ETMtatus)
                        {
                            case 0:
                            /* Display only once */
                            if (!nostartflag)
                            {
                                printk ("\nMeasure started not started");
                                nostartflag = 1;
                                stopflag = 0;
                                startflag = 0;
                            }
                            break;

                            case 1:
                            /* Display only once */
                            if (!startflag)
                            {
                                printk ("\nMeasure started");
                                nostartflag = 0;
                                stopflag = 0;
                                startflag = 1;
                            }
                            break;

                            case 2:
                            /* Display only once */
                            if (!stopflag)
                            {
                                printk ("\nMeasure stopped");
                                printk ("\nETM value from module %d = %u", b_ModuleNbr, ul_ETMValue);
                                nostartflag = 0;
                                stopflag = 1;
                                startflag = 0;
                            }
                            break;

                            case 3:
                            printk ("\nOverflow occur");
                            break;
                        }
                    break;

                    default:
                        printk ("\ni_APCI1710_ReadETMValue error");
                        printk ("\nError = %d.", i_ReturnValue);
                        return 0;
                    break;
                }
                udelay(1);
            }
            while (i++ < 100);
        }
        break;

        default:
            printk ("\ni_APCI1710_InitETM error");
            printk ("\nError = %d", i_ReturnValue);
        break;
    }

    return 0;
}


/** Called when module loads. */
static int __init apci1710Sample_init(void)
{
    int board_index = 0;

    printk("%s: looking for board %u\n",modulename,board_index);
    pdev=apci1710_lookup_board_by_index(0);
    if( !pdev )
    {
        printk("%s: board %u not found\n",modulename,board_index);
        return -ENODEV;
    }
    printk("%s: beginning test\n",modulename);
    sample_etm_kernel (pdev);

    return 0;
}
//-------------------------------------------------------------------
/** Called when module is unloaded. */
static void __exit apci1710Sample_exit(void)
{
    int i_ReturnValue = 0;
    unsigned long irqstate;

    /* Lock the function to avoid parallel configurations */
    apci1710_lock(pdev,&irqstate);

    i_ReturnValue = i_APCI1710_DisableETM (pdev, b_ModuleNbr, 0);

    /* Unlock the function so that other applications can call it */
    apci1710_unlock(pdev,irqstate);

    switch (i_ReturnValue)
    {
        case 0:
            printk ("\ni_APCI1710_DisableETM OK");
        break;

        default:
            printk ("\ni_APCI1710_DisableETM error");
            printk ("\nError = %d.", i_ReturnValue);
        break;
    }
}
//------------------------------------------------------------------------------
module_exit(apci1710Sample_exit);
module_init(apci1710Sample_init);
